UNPKG

1.95 MBJavaScriptView Raw
1/**
2 * @license Angular v11.2.4
3 * Copyright Google LLC All Rights Reserved.
4 * License: MIT
5 */
6
7let $deferred;
8function define(modules, callback) {
9 $deferred = {modules, callback};
10}
11module.exports = function(provided) {
12 const ts = provided['typescript'];
13 if (!ts) {
14 throw new Error('Caller does not provide typescript module');
15 }
16 const results = {};
17 const resolvedModules = $deferred.modules.map(m => {
18 if (m === 'exports') {
19 return results;
20 }
21 if (m === 'typescript' || m === 'typescript/lib/tsserverlibrary') {
22 return ts;
23 }
24 return require(m);
25 });
26 $deferred.callback(...resolvedModules);
27 return results;
28};
29
30define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', 'constants', 'stream', 'util', 'assert', 'path'], function (exports, ts, os, ts$1, fs$2, constants, stream, util, assert, path) { 'use strict';
31
32 var os__default = 'default' in os ? os['default'] : os;
33 var fs$2__default = 'default' in fs$2 ? fs$2['default'] : fs$2;
34 constants = constants && Object.prototype.hasOwnProperty.call(constants, 'default') ? constants['default'] : constants;
35 stream = stream && Object.prototype.hasOwnProperty.call(stream, 'default') ? stream['default'] : stream;
36 util = util && Object.prototype.hasOwnProperty.call(util, 'default') ? util['default'] : util;
37 assert = assert && Object.prototype.hasOwnProperty.call(assert, 'default') ? assert['default'] : assert;
38 var path__default = 'default' in path ? path['default'] : path;
39
40 /**
41 * The default `FileSystem` that will always fail.
42 *
43 * This is a way of ensuring that the developer consciously chooses and
44 * configures the `FileSystem` before using it; particularly important when
45 * considering static functions like `absoluteFrom()` which rely on
46 * the `FileSystem` under the hood.
47 */
48 class InvalidFileSystem {
49 exists(path) {
50 throw makeError();
51 }
52 readFile(path) {
53 throw makeError();
54 }
55 readFileBuffer(path) {
56 throw makeError();
57 }
58 writeFile(path, data, exclusive) {
59 throw makeError();
60 }
61 removeFile(path) {
62 throw makeError();
63 }
64 symlink(target, path) {
65 throw makeError();
66 }
67 readdir(path) {
68 throw makeError();
69 }
70 lstat(path) {
71 throw makeError();
72 }
73 stat(path) {
74 throw makeError();
75 }
76 pwd() {
77 throw makeError();
78 }
79 chdir(path) {
80 throw makeError();
81 }
82 extname(path) {
83 throw makeError();
84 }
85 copyFile(from, to) {
86 throw makeError();
87 }
88 moveFile(from, to) {
89 throw makeError();
90 }
91 ensureDir(path) {
92 throw makeError();
93 }
94 removeDeep(path) {
95 throw makeError();
96 }
97 isCaseSensitive() {
98 throw makeError();
99 }
100 resolve(...paths) {
101 throw makeError();
102 }
103 dirname(file) {
104 throw makeError();
105 }
106 join(basePath, ...paths) {
107 throw makeError();
108 }
109 isRoot(path) {
110 throw makeError();
111 }
112 isRooted(path) {
113 throw makeError();
114 }
115 relative(from, to) {
116 throw makeError();
117 }
118 basename(filePath, extension) {
119 throw makeError();
120 }
121 realpath(filePath) {
122 throw makeError();
123 }
124 getDefaultLibLocation() {
125 throw makeError();
126 }
127 normalize(path) {
128 throw makeError();
129 }
130 }
131 function makeError() {
132 return new Error('FileSystem has not been configured. Please call `setFileSystem()` before calling this method.');
133 }
134
135 const TS_DTS_JS_EXTENSION = /(?:\.d)?\.ts$|\.js$/;
136 /**
137 * Remove a .ts, .d.ts, or .js extension from a file name.
138 */
139 function stripExtension(path) {
140 return path.replace(TS_DTS_JS_EXTENSION, '');
141 }
142 function getSourceFileOrError(program, fileName) {
143 const sf = program.getSourceFile(fileName);
144 if (sf === undefined) {
145 throw new Error(`Program does not contain "${fileName}" - available files are ${program.getSourceFiles().map(sf => sf.fileName).join(', ')}`);
146 }
147 return sf;
148 }
149
150 let fs = new InvalidFileSystem();
151 function getFileSystem() {
152 return fs;
153 }
154 function setFileSystem(fileSystem) {
155 fs = fileSystem;
156 }
157 /**
158 * Convert the path `path` to an `AbsoluteFsPath`, throwing an error if it's not an absolute path.
159 */
160 function absoluteFrom(path) {
161 if (!fs.isRooted(path)) {
162 throw new Error(`Internal Error: absoluteFrom(${path}): path is not absolute`);
163 }
164 return fs.resolve(path);
165 }
166 /**
167 * Extract an `AbsoluteFsPath` from a `ts.SourceFile`.
168 */
169 function absoluteFromSourceFile(sf) {
170 return fs.resolve(sf.fileName);
171 }
172 /**
173 * Static access to `dirname`.
174 */
175 function dirname(file) {
176 return fs.dirname(file);
177 }
178 /**
179 * Static access to `join`.
180 */
181 function join(basePath, ...paths) {
182 return fs.join(basePath, ...paths);
183 }
184 /**
185 * Static access to `resolve`s.
186 */
187 function resolve(basePath, ...paths) {
188 return fs.resolve(basePath, ...paths);
189 }
190 /**
191 * Static access to `isRooted`.
192 */
193 function isRooted(path) {
194 return fs.isRooted(path);
195 }
196 /**
197 * Static access to `relative`.
198 */
199 function relative(from, to) {
200 return fs.relative(from, to);
201 }
202 /**
203 * Returns true if the given path is locally relative.
204 *
205 * This is used to work out if the given path is relative (i.e. not absolute) but also is not
206 * escaping the current directory.
207 */
208 function isLocalRelativePath(relativePath) {
209 return !isRooted(relativePath) && !relativePath.startsWith('..');
210 }
211 /**
212 * Converts a path to a form suitable for use as a relative module import specifier.
213 *
214 * In other words it adds the `./` to the path if it is locally relative.
215 */
216 function toRelativeImport(relativePath) {
217 return isLocalRelativePath(relativePath) ? `./${relativePath}` : relativePath;
218 }
219
220 const LogicalProjectPath = {
221 /**
222 * Get the relative path between two `LogicalProjectPath`s.
223 *
224 * This will return a `PathSegment` which would be a valid module specifier to use in `from` when
225 * importing from `to`.
226 */
227 relativePathBetween: function (from, to) {
228 const relativePath = relative(dirname(resolve(from)), resolve(to));
229 return toRelativeImport(relativePath);
230 },
231 };
232 /**
233 * A utility class which can translate absolute paths to source files into logical paths in
234 * TypeScript's logical file system, based on the root directories of the project.
235 */
236 class LogicalFileSystem {
237 constructor(rootDirs, compilerHost) {
238 this.compilerHost = compilerHost;
239 /**
240 * A cache of file paths to project paths, because computation of these paths is slightly
241 * expensive.
242 */
243 this.cache = new Map();
244 // Make a copy and sort it by length in reverse order (longest first). This speeds up lookups,
245 // since there's no need to keep going through the array once a match is found.
246 this.rootDirs = rootDirs.concat([]).sort((a, b) => b.length - a.length);
247 this.canonicalRootDirs =
248 this.rootDirs.map(dir => this.compilerHost.getCanonicalFileName(dir));
249 }
250 /**
251 * Get the logical path in the project of a `ts.SourceFile`.
252 *
253 * This method is provided as a convenient alternative to calling
254 * `logicalPathOfFile(absoluteFromSourceFile(sf))`.
255 */
256 logicalPathOfSf(sf) {
257 return this.logicalPathOfFile(absoluteFrom(sf.fileName));
258 }
259 /**
260 * Get the logical path in the project of a source file.
261 *
262 * @returns A `LogicalProjectPath` to the source file, or `null` if the source file is not in any
263 * of the TS project's root directories.
264 */
265 logicalPathOfFile(physicalFile) {
266 const canonicalFilePath = this.compilerHost.getCanonicalFileName(physicalFile);
267 if (!this.cache.has(canonicalFilePath)) {
268 let logicalFile = null;
269 for (let i = 0; i < this.rootDirs.length; i++) {
270 const rootDir = this.rootDirs[i];
271 const canonicalRootDir = this.canonicalRootDirs[i];
272 if (isWithinBasePath(canonicalRootDir, canonicalFilePath)) {
273 // Note that we match against canonical paths but then create the logical path from
274 // original paths.
275 logicalFile = this.createLogicalProjectPath(physicalFile, rootDir);
276 // The logical project does not include any special "node_modules" nested directories.
277 if (logicalFile.indexOf('/node_modules/') !== -1) {
278 logicalFile = null;
279 }
280 else {
281 break;
282 }
283 }
284 }
285 this.cache.set(canonicalFilePath, logicalFile);
286 }
287 return this.cache.get(canonicalFilePath);
288 }
289 createLogicalProjectPath(file, rootDir) {
290 const logicalPath = stripExtension(file.substr(rootDir.length));
291 return (logicalPath.startsWith('/') ? logicalPath : '/' + logicalPath);
292 }
293 }
294 /**
295 * Is the `path` a descendant of the `base`?
296 * E.g. `foo/bar/zee` is within `foo/bar` but not within `foo/car`.
297 */
298 function isWithinBasePath(base, path) {
299 return isLocalRelativePath(relative(base, path));
300 }
301
302 // simple mutable assign
303 function assign () {
304 const args = [].slice.call(arguments).filter(i => i);
305 const dest = args.shift();
306 args.forEach(src => {
307 Object.keys(src).forEach(key => {
308 dest[key] = src[key];
309 });
310 });
311
312 return dest
313 }
314
315 var assign_1 = assign;
316
317 function createCommonjsModule(fn, module) {
318 return module = { exports: {} }, fn(module, module.exports), module.exports;
319 }
320
321 var fromCallback = function (fn) {
322 return Object.defineProperty(function () {
323 if (typeof arguments[arguments.length - 1] === 'function') fn.apply(this, arguments);
324 else {
325 return new Promise((resolve, reject) => {
326 arguments[arguments.length] = (err, res) => {
327 if (err) return reject(err)
328 resolve(res);
329 };
330 arguments.length++;
331 fn.apply(this, arguments);
332 })
333 }
334 }, 'name', { value: fn.name })
335 };
336
337 var fromPromise = function (fn) {
338 return Object.defineProperty(function () {
339 const cb = arguments[arguments.length - 1];
340 if (typeof cb !== 'function') return fn.apply(this, arguments)
341 else fn.apply(this, arguments).then(r => cb(null, r), cb);
342 }, 'name', { value: fn.name })
343 };
344
345 var universalify = {
346 fromCallback: fromCallback,
347 fromPromise: fromPromise
348 };
349
350 var origCwd = process.cwd;
351 var cwd = null;
352
353 var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform;
354
355 process.cwd = function() {
356 if (!cwd)
357 cwd = origCwd.call(process);
358 return cwd
359 };
360 try {
361 process.cwd();
362 } catch (er) {}
363
364 var chdir = process.chdir;
365 process.chdir = function(d) {
366 cwd = null;
367 chdir.call(process, d);
368 };
369
370 var polyfills = patch;
371
372 function patch (fs) {
373 // (re-)implement some things that are known busted or missing.
374
375 // lchmod, broken prior to 0.6.2
376 // back-port the fix here.
377 if (constants.hasOwnProperty('O_SYMLINK') &&
378 process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
379 patchLchmod(fs);
380 }
381
382 // lutimes implementation, or no-op
383 if (!fs.lutimes) {
384 patchLutimes(fs);
385 }
386
387 // https://github.com/isaacs/node-graceful-fs/issues/4
388 // Chown should not fail on einval or eperm if non-root.
389 // It should not fail on enosys ever, as this just indicates
390 // that a fs doesn't support the intended operation.
391
392 fs.chown = chownFix(fs.chown);
393 fs.fchown = chownFix(fs.fchown);
394 fs.lchown = chownFix(fs.lchown);
395
396 fs.chmod = chmodFix(fs.chmod);
397 fs.fchmod = chmodFix(fs.fchmod);
398 fs.lchmod = chmodFix(fs.lchmod);
399
400 fs.chownSync = chownFixSync(fs.chownSync);
401 fs.fchownSync = chownFixSync(fs.fchownSync);
402 fs.lchownSync = chownFixSync(fs.lchownSync);
403
404 fs.chmodSync = chmodFixSync(fs.chmodSync);
405 fs.fchmodSync = chmodFixSync(fs.fchmodSync);
406 fs.lchmodSync = chmodFixSync(fs.lchmodSync);
407
408 fs.stat = statFix(fs.stat);
409 fs.fstat = statFix(fs.fstat);
410 fs.lstat = statFix(fs.lstat);
411
412 fs.statSync = statFixSync(fs.statSync);
413 fs.fstatSync = statFixSync(fs.fstatSync);
414 fs.lstatSync = statFixSync(fs.lstatSync);
415
416 // if lchmod/lchown do not exist, then make them no-ops
417 if (!fs.lchmod) {
418 fs.lchmod = function (path, mode, cb) {
419 if (cb) process.nextTick(cb);
420 };
421 fs.lchmodSync = function () {};
422 }
423 if (!fs.lchown) {
424 fs.lchown = function (path, uid, gid, cb) {
425 if (cb) process.nextTick(cb);
426 };
427 fs.lchownSync = function () {};
428 }
429
430 // on Windows, A/V software can lock the directory, causing this
431 // to fail with an EACCES or EPERM if the directory contains newly
432 // created files. Try again on failure, for up to 60 seconds.
433
434 // Set the timeout this long because some Windows Anti-Virus, such as Parity
435 // bit9, may lock files for up to a minute, causing npm package install
436 // failures. Also, take care to yield the scheduler. Windows scheduling gives
437 // CPU to a busy looping process, which can cause the program causing the lock
438 // contention to be starved of CPU by node, so the contention doesn't resolve.
439 if (platform === "win32") {
440 fs.rename = (function (fs$rename) { return function (from, to, cb) {
441 var start = Date.now();
442 var backoff = 0;
443 fs$rename(from, to, function CB (er) {
444 if (er
445 && (er.code === "EACCES" || er.code === "EPERM")
446 && Date.now() - start < 60000) {
447 setTimeout(function() {
448 fs.stat(to, function (stater, st) {
449 if (stater && stater.code === "ENOENT")
450 fs$rename(from, to, CB);
451 else
452 cb(er);
453 });
454 }, backoff);
455 if (backoff < 100)
456 backoff += 10;
457 return;
458 }
459 if (cb) cb(er);
460 });
461 }})(fs.rename);
462 }
463
464 // if read() returns EAGAIN, then just try it again.
465 fs.read = (function (fs$read) {
466 function read (fd, buffer, offset, length, position, callback_) {
467 var callback;
468 if (callback_ && typeof callback_ === 'function') {
469 var eagCounter = 0;
470 callback = function (er, _, __) {
471 if (er && er.code === 'EAGAIN' && eagCounter < 10) {
472 eagCounter ++;
473 return fs$read.call(fs, fd, buffer, offset, length, position, callback)
474 }
475 callback_.apply(this, arguments);
476 };
477 }
478 return fs$read.call(fs, fd, buffer, offset, length, position, callback)
479 }
480
481 // This ensures `util.promisify` works as it does for native `fs.read`.
482 read.__proto__ = fs$read;
483 return read
484 })(fs.read);
485
486 fs.readSync = (function (fs$readSync) { return function (fd, buffer, offset, length, position) {
487 var eagCounter = 0;
488 while (true) {
489 try {
490 return fs$readSync.call(fs, fd, buffer, offset, length, position)
491 } catch (er) {
492 if (er.code === 'EAGAIN' && eagCounter < 10) {
493 eagCounter ++;
494 continue
495 }
496 throw er
497 }
498 }
499 }})(fs.readSync);
500
501 function patchLchmod (fs) {
502 fs.lchmod = function (path, mode, callback) {
503 fs.open( path
504 , constants.O_WRONLY | constants.O_SYMLINK
505 , mode
506 , function (err, fd) {
507 if (err) {
508 if (callback) callback(err);
509 return
510 }
511 // prefer to return the chmod error, if one occurs,
512 // but still try to close, and report closing errors if they occur.
513 fs.fchmod(fd, mode, function (err) {
514 fs.close(fd, function(err2) {
515 if (callback) callback(err || err2);
516 });
517 });
518 });
519 };
520
521 fs.lchmodSync = function (path, mode) {
522 var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode);
523
524 // prefer to return the chmod error, if one occurs,
525 // but still try to close, and report closing errors if they occur.
526 var threw = true;
527 var ret;
528 try {
529 ret = fs.fchmodSync(fd, mode);
530 threw = false;
531 } finally {
532 if (threw) {
533 try {
534 fs.closeSync(fd);
535 } catch (er) {}
536 } else {
537 fs.closeSync(fd);
538 }
539 }
540 return ret
541 };
542 }
543
544 function patchLutimes (fs) {
545 if (constants.hasOwnProperty("O_SYMLINK")) {
546 fs.lutimes = function (path, at, mt, cb) {
547 fs.open(path, constants.O_SYMLINK, function (er, fd) {
548 if (er) {
549 if (cb) cb(er);
550 return
551 }
552 fs.futimes(fd, at, mt, function (er) {
553 fs.close(fd, function (er2) {
554 if (cb) cb(er || er2);
555 });
556 });
557 });
558 };
559
560 fs.lutimesSync = function (path, at, mt) {
561 var fd = fs.openSync(path, constants.O_SYMLINK);
562 var ret;
563 var threw = true;
564 try {
565 ret = fs.futimesSync(fd, at, mt);
566 threw = false;
567 } finally {
568 if (threw) {
569 try {
570 fs.closeSync(fd);
571 } catch (er) {}
572 } else {
573 fs.closeSync(fd);
574 }
575 }
576 return ret
577 };
578
579 } else {
580 fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb); };
581 fs.lutimesSync = function () {};
582 }
583 }
584
585 function chmodFix (orig) {
586 if (!orig) return orig
587 return function (target, mode, cb) {
588 return orig.call(fs, target, mode, function (er) {
589 if (chownErOk(er)) er = null;
590 if (cb) cb.apply(this, arguments);
591 })
592 }
593 }
594
595 function chmodFixSync (orig) {
596 if (!orig) return orig
597 return function (target, mode) {
598 try {
599 return orig.call(fs, target, mode)
600 } catch (er) {
601 if (!chownErOk(er)) throw er
602 }
603 }
604 }
605
606
607 function chownFix (orig) {
608 if (!orig) return orig
609 return function (target, uid, gid, cb) {
610 return orig.call(fs, target, uid, gid, function (er) {
611 if (chownErOk(er)) er = null;
612 if (cb) cb.apply(this, arguments);
613 })
614 }
615 }
616
617 function chownFixSync (orig) {
618 if (!orig) return orig
619 return function (target, uid, gid) {
620 try {
621 return orig.call(fs, target, uid, gid)
622 } catch (er) {
623 if (!chownErOk(er)) throw er
624 }
625 }
626 }
627
628 function statFix (orig) {
629 if (!orig) return orig
630 // Older versions of Node erroneously returned signed integers for
631 // uid + gid.
632 return function (target, options, cb) {
633 if (typeof options === 'function') {
634 cb = options;
635 options = null;
636 }
637 function callback (er, stats) {
638 if (stats) {
639 if (stats.uid < 0) stats.uid += 0x100000000;
640 if (stats.gid < 0) stats.gid += 0x100000000;
641 }
642 if (cb) cb.apply(this, arguments);
643 }
644 return options ? orig.call(fs, target, options, callback)
645 : orig.call(fs, target, callback)
646 }
647 }
648
649 function statFixSync (orig) {
650 if (!orig) return orig
651 // Older versions of Node erroneously returned signed integers for
652 // uid + gid.
653 return function (target, options) {
654 var stats = options ? orig.call(fs, target, options)
655 : orig.call(fs, target);
656 if (stats.uid < 0) stats.uid += 0x100000000;
657 if (stats.gid < 0) stats.gid += 0x100000000;
658 return stats;
659 }
660 }
661
662 // ENOSYS means that the fs doesn't support the op. Just ignore
663 // that, because it doesn't matter.
664 //
665 // if there's no getuid, or if getuid() is something other
666 // than 0, and the error is EINVAL or EPERM, then just ignore
667 // it.
668 //
669 // This specific case is a silent failure in cp, install, tar,
670 // and most other unix tools that manage permissions.
671 //
672 // When running as root, or if other types of errors are
673 // encountered, then it's strict.
674 function chownErOk (er) {
675 if (!er)
676 return true
677
678 if (er.code === "ENOSYS")
679 return true
680
681 var nonroot = !process.getuid || process.getuid() !== 0;
682 if (nonroot) {
683 if (er.code === "EINVAL" || er.code === "EPERM")
684 return true
685 }
686
687 return false
688 }
689 }
690
691 var Stream = stream.Stream;
692
693 var legacyStreams = legacy;
694
695 function legacy (fs) {
696 return {
697 ReadStream: ReadStream,
698 WriteStream: WriteStream
699 }
700
701 function ReadStream (path, options) {
702 if (!(this instanceof ReadStream)) return new ReadStream(path, options);
703
704 Stream.call(this);
705
706 var self = this;
707
708 this.path = path;
709 this.fd = null;
710 this.readable = true;
711 this.paused = false;
712
713 this.flags = 'r';
714 this.mode = 438; /*=0666*/
715 this.bufferSize = 64 * 1024;
716
717 options = options || {};
718
719 // Mixin options into this
720 var keys = Object.keys(options);
721 for (var index = 0, length = keys.length; index < length; index++) {
722 var key = keys[index];
723 this[key] = options[key];
724 }
725
726 if (this.encoding) this.setEncoding(this.encoding);
727
728 if (this.start !== undefined) {
729 if ('number' !== typeof this.start) {
730 throw TypeError('start must be a Number');
731 }
732 if (this.end === undefined) {
733 this.end = Infinity;
734 } else if ('number' !== typeof this.end) {
735 throw TypeError('end must be a Number');
736 }
737
738 if (this.start > this.end) {
739 throw new Error('start must be <= end');
740 }
741
742 this.pos = this.start;
743 }
744
745 if (this.fd !== null) {
746 process.nextTick(function() {
747 self._read();
748 });
749 return;
750 }
751
752 fs.open(this.path, this.flags, this.mode, function (err, fd) {
753 if (err) {
754 self.emit('error', err);
755 self.readable = false;
756 return;
757 }
758
759 self.fd = fd;
760 self.emit('open', fd);
761 self._read();
762 });
763 }
764
765 function WriteStream (path, options) {
766 if (!(this instanceof WriteStream)) return new WriteStream(path, options);
767
768 Stream.call(this);
769
770 this.path = path;
771 this.fd = null;
772 this.writable = true;
773
774 this.flags = 'w';
775 this.encoding = 'binary';
776 this.mode = 438; /*=0666*/
777 this.bytesWritten = 0;
778
779 options = options || {};
780
781 // Mixin options into this
782 var keys = Object.keys(options);
783 for (var index = 0, length = keys.length; index < length; index++) {
784 var key = keys[index];
785 this[key] = options[key];
786 }
787
788 if (this.start !== undefined) {
789 if ('number' !== typeof this.start) {
790 throw TypeError('start must be a Number');
791 }
792 if (this.start < 0) {
793 throw new Error('start must be >= zero');
794 }
795
796 this.pos = this.start;
797 }
798
799 this.busy = false;
800 this._queue = [];
801
802 if (this.fd === null) {
803 this._open = fs.open;
804 this._queue.push([this._open, this.path, this.flags, this.mode, undefined]);
805 this.flush();
806 }
807 }
808 }
809
810 var clone_1 = clone;
811
812 function clone (obj) {
813 if (obj === null || typeof obj !== 'object')
814 return obj
815
816 if (obj instanceof Object)
817 var copy = { __proto__: obj.__proto__ };
818 else
819 var copy = Object.create(null);
820
821 Object.getOwnPropertyNames(obj).forEach(function (key) {
822 Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key));
823 });
824
825 return copy
826 }
827
828 var gracefulFs = createCommonjsModule(function (module) {
829 /* istanbul ignore next - node 0.x polyfill */
830 var gracefulQueue;
831 var previousSymbol;
832
833 /* istanbul ignore else - node 0.x polyfill */
834 if (typeof Symbol === 'function' && typeof Symbol.for === 'function') {
835 gracefulQueue = Symbol.for('graceful-fs.queue');
836 // This is used in testing by future versions
837 previousSymbol = Symbol.for('graceful-fs.previous');
838 } else {
839 gracefulQueue = '___graceful-fs.queue';
840 previousSymbol = '___graceful-fs.previous';
841 }
842
843 function noop () {}
844
845 function publishQueue(context, queue) {
846 Object.defineProperty(context, gracefulQueue, {
847 get: function() {
848 return queue
849 }
850 });
851 }
852
853 var debug = noop;
854 if (util.debuglog)
855 debug = util.debuglog('gfs4');
856 else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ''))
857 debug = function() {
858 var m = util.format.apply(util, arguments);
859 m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ');
860 console.error(m);
861 };
862
863 // Once time initialization
864 if (!fs$2__default[gracefulQueue]) {
865 // This queue can be shared by multiple loaded instances
866 var queue = global[gracefulQueue] || [];
867 publishQueue(fs$2__default, queue);
868
869 // Patch fs.close/closeSync to shared queue version, because we need
870 // to retry() whenever a close happens *anywhere* in the program.
871 // This is essential when multiple graceful-fs instances are
872 // in play at the same time.
873 fs$2__default.close = (function (fs$close) {
874 function close (fd, cb) {
875 return fs$close.call(fs$2__default, fd, function (err) {
876 // This function uses the graceful-fs shared queue
877 if (!err) {
878 retry();
879 }
880
881 if (typeof cb === 'function')
882 cb.apply(this, arguments);
883 })
884 }
885
886 Object.defineProperty(close, previousSymbol, {
887 value: fs$close
888 });
889 return close
890 })(fs$2__default.close);
891
892 fs$2__default.closeSync = (function (fs$closeSync) {
893 function closeSync (fd) {
894 // This function uses the graceful-fs shared queue
895 fs$closeSync.apply(fs$2__default, arguments);
896 retry();
897 }
898
899 Object.defineProperty(closeSync, previousSymbol, {
900 value: fs$closeSync
901 });
902 return closeSync
903 })(fs$2__default.closeSync);
904
905 if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) {
906 process.on('exit', function() {
907 debug(fs$2__default[gracefulQueue]);
908 assert.equal(fs$2__default[gracefulQueue].length, 0);
909 });
910 }
911 }
912
913 if (!global[gracefulQueue]) {
914 publishQueue(global, fs$2__default[gracefulQueue]);
915 }
916
917 module.exports = patch(clone_1(fs$2__default));
918 if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs$2__default.__patched) {
919 module.exports = patch(fs$2__default);
920 fs$2__default.__patched = true;
921 }
922
923 function patch (fs) {
924 // Everything that references the open() function needs to be in here
925 polyfills(fs);
926 fs.gracefulify = patch;
927
928 fs.createReadStream = createReadStream;
929 fs.createWriteStream = createWriteStream;
930 var fs$readFile = fs.readFile;
931 fs.readFile = readFile;
932 function readFile (path, options, cb) {
933 if (typeof options === 'function')
934 cb = options, options = null;
935
936 return go$readFile(path, options, cb)
937
938 function go$readFile (path, options, cb) {
939 return fs$readFile(path, options, function (err) {
940 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
941 enqueue([go$readFile, [path, options, cb]]);
942 else {
943 if (typeof cb === 'function')
944 cb.apply(this, arguments);
945 retry();
946 }
947 })
948 }
949 }
950
951 var fs$writeFile = fs.writeFile;
952 fs.writeFile = writeFile;
953 function writeFile (path, data, options, cb) {
954 if (typeof options === 'function')
955 cb = options, options = null;
956
957 return go$writeFile(path, data, options, cb)
958
959 function go$writeFile (path, data, options, cb) {
960 return fs$writeFile(path, data, options, function (err) {
961 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
962 enqueue([go$writeFile, [path, data, options, cb]]);
963 else {
964 if (typeof cb === 'function')
965 cb.apply(this, arguments);
966 retry();
967 }
968 })
969 }
970 }
971
972 var fs$appendFile = fs.appendFile;
973 if (fs$appendFile)
974 fs.appendFile = appendFile;
975 function appendFile (path, data, options, cb) {
976 if (typeof options === 'function')
977 cb = options, options = null;
978
979 return go$appendFile(path, data, options, cb)
980
981 function go$appendFile (path, data, options, cb) {
982 return fs$appendFile(path, data, options, function (err) {
983 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
984 enqueue([go$appendFile, [path, data, options, cb]]);
985 else {
986 if (typeof cb === 'function')
987 cb.apply(this, arguments);
988 retry();
989 }
990 })
991 }
992 }
993
994 var fs$readdir = fs.readdir;
995 fs.readdir = readdir;
996 function readdir (path, options, cb) {
997 var args = [path];
998 if (typeof options !== 'function') {
999 args.push(options);
1000 } else {
1001 cb = options;
1002 }
1003 args.push(go$readdir$cb);
1004
1005 return go$readdir(args)
1006
1007 function go$readdir$cb (err, files) {
1008 if (files && files.sort)
1009 files.sort();
1010
1011 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
1012 enqueue([go$readdir, [args]]);
1013
1014 else {
1015 if (typeof cb === 'function')
1016 cb.apply(this, arguments);
1017 retry();
1018 }
1019 }
1020 }
1021
1022 function go$readdir (args) {
1023 return fs$readdir.apply(fs, args)
1024 }
1025
1026 if (process.version.substr(0, 4) === 'v0.8') {
1027 var legStreams = legacyStreams(fs);
1028 ReadStream = legStreams.ReadStream;
1029 WriteStream = legStreams.WriteStream;
1030 }
1031
1032 var fs$ReadStream = fs.ReadStream;
1033 if (fs$ReadStream) {
1034 ReadStream.prototype = Object.create(fs$ReadStream.prototype);
1035 ReadStream.prototype.open = ReadStream$open;
1036 }
1037
1038 var fs$WriteStream = fs.WriteStream;
1039 if (fs$WriteStream) {
1040 WriteStream.prototype = Object.create(fs$WriteStream.prototype);
1041 WriteStream.prototype.open = WriteStream$open;
1042 }
1043
1044 Object.defineProperty(fs, 'ReadStream', {
1045 get: function () {
1046 return ReadStream
1047 },
1048 set: function (val) {
1049 ReadStream = val;
1050 },
1051 enumerable: true,
1052 configurable: true
1053 });
1054 Object.defineProperty(fs, 'WriteStream', {
1055 get: function () {
1056 return WriteStream
1057 },
1058 set: function (val) {
1059 WriteStream = val;
1060 },
1061 enumerable: true,
1062 configurable: true
1063 });
1064
1065 // legacy names
1066 var FileReadStream = ReadStream;
1067 Object.defineProperty(fs, 'FileReadStream', {
1068 get: function () {
1069 return FileReadStream
1070 },
1071 set: function (val) {
1072 FileReadStream = val;
1073 },
1074 enumerable: true,
1075 configurable: true
1076 });
1077 var FileWriteStream = WriteStream;
1078 Object.defineProperty(fs, 'FileWriteStream', {
1079 get: function () {
1080 return FileWriteStream
1081 },
1082 set: function (val) {
1083 FileWriteStream = val;
1084 },
1085 enumerable: true,
1086 configurable: true
1087 });
1088
1089 function ReadStream (path, options) {
1090 if (this instanceof ReadStream)
1091 return fs$ReadStream.apply(this, arguments), this
1092 else
1093 return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
1094 }
1095
1096 function ReadStream$open () {
1097 var that = this;
1098 open(that.path, that.flags, that.mode, function (err, fd) {
1099 if (err) {
1100 if (that.autoClose)
1101 that.destroy();
1102
1103 that.emit('error', err);
1104 } else {
1105 that.fd = fd;
1106 that.emit('open', fd);
1107 that.read();
1108 }
1109 });
1110 }
1111
1112 function WriteStream (path, options) {
1113 if (this instanceof WriteStream)
1114 return fs$WriteStream.apply(this, arguments), this
1115 else
1116 return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
1117 }
1118
1119 function WriteStream$open () {
1120 var that = this;
1121 open(that.path, that.flags, that.mode, function (err, fd) {
1122 if (err) {
1123 that.destroy();
1124 that.emit('error', err);
1125 } else {
1126 that.fd = fd;
1127 that.emit('open', fd);
1128 }
1129 });
1130 }
1131
1132 function createReadStream (path, options) {
1133 return new fs.ReadStream(path, options)
1134 }
1135
1136 function createWriteStream (path, options) {
1137 return new fs.WriteStream(path, options)
1138 }
1139
1140 var fs$open = fs.open;
1141 fs.open = open;
1142 function open (path, flags, mode, cb) {
1143 if (typeof mode === 'function')
1144 cb = mode, mode = null;
1145
1146 return go$open(path, flags, mode, cb)
1147
1148 function go$open (path, flags, mode, cb) {
1149 return fs$open(path, flags, mode, function (err, fd) {
1150 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
1151 enqueue([go$open, [path, flags, mode, cb]]);
1152 else {
1153 if (typeof cb === 'function')
1154 cb.apply(this, arguments);
1155 retry();
1156 }
1157 })
1158 }
1159 }
1160
1161 return fs
1162 }
1163
1164 function enqueue (elem) {
1165 debug('ENQUEUE', elem[0].name, elem[1]);
1166 fs$2__default[gracefulQueue].push(elem);
1167 }
1168
1169 function retry () {
1170 var elem = fs$2__default[gracefulQueue].shift();
1171 if (elem) {
1172 debug('RETRY', elem[0].name, elem[1]);
1173 elem[0].apply(null, elem[1]);
1174 }
1175 }
1176 });
1177
1178 var fs_1 = createCommonjsModule(function (module, exports) {
1179 // This is adapted from https://github.com/normalize/mz
1180 // Copyright (c) 2014-2016 Jonathan Ong me@jongleberry.com and Contributors
1181 const u = universalify.fromCallback;
1182
1183
1184 const api = [
1185 'access',
1186 'appendFile',
1187 'chmod',
1188 'chown',
1189 'close',
1190 'fchmod',
1191 'fchown',
1192 'fdatasync',
1193 'fstat',
1194 'fsync',
1195 'ftruncate',
1196 'futimes',
1197 'lchown',
1198 'link',
1199 'lstat',
1200 'mkdir',
1201 'open',
1202 'readFile',
1203 'readdir',
1204 'readlink',
1205 'realpath',
1206 'rename',
1207 'rmdir',
1208 'stat',
1209 'symlink',
1210 'truncate',
1211 'unlink',
1212 'utimes',
1213 'writeFile'
1214 ];
1215 // Add methods that are only in some Node.js versions
1216 // fs.copyFile was added in Node.js v8.5.0
1217 typeof gracefulFs.copyFile === 'function' && api.push('copyFile');
1218 // fs.mkdtemp() was added in Node.js v5.10.0
1219 typeof gracefulFs.mkdtemp === 'function' && api.push('mkdtemp');
1220
1221 // Export all keys:
1222 Object.keys(gracefulFs).forEach(key => {
1223 exports[key] = gracefulFs[key];
1224 });
1225
1226 // Universalify async methods:
1227 api.forEach(method => {
1228 exports[method] = u(gracefulFs[method]);
1229 });
1230
1231 // We differ from mz/fs in that we still ship the old, broken, fs.exists()
1232 // since we are a drop-in replacement for the native module
1233 exports.exists = function (filename, callback) {
1234 if (typeof callback === 'function') {
1235 return gracefulFs.exists(filename, callback)
1236 }
1237 return new Promise(resolve => {
1238 return gracefulFs.exists(filename, resolve)
1239 })
1240 };
1241
1242 // fs.read() & fs.write need special treatment due to multiple callback args
1243
1244 exports.read = function (fd, buffer, offset, length, position, callback) {
1245 if (typeof callback === 'function') {
1246 return gracefulFs.read(fd, buffer, offset, length, position, callback)
1247 }
1248 return new Promise((resolve, reject) => {
1249 gracefulFs.read(fd, buffer, offset, length, position, (err, bytesRead, buffer) => {
1250 if (err) return reject(err)
1251 resolve({ bytesRead, buffer });
1252 });
1253 })
1254 };
1255
1256 // Function signature can be
1257 // fs.write(fd, buffer[, offset[, length[, position]]], callback)
1258 // OR
1259 // fs.write(fd, string[, position[, encoding]], callback)
1260 // so we need to handle both cases
1261 exports.write = function (fd, buffer, a, b, c, callback) {
1262 if (typeof arguments[arguments.length - 1] === 'function') {
1263 return gracefulFs.write(fd, buffer, a, b, c, callback)
1264 }
1265
1266 // Check for old, depricated fs.write(fd, string[, position[, encoding]], callback)
1267 if (typeof buffer === 'string') {
1268 return new Promise((resolve, reject) => {
1269 gracefulFs.write(fd, buffer, a, b, (err, bytesWritten, buffer) => {
1270 if (err) return reject(err)
1271 resolve({ bytesWritten, buffer });
1272 });
1273 })
1274 }
1275
1276 return new Promise((resolve, reject) => {
1277 gracefulFs.write(fd, buffer, a, b, c, (err, bytesWritten, buffer) => {
1278 if (err) return reject(err)
1279 resolve({ bytesWritten, buffer });
1280 });
1281 })
1282 };
1283 });
1284 var fs_2 = fs_1.exists;
1285 var fs_3 = fs_1.read;
1286 var fs_4 = fs_1.write;
1287
1288 // HFS, ext{2,3}, FAT do not, Node.js v0.10 does not
1289 function hasMillisResSync () {
1290 let tmpfile = path__default.join('millis-test-sync' + Date.now().toString() + Math.random().toString().slice(2));
1291 tmpfile = path__default.join(os__default.tmpdir(), tmpfile);
1292
1293 // 550 millis past UNIX epoch
1294 const d = new Date(1435410243862);
1295 gracefulFs.writeFileSync(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141');
1296 const fd = gracefulFs.openSync(tmpfile, 'r+');
1297 gracefulFs.futimesSync(fd, d, d);
1298 gracefulFs.closeSync(fd);
1299 return gracefulFs.statSync(tmpfile).mtime > 1435410243000
1300 }
1301
1302 function hasMillisRes (callback) {
1303 let tmpfile = path__default.join('millis-test' + Date.now().toString() + Math.random().toString().slice(2));
1304 tmpfile = path__default.join(os__default.tmpdir(), tmpfile);
1305
1306 // 550 millis past UNIX epoch
1307 const d = new Date(1435410243862);
1308 gracefulFs.writeFile(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141', err => {
1309 if (err) return callback(err)
1310 gracefulFs.open(tmpfile, 'r+', (err, fd) => {
1311 if (err) return callback(err)
1312 gracefulFs.futimes(fd, d, d, err => {
1313 if (err) return callback(err)
1314 gracefulFs.close(fd, err => {
1315 if (err) return callback(err)
1316 gracefulFs.stat(tmpfile, (err, stats) => {
1317 if (err) return callback(err)
1318 callback(null, stats.mtime > 1435410243000);
1319 });
1320 });
1321 });
1322 });
1323 });
1324 }
1325
1326 function timeRemoveMillis (timestamp) {
1327 if (typeof timestamp === 'number') {
1328 return Math.floor(timestamp / 1000) * 1000
1329 } else if (timestamp instanceof Date) {
1330 return new Date(Math.floor(timestamp.getTime() / 1000) * 1000)
1331 } else {
1332 throw new Error('fs-extra: timeRemoveMillis() unknown parameter type')
1333 }
1334 }
1335
1336 function utimesMillis (path, atime, mtime, callback) {
1337 // if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback)
1338 gracefulFs.open(path, 'r+', (err, fd) => {
1339 if (err) return callback(err)
1340 gracefulFs.futimes(fd, atime, mtime, futimesErr => {
1341 gracefulFs.close(fd, closeErr => {
1342 if (callback) callback(futimesErr || closeErr);
1343 });
1344 });
1345 });
1346 }
1347
1348 var utimes = {
1349 hasMillisRes,
1350 hasMillisResSync,
1351 timeRemoveMillis,
1352 utimesMillis
1353 };
1354
1355 // imported from ncp (this is temporary, will rewrite)
1356
1357
1358
1359
1360
1361 function ncp (source, dest, options, callback) {
1362 if (!callback) {
1363 callback = options;
1364 options = {};
1365 }
1366
1367 var basePath = process.cwd();
1368 var currentPath = path__default.resolve(basePath, source);
1369 var targetPath = path__default.resolve(basePath, dest);
1370
1371 var filter = options.filter;
1372 var transform = options.transform;
1373 var overwrite = options.overwrite;
1374 // If overwrite is undefined, use clobber, otherwise default to true:
1375 if (overwrite === undefined) overwrite = options.clobber;
1376 if (overwrite === undefined) overwrite = true;
1377 var errorOnExist = options.errorOnExist;
1378 var dereference = options.dereference;
1379 var preserveTimestamps = options.preserveTimestamps === true;
1380
1381 var started = 0;
1382 var finished = 0;
1383 var running = 0;
1384
1385 var errored = false;
1386
1387 startCopy(currentPath);
1388
1389 function startCopy (source) {
1390 started++;
1391 if (filter) {
1392 if (filter instanceof RegExp) {
1393 console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function');
1394 if (!filter.test(source)) {
1395 return doneOne(true)
1396 }
1397 } else if (typeof filter === 'function') {
1398 if (!filter(source, dest)) {
1399 return doneOne(true)
1400 }
1401 }
1402 }
1403 return getStats(source)
1404 }
1405
1406 function getStats (source) {
1407 var stat = dereference ? gracefulFs.stat : gracefulFs.lstat;
1408 running++;
1409 stat(source, function (err, stats) {
1410 if (err) return onError(err)
1411
1412 // We need to get the mode from the stats object and preserve it.
1413 var item = {
1414 name: source,
1415 mode: stats.mode,
1416 mtime: stats.mtime, // modified time
1417 atime: stats.atime, // access time
1418 stats: stats // temporary
1419 };
1420
1421 if (stats.isDirectory()) {
1422 return onDir(item)
1423 } else if (stats.isFile() || stats.isCharacterDevice() || stats.isBlockDevice()) {
1424 return onFile(item)
1425 } else if (stats.isSymbolicLink()) {
1426 // Symlinks don't really need to know about the mode.
1427 return onLink(source)
1428 }
1429 });
1430 }
1431
1432 function onFile (file) {
1433 var target = file.name.replace(currentPath, targetPath.replace('$', '$$$$')); // escapes '$' with '$$'
1434 isWritable(target, function (writable) {
1435 if (writable) {
1436 copyFile(file, target);
1437 } else {
1438 if (overwrite) {
1439 rmFile(target, function () {
1440 copyFile(file, target);
1441 });
1442 } else if (errorOnExist) {
1443 onError(new Error(target + ' already exists'));
1444 } else {
1445 doneOne();
1446 }
1447 }
1448 });
1449 }
1450
1451 function copyFile (file, target) {
1452 var readStream = gracefulFs.createReadStream(file.name);
1453 var writeStream = gracefulFs.createWriteStream(target, { mode: file.mode });
1454
1455 readStream.on('error', onError);
1456 writeStream.on('error', onError);
1457
1458 if (transform) {
1459 transform(readStream, writeStream, file);
1460 } else {
1461 writeStream.on('open', function () {
1462 readStream.pipe(writeStream);
1463 });
1464 }
1465
1466 writeStream.once('close', function () {
1467 gracefulFs.chmod(target, file.mode, function (err) {
1468 if (err) return onError(err)
1469 if (preserveTimestamps) {
1470 utimes.utimesMillis(target, file.atime, file.mtime, function (err) {
1471 if (err) return onError(err)
1472 return doneOne()
1473 });
1474 } else {
1475 doneOne();
1476 }
1477 });
1478 });
1479 }
1480
1481 function rmFile (file, done) {
1482 gracefulFs.unlink(file, function (err) {
1483 if (err) return onError(err)
1484 return done()
1485 });
1486 }
1487
1488 function onDir (dir) {
1489 var target = dir.name.replace(currentPath, targetPath.replace('$', '$$$$')); // escapes '$' with '$$'
1490 isWritable(target, function (writable) {
1491 if (writable) {
1492 return mkDir(dir, target)
1493 }
1494 copyDir(dir.name);
1495 });
1496 }
1497
1498 function mkDir (dir, target) {
1499 gracefulFs.mkdir(target, dir.mode, function (err) {
1500 if (err) return onError(err)
1501 // despite setting mode in fs.mkdir, doesn't seem to work
1502 // so we set it here.
1503 gracefulFs.chmod(target, dir.mode, function (err) {
1504 if (err) return onError(err)
1505 copyDir(dir.name);
1506 });
1507 });
1508 }
1509
1510 function copyDir (dir) {
1511 gracefulFs.readdir(dir, function (err, items) {
1512 if (err) return onError(err)
1513 items.forEach(function (item) {
1514 startCopy(path__default.join(dir, item));
1515 });
1516 return doneOne()
1517 });
1518 }
1519
1520 function onLink (link) {
1521 var target = link.replace(currentPath, targetPath);
1522 gracefulFs.readlink(link, function (err, resolvedPath) {
1523 if (err) return onError(err)
1524 checkLink(resolvedPath, target);
1525 });
1526 }
1527
1528 function checkLink (resolvedPath, target) {
1529 if (dereference) {
1530 resolvedPath = path__default.resolve(basePath, resolvedPath);
1531 }
1532 isWritable(target, function (writable) {
1533 if (writable) {
1534 return makeLink(resolvedPath, target)
1535 }
1536 gracefulFs.readlink(target, function (err, targetDest) {
1537 if (err) return onError(err)
1538
1539 if (dereference) {
1540 targetDest = path__default.resolve(basePath, targetDest);
1541 }
1542 if (targetDest === resolvedPath) {
1543 return doneOne()
1544 }
1545 return rmFile(target, function () {
1546 makeLink(resolvedPath, target);
1547 })
1548 });
1549 });
1550 }
1551
1552 function makeLink (linkPath, target) {
1553 gracefulFs.symlink(linkPath, target, function (err) {
1554 if (err) return onError(err)
1555 return doneOne()
1556 });
1557 }
1558
1559 function isWritable (path, done) {
1560 gracefulFs.lstat(path, function (err) {
1561 if (err) {
1562 if (err.code === 'ENOENT') return done(true)
1563 return done(false)
1564 }
1565 return done(false)
1566 });
1567 }
1568
1569 function onError (err) {
1570 // ensure callback is defined & called only once:
1571 if (!errored && callback !== undefined) {
1572 errored = true;
1573 return callback(err)
1574 }
1575 }
1576
1577 function doneOne (skipped) {
1578 if (!skipped) running--;
1579 finished++;
1580 if ((started === finished) && (running === 0)) {
1581 if (callback !== undefined) {
1582 return callback(null)
1583 }
1584 }
1585 }
1586 }
1587
1588 var ncp_1 = ncp;
1589
1590 // get drive on windows
1591 function getRootPath (p) {
1592 p = path__default.normalize(path__default.resolve(p)).split(path__default.sep);
1593 if (p.length > 0) return p[0]
1594 return null
1595 }
1596
1597 // http://stackoverflow.com/a/62888/10333 contains more accurate
1598 // TODO: expand to include the rest
1599 const INVALID_PATH_CHARS = /[<>:"|?*]/;
1600
1601 function invalidWin32Path (p) {
1602 const rp = getRootPath(p);
1603 p = p.replace(rp, '');
1604 return INVALID_PATH_CHARS.test(p)
1605 }
1606
1607 var win32 = {
1608 getRootPath,
1609 invalidWin32Path
1610 };
1611
1612 const invalidWin32Path$1 = win32.invalidWin32Path;
1613
1614 const o777 = parseInt('0777', 8);
1615
1616 function mkdirs (p, opts, callback, made) {
1617 if (typeof opts === 'function') {
1618 callback = opts;
1619 opts = {};
1620 } else if (!opts || typeof opts !== 'object') {
1621 opts = { mode: opts };
1622 }
1623
1624 if (process.platform === 'win32' && invalidWin32Path$1(p)) {
1625 const errInval = new Error(p + ' contains invalid WIN32 path characters.');
1626 errInval.code = 'EINVAL';
1627 return callback(errInval)
1628 }
1629
1630 let mode = opts.mode;
1631 const xfs = opts.fs || gracefulFs;
1632
1633 if (mode === undefined) {
1634 mode = o777 & (~process.umask());
1635 }
1636 if (!made) made = null;
1637
1638 callback = callback || function () {};
1639 p = path__default.resolve(p);
1640
1641 xfs.mkdir(p, mode, er => {
1642 if (!er) {
1643 made = made || p;
1644 return callback(null, made)
1645 }
1646 switch (er.code) {
1647 case 'ENOENT':
1648 if (path__default.dirname(p) === p) return callback(er)
1649 mkdirs(path__default.dirname(p), opts, (er, made) => {
1650 if (er) callback(er, made);
1651 else mkdirs(p, opts, callback, made);
1652 });
1653 break
1654
1655 // In the case of any other error, just see if there's a dir
1656 // there already. If so, then hooray! If not, then something
1657 // is borked.
1658 default:
1659 xfs.stat(p, (er2, stat) => {
1660 // if the stat fails, then that's super weird.
1661 // let the original error be the failure reason.
1662 if (er2 || !stat.isDirectory()) callback(er, made);
1663 else callback(null, made);
1664 });
1665 break
1666 }
1667 });
1668 }
1669
1670 var mkdirs_1 = mkdirs;
1671
1672 const invalidWin32Path$2 = win32.invalidWin32Path;
1673
1674 const o777$1 = parseInt('0777', 8);
1675
1676 function mkdirsSync (p, opts, made) {
1677 if (!opts || typeof opts !== 'object') {
1678 opts = { mode: opts };
1679 }
1680
1681 let mode = opts.mode;
1682 const xfs = opts.fs || gracefulFs;
1683
1684 if (process.platform === 'win32' && invalidWin32Path$2(p)) {
1685 const errInval = new Error(p + ' contains invalid WIN32 path characters.');
1686 errInval.code = 'EINVAL';
1687 throw errInval
1688 }
1689
1690 if (mode === undefined) {
1691 mode = o777$1 & (~process.umask());
1692 }
1693 if (!made) made = null;
1694
1695 p = path__default.resolve(p);
1696
1697 try {
1698 xfs.mkdirSync(p, mode);
1699 made = made || p;
1700 } catch (err0) {
1701 switch (err0.code) {
1702 case 'ENOENT':
1703 if (path__default.dirname(p) === p) throw err0
1704 made = mkdirsSync(path__default.dirname(p), opts, made);
1705 mkdirsSync(p, opts, made);
1706 break
1707
1708 // In the case of any other error, just see if there's a dir
1709 // there already. If so, then hooray! If not, then something
1710 // is borked.
1711 default:
1712 let stat;
1713 try {
1714 stat = xfs.statSync(p);
1715 } catch (err1) {
1716 throw err0
1717 }
1718 if (!stat.isDirectory()) throw err0
1719 break
1720 }
1721 }
1722
1723 return made
1724 }
1725
1726 var mkdirsSync_1 = mkdirsSync;
1727
1728 const u = universalify.fromCallback;
1729 const mkdirs$1 = u(mkdirs_1);
1730
1731
1732 var mkdirs_1$1 = {
1733 mkdirs: mkdirs$1,
1734 mkdirsSync: mkdirsSync_1,
1735 // alias
1736 mkdirp: mkdirs$1,
1737 mkdirpSync: mkdirsSync_1,
1738 ensureDir: mkdirs$1,
1739 ensureDirSync: mkdirsSync_1
1740 };
1741
1742 const u$1 = universalify.fromPromise;
1743
1744
1745 function pathExists (path) {
1746 return fs_1.access(path).then(() => true).catch(() => false)
1747 }
1748
1749 var pathExists_1 = {
1750 pathExists: u$1(pathExists),
1751 pathExistsSync: fs_1.existsSync
1752 };
1753
1754 const pathExists$1 = pathExists_1.pathExists;
1755
1756 function copy (src, dest, options, callback) {
1757 if (typeof options === 'function' && !callback) {
1758 callback = options;
1759 options = {};
1760 } else if (typeof options === 'function' || options instanceof RegExp) {
1761 options = {filter: options};
1762 }
1763 callback = callback || function () {};
1764 options = options || {};
1765
1766 // Warn about using preserveTimestamps on 32-bit node:
1767 if (options.preserveTimestamps && process.arch === 'ia32') {
1768 console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
1769 see https://github.com/jprichardson/node-fs-extra/issues/269`);
1770 }
1771
1772 // don't allow src and dest to be the same
1773 const basePath = process.cwd();
1774 const currentPath = path__default.resolve(basePath, src);
1775 const targetPath = path__default.resolve(basePath, dest);
1776 if (currentPath === targetPath) return callback(new Error('Source and destination must not be the same.'))
1777
1778 gracefulFs.lstat(src, (err, stats) => {
1779 if (err) return callback(err)
1780
1781 let dir = null;
1782 if (stats.isDirectory()) {
1783 const parts = dest.split(path__default.sep);
1784 parts.pop();
1785 dir = parts.join(path__default.sep);
1786 } else {
1787 dir = path__default.dirname(dest);
1788 }
1789
1790 pathExists$1(dir, (err, dirExists) => {
1791 if (err) return callback(err)
1792 if (dirExists) return ncp_1(src, dest, options, callback)
1793 mkdirs_1$1.mkdirs(dir, err => {
1794 if (err) return callback(err)
1795 ncp_1(src, dest, options, callback);
1796 });
1797 });
1798 });
1799 }
1800
1801 var copy_1 = copy;
1802
1803 const u$2 = universalify.fromCallback;
1804 var copy$1 = {
1805 copy: u$2(copy_1)
1806 };
1807
1808 /* eslint-disable node/no-deprecated-api */
1809 var buffer = function (size) {
1810 if (typeof Buffer.allocUnsafe === 'function') {
1811 try {
1812 return Buffer.allocUnsafe(size)
1813 } catch (e) {
1814 return new Buffer(size)
1815 }
1816 }
1817 return new Buffer(size)
1818 };
1819
1820 const BUF_LENGTH = 64 * 1024;
1821 const _buff = buffer(BUF_LENGTH);
1822
1823 function copyFileSync (srcFile, destFile, options) {
1824 const overwrite = options.overwrite;
1825 const errorOnExist = options.errorOnExist;
1826 const preserveTimestamps = options.preserveTimestamps;
1827
1828 if (gracefulFs.existsSync(destFile)) {
1829 if (overwrite) {
1830 gracefulFs.unlinkSync(destFile);
1831 } else if (errorOnExist) {
1832 throw new Error(`${destFile} already exists`)
1833 } else return
1834 }
1835
1836 const fdr = gracefulFs.openSync(srcFile, 'r');
1837 const stat = gracefulFs.fstatSync(fdr);
1838 const fdw = gracefulFs.openSync(destFile, 'w', stat.mode);
1839 let bytesRead = 1;
1840 let pos = 0;
1841
1842 while (bytesRead > 0) {
1843 bytesRead = gracefulFs.readSync(fdr, _buff, 0, BUF_LENGTH, pos);
1844 gracefulFs.writeSync(fdw, _buff, 0, bytesRead);
1845 pos += bytesRead;
1846 }
1847
1848 if (preserveTimestamps) {
1849 gracefulFs.futimesSync(fdw, stat.atime, stat.mtime);
1850 }
1851
1852 gracefulFs.closeSync(fdr);
1853 gracefulFs.closeSync(fdw);
1854 }
1855
1856 var copyFileSync_1 = copyFileSync;
1857
1858 function copySync (src, dest, options) {
1859 if (typeof options === 'function' || options instanceof RegExp) {
1860 options = {filter: options};
1861 }
1862
1863 options = options || {};
1864 options.recursive = !!options.recursive;
1865
1866 // default to true for now
1867 options.clobber = 'clobber' in options ? !!options.clobber : true;
1868 // overwrite falls back to clobber
1869 options.overwrite = 'overwrite' in options ? !!options.overwrite : options.clobber;
1870 options.dereference = 'dereference' in options ? !!options.dereference : false;
1871 options.preserveTimestamps = 'preserveTimestamps' in options ? !!options.preserveTimestamps : false;
1872
1873 options.filter = options.filter || function () { return true };
1874
1875 // Warn about using preserveTimestamps on 32-bit node:
1876 if (options.preserveTimestamps && process.arch === 'ia32') {
1877 console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
1878 see https://github.com/jprichardson/node-fs-extra/issues/269`);
1879 }
1880
1881 const stats = (options.recursive && !options.dereference) ? gracefulFs.lstatSync(src) : gracefulFs.statSync(src);
1882 const destFolder = path__default.dirname(dest);
1883 const destFolderExists = gracefulFs.existsSync(destFolder);
1884 let performCopy = false;
1885
1886 if (options.filter instanceof RegExp) {
1887 console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function');
1888 performCopy = options.filter.test(src);
1889 } else if (typeof options.filter === 'function') performCopy = options.filter(src, dest);
1890
1891 if (stats.isFile() && performCopy) {
1892 if (!destFolderExists) mkdirs_1$1.mkdirsSync(destFolder);
1893 copyFileSync_1(src, dest, {
1894 overwrite: options.overwrite,
1895 errorOnExist: options.errorOnExist,
1896 preserveTimestamps: options.preserveTimestamps
1897 });
1898 } else if (stats.isDirectory() && performCopy) {
1899 if (!gracefulFs.existsSync(dest)) mkdirs_1$1.mkdirsSync(dest);
1900 const contents = gracefulFs.readdirSync(src);
1901 contents.forEach(content => {
1902 const opts = options;
1903 opts.recursive = true;
1904 copySync(path__default.join(src, content), path__default.join(dest, content), opts);
1905 });
1906 } else if (options.recursive && stats.isSymbolicLink() && performCopy) {
1907 const srcPath = gracefulFs.readlinkSync(src);
1908 gracefulFs.symlinkSync(srcPath, dest);
1909 }
1910 }
1911
1912 var copySync_1 = copySync;
1913
1914 var copySync$1 = {
1915 copySync: copySync_1
1916 };
1917
1918 const isWindows = (process.platform === 'win32');
1919
1920 function defaults (options) {
1921 const methods = [
1922 'unlink',
1923 'chmod',
1924 'stat',
1925 'lstat',
1926 'rmdir',
1927 'readdir'
1928 ];
1929 methods.forEach(m => {
1930 options[m] = options[m] || gracefulFs[m];
1931 m = m + 'Sync';
1932 options[m] = options[m] || gracefulFs[m];
1933 });
1934
1935 options.maxBusyTries = options.maxBusyTries || 3;
1936 }
1937
1938 function rimraf (p, options, cb) {
1939 let busyTries = 0;
1940
1941 if (typeof options === 'function') {
1942 cb = options;
1943 options = {};
1944 }
1945
1946 assert(p, 'rimraf: missing path');
1947 assert.equal(typeof p, 'string', 'rimraf: path should be a string');
1948 assert.equal(typeof cb, 'function', 'rimraf: callback function required');
1949 assert(options, 'rimraf: invalid options argument provided');
1950 assert.equal(typeof options, 'object', 'rimraf: options should be object');
1951
1952 defaults(options);
1953
1954 rimraf_(p, options, function CB (er) {
1955 if (er) {
1956 if ((er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') &&
1957 busyTries < options.maxBusyTries) {
1958 busyTries++;
1959 let time = busyTries * 100;
1960 // try again, with the same exact callback as this one.
1961 return setTimeout(() => rimraf_(p, options, CB), time)
1962 }
1963
1964 // already gone
1965 if (er.code === 'ENOENT') er = null;
1966 }
1967
1968 cb(er);
1969 });
1970 }
1971
1972 // Two possible strategies.
1973 // 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
1974 // 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
1975 //
1976 // Both result in an extra syscall when you guess wrong. However, there
1977 // are likely far more normal files in the world than directories. This
1978 // is based on the assumption that a the average number of files per
1979 // directory is >= 1.
1980 //
1981 // If anyone ever complains about this, then I guess the strategy could
1982 // be made configurable somehow. But until then, YAGNI.
1983 function rimraf_ (p, options, cb) {
1984 assert(p);
1985 assert(options);
1986 assert(typeof cb === 'function');
1987
1988 // sunos lets the root user unlink directories, which is... weird.
1989 // so we have to lstat here and make sure it's not a dir.
1990 options.lstat(p, (er, st) => {
1991 if (er && er.code === 'ENOENT') {
1992 return cb(null)
1993 }
1994
1995 // Windows can EPERM on stat. Life is suffering.
1996 if (er && er.code === 'EPERM' && isWindows) {
1997 return fixWinEPERM(p, options, er, cb)
1998 }
1999
2000 if (st && st.isDirectory()) {
2001 return rmdir(p, options, er, cb)
2002 }
2003
2004 options.unlink(p, er => {
2005 if (er) {
2006 if (er.code === 'ENOENT') {
2007 return cb(null)
2008 }
2009 if (er.code === 'EPERM') {
2010 return (isWindows)
2011 ? fixWinEPERM(p, options, er, cb)
2012 : rmdir(p, options, er, cb)
2013 }
2014 if (er.code === 'EISDIR') {
2015 return rmdir(p, options, er, cb)
2016 }
2017 }
2018 return cb(er)
2019 });
2020 });
2021 }
2022
2023 function fixWinEPERM (p, options, er, cb) {
2024 assert(p);
2025 assert(options);
2026 assert(typeof cb === 'function');
2027 if (er) {
2028 assert(er instanceof Error);
2029 }
2030
2031 options.chmod(p, 666, er2 => {
2032 if (er2) {
2033 cb(er2.code === 'ENOENT' ? null : er);
2034 } else {
2035 options.stat(p, (er3, stats) => {
2036 if (er3) {
2037 cb(er3.code === 'ENOENT' ? null : er);
2038 } else if (stats.isDirectory()) {
2039 rmdir(p, options, er, cb);
2040 } else {
2041 options.unlink(p, cb);
2042 }
2043 });
2044 }
2045 });
2046 }
2047
2048 function fixWinEPERMSync (p, options, er) {
2049 let stats;
2050
2051 assert(p);
2052 assert(options);
2053 if (er) {
2054 assert(er instanceof Error);
2055 }
2056
2057 try {
2058 options.chmodSync(p, 666);
2059 } catch (er2) {
2060 if (er2.code === 'ENOENT') {
2061 return
2062 } else {
2063 throw er
2064 }
2065 }
2066
2067 try {
2068 stats = options.statSync(p);
2069 } catch (er3) {
2070 if (er3.code === 'ENOENT') {
2071 return
2072 } else {
2073 throw er
2074 }
2075 }
2076
2077 if (stats.isDirectory()) {
2078 rmdirSync(p, options, er);
2079 } else {
2080 options.unlinkSync(p);
2081 }
2082 }
2083
2084 function rmdir (p, options, originalEr, cb) {
2085 assert(p);
2086 assert(options);
2087 if (originalEr) {
2088 assert(originalEr instanceof Error);
2089 }
2090 assert(typeof cb === 'function');
2091
2092 // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
2093 // if we guessed wrong, and it's not a directory, then
2094 // raise the original error.
2095 options.rmdir(p, er => {
2096 if (er && (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM')) {
2097 rmkids(p, options, cb);
2098 } else if (er && er.code === 'ENOTDIR') {
2099 cb(originalEr);
2100 } else {
2101 cb(er);
2102 }
2103 });
2104 }
2105
2106 function rmkids (p, options, cb) {
2107 assert(p);
2108 assert(options);
2109 assert(typeof cb === 'function');
2110
2111 options.readdir(p, (er, files) => {
2112 if (er) return cb(er)
2113
2114 let n = files.length;
2115 let errState;
2116
2117 if (n === 0) return options.rmdir(p, cb)
2118
2119 files.forEach(f => {
2120 rimraf(path__default.join(p, f), options, er => {
2121 if (errState) {
2122 return
2123 }
2124 if (er) return cb(errState = er)
2125 if (--n === 0) {
2126 options.rmdir(p, cb);
2127 }
2128 });
2129 });
2130 });
2131 }
2132
2133 // this looks simpler, and is strictly *faster*, but will
2134 // tie up the JavaScript thread and fail on excessively
2135 // deep directory trees.
2136 function rimrafSync (p, options) {
2137 let st;
2138
2139 options = options || {};
2140 defaults(options);
2141
2142 assert(p, 'rimraf: missing path');
2143 assert.equal(typeof p, 'string', 'rimraf: path should be a string');
2144 assert(options, 'rimraf: missing options');
2145 assert.equal(typeof options, 'object', 'rimraf: options should be object');
2146
2147 try {
2148 st = options.lstatSync(p);
2149 } catch (er) {
2150 if (er.code === 'ENOENT') {
2151 return
2152 }
2153
2154 // Windows can EPERM on stat. Life is suffering.
2155 if (er.code === 'EPERM' && isWindows) {
2156 fixWinEPERMSync(p, options, er);
2157 }
2158 }
2159
2160 try {
2161 // sunos lets the root user unlink directories, which is... weird.
2162 if (st && st.isDirectory()) {
2163 rmdirSync(p, options, null);
2164 } else {
2165 options.unlinkSync(p);
2166 }
2167 } catch (er) {
2168 if (er.code === 'ENOENT') {
2169 return
2170 } else if (er.code === 'EPERM') {
2171 return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
2172 } else if (er.code !== 'EISDIR') {
2173 throw er
2174 }
2175 rmdirSync(p, options, er);
2176 }
2177 }
2178
2179 function rmdirSync (p, options, originalEr) {
2180 assert(p);
2181 assert(options);
2182 if (originalEr) {
2183 assert(originalEr instanceof Error);
2184 }
2185
2186 try {
2187 options.rmdirSync(p);
2188 } catch (er) {
2189 if (er.code === 'ENOTDIR') {
2190 throw originalEr
2191 } else if (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM') {
2192 rmkidsSync(p, options);
2193 } else if (er.code !== 'ENOENT') {
2194 throw er
2195 }
2196 }
2197 }
2198
2199 function rmkidsSync (p, options) {
2200 assert(p);
2201 assert(options);
2202 options.readdirSync(p).forEach(f => rimrafSync(path__default.join(p, f), options));
2203
2204 // We only end up here once we got ENOTEMPTY at least once, and
2205 // at this point, we are guaranteed to have removed all the kids.
2206 // So, we know that it won't be ENOENT or ENOTDIR or anything else.
2207 // try really hard to delete stuff on windows, because it has a
2208 // PROFOUNDLY annoying habit of not closing handles promptly when
2209 // files are deleted, resulting in spurious ENOTEMPTY errors.
2210 const retries = isWindows ? 100 : 1;
2211 let i = 0;
2212 do {
2213 let threw = true;
2214 try {
2215 const ret = options.rmdirSync(p, options);
2216 threw = false;
2217 return ret
2218 } finally {
2219 if (++i < retries && threw) continue // eslint-disable-line
2220 }
2221 } while (true)
2222 }
2223
2224 var rimraf_1 = rimraf;
2225 rimraf.sync = rimrafSync;
2226
2227 const u$3 = universalify.fromCallback;
2228
2229
2230 var remove = {
2231 remove: u$3(rimraf_1),
2232 removeSync: rimraf_1.sync
2233 };
2234
2235 var _fs;
2236 try {
2237 _fs = gracefulFs;
2238 } catch (_) {
2239 _fs = fs$2__default;
2240 }
2241
2242 function readFile (file, options, callback) {
2243 if (callback == null) {
2244 callback = options;
2245 options = {};
2246 }
2247
2248 if (typeof options === 'string') {
2249 options = {encoding: options};
2250 }
2251
2252 options = options || {};
2253 var fs = options.fs || _fs;
2254
2255 var shouldThrow = true;
2256 if ('throws' in options) {
2257 shouldThrow = options.throws;
2258 }
2259
2260 fs.readFile(file, options, function (err, data) {
2261 if (err) return callback(err)
2262
2263 data = stripBom(data);
2264
2265 var obj;
2266 try {
2267 obj = JSON.parse(data, options ? options.reviver : null);
2268 } catch (err2) {
2269 if (shouldThrow) {
2270 err2.message = file + ': ' + err2.message;
2271 return callback(err2)
2272 } else {
2273 return callback(null, null)
2274 }
2275 }
2276
2277 callback(null, obj);
2278 });
2279 }
2280
2281 function readFileSync (file, options) {
2282 options = options || {};
2283 if (typeof options === 'string') {
2284 options = {encoding: options};
2285 }
2286
2287 var fs = options.fs || _fs;
2288
2289 var shouldThrow = true;
2290 if ('throws' in options) {
2291 shouldThrow = options.throws;
2292 }
2293
2294 try {
2295 var content = fs.readFileSync(file, options);
2296 content = stripBom(content);
2297 return JSON.parse(content, options.reviver)
2298 } catch (err) {
2299 if (shouldThrow) {
2300 err.message = file + ': ' + err.message;
2301 throw err
2302 } else {
2303 return null
2304 }
2305 }
2306 }
2307
2308 function stringify (obj, options) {
2309 var spaces;
2310 var EOL = '\n';
2311 if (typeof options === 'object' && options !== null) {
2312 if (options.spaces) {
2313 spaces = options.spaces;
2314 }
2315 if (options.EOL) {
2316 EOL = options.EOL;
2317 }
2318 }
2319
2320 var str = JSON.stringify(obj, options ? options.replacer : null, spaces);
2321
2322 return str.replace(/\n/g, EOL) + EOL
2323 }
2324
2325 function writeFile (file, obj, options, callback) {
2326 if (callback == null) {
2327 callback = options;
2328 options = {};
2329 }
2330 options = options || {};
2331 var fs = options.fs || _fs;
2332
2333 var str = '';
2334 try {
2335 str = stringify(obj, options);
2336 } catch (err) {
2337 // Need to return whether a callback was passed or not
2338 if (callback) callback(err, null);
2339 return
2340 }
2341
2342 fs.writeFile(file, str, options, callback);
2343 }
2344
2345 function writeFileSync (file, obj, options) {
2346 options = options || {};
2347 var fs = options.fs || _fs;
2348
2349 var str = stringify(obj, options);
2350 // not sure if fs.writeFileSync returns anything, but just in case
2351 return fs.writeFileSync(file, str, options)
2352 }
2353
2354 function stripBom (content) {
2355 // we do this because JSON.parse would convert it to a utf8 string if encoding wasn't specified
2356 if (Buffer.isBuffer(content)) content = content.toString('utf8');
2357 content = content.replace(/^\uFEFF/, '');
2358 return content
2359 }
2360
2361 var jsonfile = {
2362 readFile: readFile,
2363 readFileSync: readFileSync,
2364 writeFile: writeFile,
2365 writeFileSync: writeFileSync
2366 };
2367
2368 var jsonfile_1 = jsonfile;
2369
2370 const u$4 = universalify.fromCallback;
2371
2372
2373 var jsonfile$1 = {
2374 // jsonfile exports
2375 readJson: u$4(jsonfile_1.readFile),
2376 readJsonSync: jsonfile_1.readFileSync,
2377 writeJson: u$4(jsonfile_1.writeFile),
2378 writeJsonSync: jsonfile_1.writeFileSync
2379 };
2380
2381 const pathExists$2 = pathExists_1.pathExists;
2382
2383
2384 function outputJson (file, data, options, callback) {
2385 if (typeof options === 'function') {
2386 callback = options;
2387 options = {};
2388 }
2389
2390 const dir = path__default.dirname(file);
2391
2392 pathExists$2(dir, (err, itDoes) => {
2393 if (err) return callback(err)
2394 if (itDoes) return jsonfile$1.writeJson(file, data, options, callback)
2395
2396 mkdirs_1$1.mkdirs(dir, err => {
2397 if (err) return callback(err)
2398 jsonfile$1.writeJson(file, data, options, callback);
2399 });
2400 });
2401 }
2402
2403 var outputJson_1 = outputJson;
2404
2405 function outputJsonSync (file, data, options) {
2406 const dir = path__default.dirname(file);
2407
2408 if (!gracefulFs.existsSync(dir)) {
2409 mkdirs_1$1.mkdirsSync(dir);
2410 }
2411
2412 jsonfile$1.writeJsonSync(file, data, options);
2413 }
2414
2415 var outputJsonSync_1 = outputJsonSync;
2416
2417 const u$5 = universalify.fromCallback;
2418
2419
2420 jsonfile$1.outputJson = u$5(outputJson_1);
2421 jsonfile$1.outputJsonSync = outputJsonSync_1;
2422 // aliases
2423 jsonfile$1.outputJSON = jsonfile$1.outputJson;
2424 jsonfile$1.outputJSONSync = jsonfile$1.outputJsonSync;
2425 jsonfile$1.writeJSON = jsonfile$1.writeJson;
2426 jsonfile$1.writeJSONSync = jsonfile$1.writeJsonSync;
2427 jsonfile$1.readJSON = jsonfile$1.readJson;
2428 jsonfile$1.readJSONSync = jsonfile$1.readJsonSync;
2429
2430 var json = jsonfile$1;
2431
2432 // most of this code was written by Andrew Kelley
2433 // licensed under the BSD license: see
2434 // https://github.com/andrewrk/node-mv/blob/master/package.json
2435
2436 // this needs a cleanup
2437
2438 const u$6 = universalify.fromCallback;
2439
2440
2441
2442 const remove$1 = remove.remove;
2443 const mkdirp = mkdirs_1$1.mkdirs;
2444
2445 function move (src, dest, options, callback) {
2446 if (typeof options === 'function') {
2447 callback = options;
2448 options = {};
2449 }
2450
2451 const overwrite = options.overwrite || options.clobber || false;
2452
2453 isSrcSubdir(src, dest, (err, itIs) => {
2454 if (err) return callback(err)
2455 if (itIs) return callback(new Error(`Cannot move '${src}' to a subdirectory of itself, '${dest}'.`))
2456 mkdirp(path__default.dirname(dest), err => {
2457 if (err) return callback(err)
2458 doRename();
2459 });
2460 });
2461
2462 function doRename () {
2463 if (path__default.resolve(src) === path__default.resolve(dest)) {
2464 gracefulFs.access(src, callback);
2465 } else if (overwrite) {
2466 gracefulFs.rename(src, dest, err => {
2467 if (!err) return callback()
2468
2469 if (err.code === 'ENOTEMPTY' || err.code === 'EEXIST') {
2470 remove$1(dest, err => {
2471 if (err) return callback(err)
2472 options.overwrite = false; // just overwriteed it, no need to do it again
2473 move(src, dest, options, callback);
2474 });
2475 return
2476 }
2477
2478 // weird Windows shit
2479 if (err.code === 'EPERM') {
2480 setTimeout(() => {
2481 remove$1(dest, err => {
2482 if (err) return callback(err)
2483 options.overwrite = false;
2484 move(src, dest, options, callback);
2485 });
2486 }, 200);
2487 return
2488 }
2489
2490 if (err.code !== 'EXDEV') return callback(err)
2491 moveAcrossDevice(src, dest, overwrite, callback);
2492 });
2493 } else {
2494 gracefulFs.link(src, dest, err => {
2495 if (err) {
2496 if (err.code === 'EXDEV' || err.code === 'EISDIR' || err.code === 'EPERM' || err.code === 'ENOTSUP') {
2497 return moveAcrossDevice(src, dest, overwrite, callback)
2498 }
2499 return callback(err)
2500 }
2501 return gracefulFs.unlink(src, callback)
2502 });
2503 }
2504 }
2505 }
2506
2507 function moveAcrossDevice (src, dest, overwrite, callback) {
2508 gracefulFs.stat(src, (err, stat) => {
2509 if (err) return callback(err)
2510
2511 if (stat.isDirectory()) {
2512 moveDirAcrossDevice(src, dest, overwrite, callback);
2513 } else {
2514 moveFileAcrossDevice(src, dest, overwrite, callback);
2515 }
2516 });
2517 }
2518
2519 function moveFileAcrossDevice (src, dest, overwrite, callback) {
2520 const flags = overwrite ? 'w' : 'wx';
2521 const ins = gracefulFs.createReadStream(src);
2522 const outs = gracefulFs.createWriteStream(dest, { flags });
2523
2524 ins.on('error', err => {
2525 ins.destroy();
2526 outs.destroy();
2527 outs.removeListener('close', onClose);
2528
2529 // may want to create a directory but `out` line above
2530 // creates an empty file for us: See #108
2531 // don't care about error here
2532 gracefulFs.unlink(dest, () => {
2533 // note: `err` here is from the input stream errror
2534 if (err.code === 'EISDIR' || err.code === 'EPERM') {
2535 moveDirAcrossDevice(src, dest, overwrite, callback);
2536 } else {
2537 callback(err);
2538 }
2539 });
2540 });
2541
2542 outs.on('error', err => {
2543 ins.destroy();
2544 outs.destroy();
2545 outs.removeListener('close', onClose);
2546 callback(err);
2547 });
2548
2549 outs.once('close', onClose);
2550 ins.pipe(outs);
2551
2552 function onClose () {
2553 gracefulFs.unlink(src, callback);
2554 }
2555 }
2556
2557 function moveDirAcrossDevice (src, dest, overwrite, callback) {
2558 const options = {
2559 overwrite: false
2560 };
2561
2562 if (overwrite) {
2563 remove$1(dest, err => {
2564 if (err) return callback(err)
2565 startNcp();
2566 });
2567 } else {
2568 startNcp();
2569 }
2570
2571 function startNcp () {
2572 ncp_1(src, dest, options, err => {
2573 if (err) return callback(err)
2574 remove$1(src, callback);
2575 });
2576 }
2577 }
2578
2579 // return true if dest is a subdir of src, otherwise false.
2580 // extract dest base dir and check if that is the same as src basename
2581 function isSrcSubdir (src, dest, cb) {
2582 gracefulFs.stat(src, (err, st) => {
2583 if (err) return cb(err)
2584 if (st.isDirectory()) {
2585 const baseDir = dest.split(path__default.dirname(src) + path__default.sep)[1];
2586 if (baseDir) {
2587 const destBasename = baseDir.split(path__default.sep)[0];
2588 if (destBasename) return cb(null, src !== dest && dest.indexOf(src) > -1 && destBasename === path__default.basename(src))
2589 return cb(null, false)
2590 }
2591 return cb(null, false)
2592 }
2593 return cb(null, false)
2594 });
2595 }
2596
2597 var move_1 = {
2598 move: u$6(move)
2599 };
2600
2601 const copySync$2 = copySync$1.copySync;
2602 const removeSync = remove.removeSync;
2603 const mkdirpSync = mkdirs_1$1.mkdirsSync;
2604
2605
2606 function moveSync (src, dest, options) {
2607 options = options || {};
2608 const overwrite = options.overwrite || options.clobber || false;
2609
2610 src = path__default.resolve(src);
2611 dest = path__default.resolve(dest);
2612
2613 if (src === dest) return gracefulFs.accessSync(src)
2614
2615 if (isSrcSubdir$1(src, dest)) throw new Error(`Cannot move '${src}' into itself '${dest}'.`)
2616
2617 mkdirpSync(path__default.dirname(dest));
2618 tryRenameSync();
2619
2620 function tryRenameSync () {
2621 if (overwrite) {
2622 try {
2623 return gracefulFs.renameSync(src, dest)
2624 } catch (err) {
2625 if (err.code === 'ENOTEMPTY' || err.code === 'EEXIST' || err.code === 'EPERM') {
2626 removeSync(dest);
2627 options.overwrite = false; // just overwriteed it, no need to do it again
2628 return moveSync(src, dest, options)
2629 }
2630
2631 if (err.code !== 'EXDEV') throw err
2632 return moveSyncAcrossDevice(src, dest, overwrite)
2633 }
2634 } else {
2635 try {
2636 gracefulFs.linkSync(src, dest);
2637 return gracefulFs.unlinkSync(src)
2638 } catch (err) {
2639 if (err.code === 'EXDEV' || err.code === 'EISDIR' || err.code === 'EPERM' || err.code === 'ENOTSUP') {
2640 return moveSyncAcrossDevice(src, dest, overwrite)
2641 }
2642 throw err
2643 }
2644 }
2645 }
2646 }
2647
2648 function moveSyncAcrossDevice (src, dest, overwrite) {
2649 const stat = gracefulFs.statSync(src);
2650
2651 if (stat.isDirectory()) {
2652 return moveDirSyncAcrossDevice(src, dest, overwrite)
2653 } else {
2654 return moveFileSyncAcrossDevice(src, dest, overwrite)
2655 }
2656 }
2657
2658 function moveFileSyncAcrossDevice (src, dest, overwrite) {
2659 const BUF_LENGTH = 64 * 1024;
2660 const _buff = buffer(BUF_LENGTH);
2661
2662 const flags = overwrite ? 'w' : 'wx';
2663
2664 const fdr = gracefulFs.openSync(src, 'r');
2665 const stat = gracefulFs.fstatSync(fdr);
2666 const fdw = gracefulFs.openSync(dest, flags, stat.mode);
2667 let bytesRead = 1;
2668 let pos = 0;
2669
2670 while (bytesRead > 0) {
2671 bytesRead = gracefulFs.readSync(fdr, _buff, 0, BUF_LENGTH, pos);
2672 gracefulFs.writeSync(fdw, _buff, 0, bytesRead);
2673 pos += bytesRead;
2674 }
2675
2676 gracefulFs.closeSync(fdr);
2677 gracefulFs.closeSync(fdw);
2678 return gracefulFs.unlinkSync(src)
2679 }
2680
2681 function moveDirSyncAcrossDevice (src, dest, overwrite) {
2682 const options = {
2683 overwrite: false
2684 };
2685
2686 if (overwrite) {
2687 removeSync(dest);
2688 tryCopySync();
2689 } else {
2690 tryCopySync();
2691 }
2692
2693 function tryCopySync () {
2694 copySync$2(src, dest, options);
2695 return removeSync(src)
2696 }
2697 }
2698
2699 // return true if dest is a subdir of src, otherwise false.
2700 // extract dest base dir and check if that is the same as src basename
2701 function isSrcSubdir$1 (src, dest) {
2702 try {
2703 return gracefulFs.statSync(src).isDirectory() &&
2704 src !== dest &&
2705 dest.indexOf(src) > -1 &&
2706 dest.split(path__default.dirname(src) + path__default.sep)[1].split(path__default.sep)[0] === path__default.basename(src)
2707 } catch (e) {
2708 return false
2709 }
2710 }
2711
2712 var moveSync_1 = {
2713 moveSync
2714 };
2715
2716 const u$7 = universalify.fromCallback;
2717
2718
2719
2720
2721
2722 const emptyDir = u$7(function emptyDir (dir, callback) {
2723 callback = callback || function () {};
2724 fs$2__default.readdir(dir, (err, items) => {
2725 if (err) return mkdirs_1$1.mkdirs(dir, callback)
2726
2727 items = items.map(item => path__default.join(dir, item));
2728
2729 deleteItem();
2730
2731 function deleteItem () {
2732 const item = items.pop();
2733 if (!item) return callback()
2734 remove.remove(item, err => {
2735 if (err) return callback(err)
2736 deleteItem();
2737 });
2738 }
2739 });
2740 });
2741
2742 function emptyDirSync (dir) {
2743 let items;
2744 try {
2745 items = fs$2__default.readdirSync(dir);
2746 } catch (err) {
2747 return mkdirs_1$1.mkdirsSync(dir)
2748 }
2749
2750 items.forEach(item => {
2751 item = path__default.join(dir, item);
2752 remove.removeSync(item);
2753 });
2754 }
2755
2756 var empty = {
2757 emptyDirSync,
2758 emptydirSync: emptyDirSync,
2759 emptyDir,
2760 emptydir: emptyDir
2761 };
2762
2763 const u$8 = universalify.fromCallback;
2764
2765
2766
2767 const pathExists$3 = pathExists_1.pathExists;
2768
2769 function createFile (file, callback) {
2770 function makeFile () {
2771 gracefulFs.writeFile(file, '', err => {
2772 if (err) return callback(err)
2773 callback();
2774 });
2775 }
2776
2777 gracefulFs.stat(file, (err, stats) => { // eslint-disable-line handle-callback-err
2778 if (!err && stats.isFile()) return callback()
2779 const dir = path__default.dirname(file);
2780 pathExists$3(dir, (err, dirExists) => {
2781 if (err) return callback(err)
2782 if (dirExists) return makeFile()
2783 mkdirs_1$1.mkdirs(dir, err => {
2784 if (err) return callback(err)
2785 makeFile();
2786 });
2787 });
2788 });
2789 }
2790
2791 function createFileSync (file) {
2792 let stats;
2793 try {
2794 stats = gracefulFs.statSync(file);
2795 } catch (e) {}
2796 if (stats && stats.isFile()) return
2797
2798 const dir = path__default.dirname(file);
2799 if (!gracefulFs.existsSync(dir)) {
2800 mkdirs_1$1.mkdirsSync(dir);
2801 }
2802
2803 gracefulFs.writeFileSync(file, '');
2804 }
2805
2806 var file = {
2807 createFile: u$8(createFile),
2808 createFileSync
2809 };
2810
2811 const u$9 = universalify.fromCallback;
2812
2813
2814
2815 const pathExists$4 = pathExists_1.pathExists;
2816
2817 function createLink (srcpath, dstpath, callback) {
2818 function makeLink (srcpath, dstpath) {
2819 gracefulFs.link(srcpath, dstpath, err => {
2820 if (err) return callback(err)
2821 callback(null);
2822 });
2823 }
2824
2825 pathExists$4(dstpath, (err, destinationExists) => {
2826 if (err) return callback(err)
2827 if (destinationExists) return callback(null)
2828 gracefulFs.lstat(srcpath, (err, stat) => {
2829 if (err) {
2830 err.message = err.message.replace('lstat', 'ensureLink');
2831 return callback(err)
2832 }
2833
2834 const dir = path__default.dirname(dstpath);
2835 pathExists$4(dir, (err, dirExists) => {
2836 if (err) return callback(err)
2837 if (dirExists) return makeLink(srcpath, dstpath)
2838 mkdirs_1$1.mkdirs(dir, err => {
2839 if (err) return callback(err)
2840 makeLink(srcpath, dstpath);
2841 });
2842 });
2843 });
2844 });
2845 }
2846
2847 function createLinkSync (srcpath, dstpath, callback) {
2848 const destinationExists = gracefulFs.existsSync(dstpath);
2849 if (destinationExists) return undefined
2850
2851 try {
2852 gracefulFs.lstatSync(srcpath);
2853 } catch (err) {
2854 err.message = err.message.replace('lstat', 'ensureLink');
2855 throw err
2856 }
2857
2858 const dir = path__default.dirname(dstpath);
2859 const dirExists = gracefulFs.existsSync(dir);
2860 if (dirExists) return gracefulFs.linkSync(srcpath, dstpath)
2861 mkdirs_1$1.mkdirsSync(dir);
2862
2863 return gracefulFs.linkSync(srcpath, dstpath)
2864 }
2865
2866 var link = {
2867 createLink: u$9(createLink),
2868 createLinkSync
2869 };
2870
2871 const pathExists$5 = pathExists_1.pathExists;
2872
2873 /**
2874 * Function that returns two types of paths, one relative to symlink, and one
2875 * relative to the current working directory. Checks if path is absolute or
2876 * relative. If the path is relative, this function checks if the path is
2877 * relative to symlink or relative to current working directory. This is an
2878 * initiative to find a smarter `srcpath` to supply when building symlinks.
2879 * This allows you to determine which path to use out of one of three possible
2880 * types of source paths. The first is an absolute path. This is detected by
2881 * `path.isAbsolute()`. When an absolute path is provided, it is checked to
2882 * see if it exists. If it does it's used, if not an error is returned
2883 * (callback)/ thrown (sync). The other two options for `srcpath` are a
2884 * relative url. By default Node's `fs.symlink` works by creating a symlink
2885 * using `dstpath` and expects the `srcpath` to be relative to the newly
2886 * created symlink. If you provide a `srcpath` that does not exist on the file
2887 * system it results in a broken symlink. To minimize this, the function
2888 * checks to see if the 'relative to symlink' source file exists, and if it
2889 * does it will use it. If it does not, it checks if there's a file that
2890 * exists that is relative to the current working directory, if does its used.
2891 * This preserves the expectations of the original fs.symlink spec and adds
2892 * the ability to pass in `relative to current working direcotry` paths.
2893 */
2894
2895 function symlinkPaths (srcpath, dstpath, callback) {
2896 if (path__default.isAbsolute(srcpath)) {
2897 return gracefulFs.lstat(srcpath, (err, stat) => {
2898 if (err) {
2899 err.message = err.message.replace('lstat', 'ensureSymlink');
2900 return callback(err)
2901 }
2902 return callback(null, {
2903 'toCwd': srcpath,
2904 'toDst': srcpath
2905 })
2906 })
2907 } else {
2908 const dstdir = path__default.dirname(dstpath);
2909 const relativeToDst = path__default.join(dstdir, srcpath);
2910 return pathExists$5(relativeToDst, (err, exists) => {
2911 if (err) return callback(err)
2912 if (exists) {
2913 return callback(null, {
2914 'toCwd': relativeToDst,
2915 'toDst': srcpath
2916 })
2917 } else {
2918 return gracefulFs.lstat(srcpath, (err, stat) => {
2919 if (err) {
2920 err.message = err.message.replace('lstat', 'ensureSymlink');
2921 return callback(err)
2922 }
2923 return callback(null, {
2924 'toCwd': srcpath,
2925 'toDst': path__default.relative(dstdir, srcpath)
2926 })
2927 })
2928 }
2929 })
2930 }
2931 }
2932
2933 function symlinkPathsSync (srcpath, dstpath) {
2934 let exists;
2935 if (path__default.isAbsolute(srcpath)) {
2936 exists = gracefulFs.existsSync(srcpath);
2937 if (!exists) throw new Error('absolute srcpath does not exist')
2938 return {
2939 'toCwd': srcpath,
2940 'toDst': srcpath
2941 }
2942 } else {
2943 const dstdir = path__default.dirname(dstpath);
2944 const relativeToDst = path__default.join(dstdir, srcpath);
2945 exists = gracefulFs.existsSync(relativeToDst);
2946 if (exists) {
2947 return {
2948 'toCwd': relativeToDst,
2949 'toDst': srcpath
2950 }
2951 } else {
2952 exists = gracefulFs.existsSync(srcpath);
2953 if (!exists) throw new Error('relative srcpath does not exist')
2954 return {
2955 'toCwd': srcpath,
2956 'toDst': path__default.relative(dstdir, srcpath)
2957 }
2958 }
2959 }
2960 }
2961
2962 var symlinkPaths_1 = {
2963 symlinkPaths,
2964 symlinkPathsSync
2965 };
2966
2967 function symlinkType (srcpath, type, callback) {
2968 callback = (typeof type === 'function') ? type : callback;
2969 type = (typeof type === 'function') ? false : type;
2970 if (type) return callback(null, type)
2971 gracefulFs.lstat(srcpath, (err, stats) => {
2972 if (err) return callback(null, 'file')
2973 type = (stats && stats.isDirectory()) ? 'dir' : 'file';
2974 callback(null, type);
2975 });
2976 }
2977
2978 function symlinkTypeSync (srcpath, type) {
2979 let stats;
2980
2981 if (type) return type
2982 try {
2983 stats = gracefulFs.lstatSync(srcpath);
2984 } catch (e) {
2985 return 'file'
2986 }
2987 return (stats && stats.isDirectory()) ? 'dir' : 'file'
2988 }
2989
2990 var symlinkType_1 = {
2991 symlinkType,
2992 symlinkTypeSync
2993 };
2994
2995 const u$a = universalify.fromCallback;
2996
2997
2998
2999 const mkdirs$2 = mkdirs_1$1.mkdirs;
3000 const mkdirsSync$1 = mkdirs_1$1.mkdirsSync;
3001
3002
3003 const symlinkPaths$1 = symlinkPaths_1.symlinkPaths;
3004 const symlinkPathsSync$1 = symlinkPaths_1.symlinkPathsSync;
3005
3006
3007 const symlinkType$1 = symlinkType_1.symlinkType;
3008 const symlinkTypeSync$1 = symlinkType_1.symlinkTypeSync;
3009
3010 const pathExists$6 = pathExists_1.pathExists;
3011
3012 function createSymlink (srcpath, dstpath, type, callback) {
3013 callback = (typeof type === 'function') ? type : callback;
3014 type = (typeof type === 'function') ? false : type;
3015
3016 pathExists$6(dstpath, (err, destinationExists) => {
3017 if (err) return callback(err)
3018 if (destinationExists) return callback(null)
3019 symlinkPaths$1(srcpath, dstpath, (err, relative) => {
3020 if (err) return callback(err)
3021 srcpath = relative.toDst;
3022 symlinkType$1(relative.toCwd, type, (err, type) => {
3023 if (err) return callback(err)
3024 const dir = path__default.dirname(dstpath);
3025 pathExists$6(dir, (err, dirExists) => {
3026 if (err) return callback(err)
3027 if (dirExists) return gracefulFs.symlink(srcpath, dstpath, type, callback)
3028 mkdirs$2(dir, err => {
3029 if (err) return callback(err)
3030 gracefulFs.symlink(srcpath, dstpath, type, callback);
3031 });
3032 });
3033 });
3034 });
3035 });
3036 }
3037
3038 function createSymlinkSync (srcpath, dstpath, type, callback) {
3039 type = (typeof type === 'function') ? false : type;
3040
3041 const destinationExists = gracefulFs.existsSync(dstpath);
3042 if (destinationExists) return undefined
3043
3044 const relative = symlinkPathsSync$1(srcpath, dstpath);
3045 srcpath = relative.toDst;
3046 type = symlinkTypeSync$1(relative.toCwd, type);
3047 const dir = path__default.dirname(dstpath);
3048 const exists = gracefulFs.existsSync(dir);
3049 if (exists) return gracefulFs.symlinkSync(srcpath, dstpath, type)
3050 mkdirsSync$1(dir);
3051 return gracefulFs.symlinkSync(srcpath, dstpath, type)
3052 }
3053
3054 var symlink = {
3055 createSymlink: u$a(createSymlink),
3056 createSymlinkSync
3057 };
3058
3059 var ensure = {
3060 // file
3061 createFile: file.createFile,
3062 createFileSync: file.createFileSync,
3063 ensureFile: file.createFile,
3064 ensureFileSync: file.createFileSync,
3065 // link
3066 createLink: link.createLink,
3067 createLinkSync: link.createLinkSync,
3068 ensureLink: link.createLink,
3069 ensureLinkSync: link.createLinkSync,
3070 // symlink
3071 createSymlink: symlink.createSymlink,
3072 createSymlinkSync: symlink.createSymlinkSync,
3073 ensureSymlink: symlink.createSymlink,
3074 ensureSymlinkSync: symlink.createSymlinkSync
3075 };
3076
3077 const u$b = universalify.fromCallback;
3078
3079
3080
3081 const pathExists$7 = pathExists_1.pathExists;
3082
3083 function outputFile (file, data, encoding, callback) {
3084 if (typeof encoding === 'function') {
3085 callback = encoding;
3086 encoding = 'utf8';
3087 }
3088
3089 const dir = path__default.dirname(file);
3090 pathExists$7(dir, (err, itDoes) => {
3091 if (err) return callback(err)
3092 if (itDoes) return gracefulFs.writeFile(file, data, encoding, callback)
3093
3094 mkdirs_1$1.mkdirs(dir, err => {
3095 if (err) return callback(err)
3096
3097 gracefulFs.writeFile(file, data, encoding, callback);
3098 });
3099 });
3100 }
3101
3102 function outputFileSync (file, data, encoding) {
3103 const dir = path__default.dirname(file);
3104 if (gracefulFs.existsSync(dir)) {
3105 return gracefulFs.writeFileSync.apply(gracefulFs, arguments)
3106 }
3107 mkdirs_1$1.mkdirsSync(dir);
3108 gracefulFs.writeFileSync.apply(gracefulFs, arguments);
3109 }
3110
3111 var output = {
3112 outputFile: u$b(outputFile),
3113 outputFileSync
3114 };
3115
3116 const fs$1 = {};
3117
3118 // Export graceful-fs:
3119 assign_1(fs$1, fs_1);
3120 // Export extra methods:
3121 assign_1(fs$1, copy$1);
3122 assign_1(fs$1, copySync$1);
3123 assign_1(fs$1, mkdirs_1$1);
3124 assign_1(fs$1, remove);
3125 assign_1(fs$1, json);
3126 assign_1(fs$1, move_1);
3127 assign_1(fs$1, moveSync_1);
3128 assign_1(fs$1, empty);
3129 assign_1(fs$1, ensure);
3130 assign_1(fs$1, output);
3131 assign_1(fs$1, pathExists_1);
3132
3133 var lib = fs$1;
3134
3135 var fsExtra = /*#__PURE__*/Object.freeze({
3136 __proto__: null,
3137 'default': lib,
3138 __moduleExports: lib
3139 });
3140
3141 /**
3142 * @license
3143 * Copyright Google LLC All Rights Reserved.
3144 *
3145 * Use of this source code is governed by an MIT-style license that can be
3146 * found in the LICENSE file at https://angular.io/license
3147 */
3148 /**
3149 * A wrapper around the Node.js file-system that supports path manipulation.
3150 */
3151 class NodeJSPathManipulation {
3152 pwd() {
3153 return this.normalize(process.cwd());
3154 }
3155 chdir(dir) {
3156 process.chdir(dir);
3157 }
3158 resolve(...paths) {
3159 return this.normalize(path.resolve(...paths));
3160 }
3161 dirname(file) {
3162 return this.normalize(path.dirname(file));
3163 }
3164 join(basePath, ...paths) {
3165 return this.normalize(path.join(basePath, ...paths));
3166 }
3167 isRoot(path) {
3168 return this.dirname(path) === this.normalize(path);
3169 }
3170 isRooted(path$1) {
3171 return path.isAbsolute(path$1);
3172 }
3173 relative(from, to) {
3174 return this.normalize(path.relative(from, to));
3175 }
3176 basename(filePath, extension) {
3177 return path.basename(filePath, extension);
3178 }
3179 extname(path$1) {
3180 return path.extname(path$1);
3181 }
3182 normalize(path) {
3183 // Convert backslashes to forward slashes
3184 return path.replace(/\\/g, '/');
3185 }
3186 }
3187 /**
3188 * A wrapper around the Node.js file-system that supports readonly operations and path manipulation.
3189 */
3190 class NodeJSReadonlyFileSystem extends NodeJSPathManipulation {
3191 constructor() {
3192 super(...arguments);
3193 this._caseSensitive = undefined;
3194 }
3195 isCaseSensitive() {
3196 if (this._caseSensitive === undefined) {
3197 // Note the use of the real file-system is intentional:
3198 // `this.exists()` relies upon `isCaseSensitive()` so that would cause an infinite recursion.
3199 this._caseSensitive = !fs$2.existsSync(this.normalize(toggleCase(__filename)));
3200 }
3201 return this._caseSensitive;
3202 }
3203 exists(path) {
3204 return fs$2.existsSync(path);
3205 }
3206 readFile(path) {
3207 return fs$2.readFileSync(path, 'utf8');
3208 }
3209 readFileBuffer(path) {
3210 return fs$2.readFileSync(path);
3211 }
3212 readdir(path) {
3213 return fs$2.readdirSync(path);
3214 }
3215 lstat(path) {
3216 return fs$2.lstatSync(path);
3217 }
3218 stat(path) {
3219 return fs$2.statSync(path);
3220 }
3221 realpath(path) {
3222 return this.resolve(fs$2.realpathSync(path));
3223 }
3224 getDefaultLibLocation() {
3225 return this.resolve(require.resolve('typescript'), '..');
3226 }
3227 }
3228 /**
3229 * A wrapper around the Node.js file-system (i.e. the `fs` package).
3230 */
3231 class NodeJSFileSystem extends NodeJSReadonlyFileSystem {
3232 writeFile(path, data, exclusive = false) {
3233 fs$2.writeFileSync(path, data, exclusive ? { flag: 'wx' } : undefined);
3234 }
3235 removeFile(path) {
3236 fs$2.unlinkSync(path);
3237 }
3238 symlink(target, path) {
3239 fs$2.symlinkSync(target, path);
3240 }
3241 copyFile(from, to) {
3242 fs$2.copyFileSync(from, to);
3243 }
3244 moveFile(from, to) {
3245 fs$2.renameSync(from, to);
3246 }
3247 ensureDir(path) {
3248 const parents = [];
3249 while (!this.isRoot(path) && !this.exists(path)) {
3250 parents.push(path);
3251 path = this.dirname(path);
3252 }
3253 while (parents.length) {
3254 this.safeMkdir(parents.pop());
3255 }
3256 }
3257 removeDeep(path) {
3258 undefined(path);
3259 }
3260 safeMkdir(path) {
3261 try {
3262 fs$2.mkdirSync(path);
3263 }
3264 catch (err) {
3265 // Ignore the error, if the path already exists and points to a directory.
3266 // Re-throw otherwise.
3267 if (!this.exists(path) || !this.stat(path).isDirectory()) {
3268 throw err;
3269 }
3270 }
3271 }
3272 }
3273 /**
3274 * Toggle the case of each character in a string.
3275 */
3276 function toggleCase(str) {
3277 return str.replace(/\w/g, ch => ch.toUpperCase() === ch ? ch.toLowerCase() : ch.toUpperCase());
3278 }
3279
3280 /**
3281 * @license
3282 * Copyright Google LLC All Rights Reserved.
3283 *
3284 * Use of this source code is governed by an MIT-style license that can be
3285 * found in the LICENSE file at https://angular.io/license
3286 */
3287 var TagContentType;
3288 (function (TagContentType) {
3289 TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
3290 TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
3291 TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
3292 })(TagContentType || (TagContentType = {}));
3293 function splitNsName(elementName) {
3294 if (elementName[0] != ':') {
3295 return [null, elementName];
3296 }
3297 const colonIndex = elementName.indexOf(':', 1);
3298 if (colonIndex == -1) {
3299 throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
3300 }
3301 return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
3302 }
3303 // `<ng-container>` tags work the same regardless the namespace
3304 function isNgContainer(tagName) {
3305 return splitNsName(tagName)[1] === 'ng-container';
3306 }
3307 // `<ng-content>` tags work the same regardless the namespace
3308 function isNgContent(tagName) {
3309 return splitNsName(tagName)[1] === 'ng-content';
3310 }
3311 // `<ng-template>` tags work the same regardless the namespace
3312 function isNgTemplate(tagName) {
3313 return splitNsName(tagName)[1] === 'ng-template';
3314 }
3315 function getNsPrefix(fullName) {
3316 return fullName === null ? null : splitNsName(fullName)[0];
3317 }
3318 function mergeNsAndName(prefix, localName) {
3319 return prefix ? `:${prefix}:${localName}` : localName;
3320 }
3321 // see https://www.w3.org/TR/html51/syntax.html#named-character-references
3322 // see https://html.spec.whatwg.org/multipage/entities.json
3323 // This list is not exhaustive to keep the compiler footprint low.
3324 // The `&#123;` / `&#x1ab;` syntax should be used when the named character reference does not
3325 // exist.
3326 const NAMED_ENTITIES = {
3327 'Aacute': '\u00C1',
3328 'aacute': '\u00E1',
3329 'Acirc': '\u00C2',
3330 'acirc': '\u00E2',
3331 'acute': '\u00B4',
3332 'AElig': '\u00C6',
3333 'aelig': '\u00E6',
3334 'Agrave': '\u00C0',
3335 'agrave': '\u00E0',
3336 'alefsym': '\u2135',
3337 'Alpha': '\u0391',
3338 'alpha': '\u03B1',
3339 'amp': '&',
3340 'and': '\u2227',
3341 'ang': '\u2220',
3342 'apos': '\u0027',
3343 'Aring': '\u00C5',
3344 'aring': '\u00E5',
3345 'asymp': '\u2248',
3346 'Atilde': '\u00C3',
3347 'atilde': '\u00E3',
3348 'Auml': '\u00C4',
3349 'auml': '\u00E4',
3350 'bdquo': '\u201E',
3351 'Beta': '\u0392',
3352 'beta': '\u03B2',
3353 'brvbar': '\u00A6',
3354 'bull': '\u2022',
3355 'cap': '\u2229',
3356 'Ccedil': '\u00C7',
3357 'ccedil': '\u00E7',
3358 'cedil': '\u00B8',
3359 'cent': '\u00A2',
3360 'Chi': '\u03A7',
3361 'chi': '\u03C7',
3362 'circ': '\u02C6',
3363 'clubs': '\u2663',
3364 'cong': '\u2245',
3365 'copy': '\u00A9',
3366 'crarr': '\u21B5',
3367 'cup': '\u222A',
3368 'curren': '\u00A4',
3369 'dagger': '\u2020',
3370 'Dagger': '\u2021',
3371 'darr': '\u2193',
3372 'dArr': '\u21D3',
3373 'deg': '\u00B0',
3374 'Delta': '\u0394',
3375 'delta': '\u03B4',
3376 'diams': '\u2666',
3377 'divide': '\u00F7',
3378 'Eacute': '\u00C9',
3379 'eacute': '\u00E9',
3380 'Ecirc': '\u00CA',
3381 'ecirc': '\u00EA',
3382 'Egrave': '\u00C8',
3383 'egrave': '\u00E8',
3384 'empty': '\u2205',
3385 'emsp': '\u2003',
3386 'ensp': '\u2002',
3387 'Epsilon': '\u0395',
3388 'epsilon': '\u03B5',
3389 'equiv': '\u2261',
3390 'Eta': '\u0397',
3391 'eta': '\u03B7',
3392 'ETH': '\u00D0',
3393 'eth': '\u00F0',
3394 'Euml': '\u00CB',
3395 'euml': '\u00EB',
3396 'euro': '\u20AC',
3397 'exist': '\u2203',
3398 'fnof': '\u0192',
3399 'forall': '\u2200',
3400 'frac12': '\u00BD',
3401 'frac14': '\u00BC',
3402 'frac34': '\u00BE',
3403 'frasl': '\u2044',
3404 'Gamma': '\u0393',
3405 'gamma': '\u03B3',
3406 'ge': '\u2265',
3407 'gt': '>',
3408 'harr': '\u2194',
3409 'hArr': '\u21D4',
3410 'hearts': '\u2665',
3411 'hellip': '\u2026',
3412 'Iacute': '\u00CD',
3413 'iacute': '\u00ED',
3414 'Icirc': '\u00CE',
3415 'icirc': '\u00EE',
3416 'iexcl': '\u00A1',
3417 'Igrave': '\u00CC',
3418 'igrave': '\u00EC',
3419 'image': '\u2111',
3420 'infin': '\u221E',
3421 'int': '\u222B',
3422 'Iota': '\u0399',
3423 'iota': '\u03B9',
3424 'iquest': '\u00BF',
3425 'isin': '\u2208',
3426 'Iuml': '\u00CF',
3427 'iuml': '\u00EF',
3428 'Kappa': '\u039A',
3429 'kappa': '\u03BA',
3430 'Lambda': '\u039B',
3431 'lambda': '\u03BB',
3432 'lang': '\u27E8',
3433 'laquo': '\u00AB',
3434 'larr': '\u2190',
3435 'lArr': '\u21D0',
3436 'lceil': '\u2308',
3437 'ldquo': '\u201C',
3438 'le': '\u2264',
3439 'lfloor': '\u230A',
3440 'lowast': '\u2217',
3441 'loz': '\u25CA',
3442 'lrm': '\u200E',
3443 'lsaquo': '\u2039',
3444 'lsquo': '\u2018',
3445 'lt': '<',
3446 'macr': '\u00AF',
3447 'mdash': '\u2014',
3448 'micro': '\u00B5',
3449 'middot': '\u00B7',
3450 'minus': '\u2212',
3451 'Mu': '\u039C',
3452 'mu': '\u03BC',
3453 'nabla': '\u2207',
3454 'nbsp': '\u00A0',
3455 'ndash': '\u2013',
3456 'ne': '\u2260',
3457 'ni': '\u220B',
3458 'not': '\u00AC',
3459 'notin': '\u2209',
3460 'nsub': '\u2284',
3461 'Ntilde': '\u00D1',
3462 'ntilde': '\u00F1',
3463 'Nu': '\u039D',
3464 'nu': '\u03BD',
3465 'Oacute': '\u00D3',
3466 'oacute': '\u00F3',
3467 'Ocirc': '\u00D4',
3468 'ocirc': '\u00F4',
3469 'OElig': '\u0152',
3470 'oelig': '\u0153',
3471 'Ograve': '\u00D2',
3472 'ograve': '\u00F2',
3473 'oline': '\u203E',
3474 'Omega': '\u03A9',
3475 'omega': '\u03C9',
3476 'Omicron': '\u039F',
3477 'omicron': '\u03BF',
3478 'oplus': '\u2295',
3479 'or': '\u2228',
3480 'ordf': '\u00AA',
3481 'ordm': '\u00BA',
3482 'Oslash': '\u00D8',
3483 'oslash': '\u00F8',
3484 'Otilde': '\u00D5',
3485 'otilde': '\u00F5',
3486 'otimes': '\u2297',
3487 'Ouml': '\u00D6',
3488 'ouml': '\u00F6',
3489 'para': '\u00B6',
3490 'permil': '\u2030',
3491 'perp': '\u22A5',
3492 'Phi': '\u03A6',
3493 'phi': '\u03C6',
3494 'Pi': '\u03A0',
3495 'pi': '\u03C0',
3496 'piv': '\u03D6',
3497 'plusmn': '\u00B1',
3498 'pound': '\u00A3',
3499 'prime': '\u2032',
3500 'Prime': '\u2033',
3501 'prod': '\u220F',
3502 'prop': '\u221D',
3503 'Psi': '\u03A8',
3504 'psi': '\u03C8',
3505 'quot': '\u0022',
3506 'radic': '\u221A',
3507 'rang': '\u27E9',
3508 'raquo': '\u00BB',
3509 'rarr': '\u2192',
3510 'rArr': '\u21D2',
3511 'rceil': '\u2309',
3512 'rdquo': '\u201D',
3513 'real': '\u211C',
3514 'reg': '\u00AE',
3515 'rfloor': '\u230B',
3516 'Rho': '\u03A1',
3517 'rho': '\u03C1',
3518 'rlm': '\u200F',
3519 'rsaquo': '\u203A',
3520 'rsquo': '\u2019',
3521 'sbquo': '\u201A',
3522 'Scaron': '\u0160',
3523 'scaron': '\u0161',
3524 'sdot': '\u22C5',
3525 'sect': '\u00A7',
3526 'shy': '\u00AD',
3527 'Sigma': '\u03A3',
3528 'sigma': '\u03C3',
3529 'sigmaf': '\u03C2',
3530 'sim': '\u223C',
3531 'spades': '\u2660',
3532 'sub': '\u2282',
3533 'sube': '\u2286',
3534 'sum': '\u2211',
3535 'sup': '\u2283',
3536 'sup1': '\u00B9',
3537 'sup2': '\u00B2',
3538 'sup3': '\u00B3',
3539 'supe': '\u2287',
3540 'szlig': '\u00DF',
3541 'Tau': '\u03A4',
3542 'tau': '\u03C4',
3543 'there4': '\u2234',
3544 'Theta': '\u0398',
3545 'theta': '\u03B8',
3546 'thetasym': '\u03D1',
3547 'thinsp': '\u2009',
3548 'THORN': '\u00DE',
3549 'thorn': '\u00FE',
3550 'tilde': '\u02DC',
3551 'times': '\u00D7',
3552 'trade': '\u2122',
3553 'Uacute': '\u00DA',
3554 'uacute': '\u00FA',
3555 'uarr': '\u2191',
3556 'uArr': '\u21D1',
3557 'Ucirc': '\u00DB',
3558 'ucirc': '\u00FB',
3559 'Ugrave': '\u00D9',
3560 'ugrave': '\u00F9',
3561 'uml': '\u00A8',
3562 'upsih': '\u03D2',
3563 'Upsilon': '\u03A5',
3564 'upsilon': '\u03C5',
3565 'Uuml': '\u00DC',
3566 'uuml': '\u00FC',
3567 'weierp': '\u2118',
3568 'Xi': '\u039E',
3569 'xi': '\u03BE',
3570 'Yacute': '\u00DD',
3571 'yacute': '\u00FD',
3572 'yen': '\u00A5',
3573 'yuml': '\u00FF',
3574 'Yuml': '\u0178',
3575 'Zeta': '\u0396',
3576 'zeta': '\u03B6',
3577 'zwj': '\u200D',
3578 'zwnj': '\u200C',
3579 };
3580 // The &ngsp; pseudo-entity is denoting a space. see:
3581 // https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart
3582 const NGSP_UNICODE = '\uE500';
3583 NAMED_ENTITIES['ngsp'] = NGSP_UNICODE;
3584
3585 /**
3586 * @license
3587 * Copyright Google LLC All Rights Reserved.
3588 *
3589 * Use of this source code is governed by an MIT-style license that can be
3590 * found in the LICENSE file at https://angular.io/license
3591 */
3592 class HtmlTagDefinition {
3593 constructor({ closedByChildren, implicitNamespacePrefix, contentType = TagContentType.PARSABLE_DATA, closedByParent = false, isVoid = false, ignoreFirstLf = false, preventNamespaceInheritance = false } = {}) {
3594 this.closedByChildren = {};
3595 this.closedByParent = false;
3596 this.canSelfClose = false;
3597 if (closedByChildren && closedByChildren.length > 0) {
3598 closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true);
3599 }
3600 this.isVoid = isVoid;
3601 this.closedByParent = closedByParent || isVoid;
3602 this.implicitNamespacePrefix = implicitNamespacePrefix || null;
3603 this.contentType = contentType;
3604 this.ignoreFirstLf = ignoreFirstLf;
3605 this.preventNamespaceInheritance = preventNamespaceInheritance;
3606 }
3607 isClosedByChild(name) {
3608 return this.isVoid || name.toLowerCase() in this.closedByChildren;
3609 }
3610 getContentType(prefix) {
3611 if (typeof this.contentType === 'object') {
3612 const overrideType = prefix == null ? undefined : this.contentType[prefix];
3613 return overrideType !== null && overrideType !== void 0 ? overrideType : this.contentType.default;
3614 }
3615 return this.contentType;
3616 }
3617 }
3618 let _DEFAULT_TAG_DEFINITION;
3619 // see https://www.w3.org/TR/html51/syntax.html#optional-tags
3620 // This implementation does not fully conform to the HTML5 spec.
3621 let TAG_DEFINITIONS;
3622 function getHtmlTagDefinition(tagName) {
3623 var _a, _b;
3624 if (!TAG_DEFINITIONS) {
3625 _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition();
3626 TAG_DEFINITIONS = {
3627 'base': new HtmlTagDefinition({ isVoid: true }),
3628 'meta': new HtmlTagDefinition({ isVoid: true }),
3629 'area': new HtmlTagDefinition({ isVoid: true }),
3630 'embed': new HtmlTagDefinition({ isVoid: true }),
3631 'link': new HtmlTagDefinition({ isVoid: true }),
3632 'img': new HtmlTagDefinition({ isVoid: true }),
3633 'input': new HtmlTagDefinition({ isVoid: true }),
3634 'param': new HtmlTagDefinition({ isVoid: true }),
3635 'hr': new HtmlTagDefinition({ isVoid: true }),
3636 'br': new HtmlTagDefinition({ isVoid: true }),
3637 'source': new HtmlTagDefinition({ isVoid: true }),
3638 'track': new HtmlTagDefinition({ isVoid: true }),
3639 'wbr': new HtmlTagDefinition({ isVoid: true }),
3640 'p': new HtmlTagDefinition({
3641 closedByChildren: [
3642 'address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset',
3643 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',
3644 'h6', 'header', 'hgroup', 'hr', 'main', 'nav', 'ol',
3645 'p', 'pre', 'section', 'table', 'ul'
3646 ],
3647 closedByParent: true
3648 }),
3649 'thead': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'] }),
3650 'tbody': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'], closedByParent: true }),
3651 'tfoot': new HtmlTagDefinition({ closedByChildren: ['tbody'], closedByParent: true }),
3652 'tr': new HtmlTagDefinition({ closedByChildren: ['tr'], closedByParent: true }),
3653 'td': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }),
3654 'th': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }),
3655 'col': new HtmlTagDefinition({ isVoid: true }),
3656 'svg': new HtmlTagDefinition({ implicitNamespacePrefix: 'svg' }),
3657 'foreignObject': new HtmlTagDefinition({
3658 // Usually the implicit namespace here would be redundant since it will be inherited from
3659 // the parent `svg`, but we have to do it for `foreignObject`, because the way the parser
3660 // works is that the parent node of an end tag is its own start tag which means that
3661 // the `preventNamespaceInheritance` on `foreignObject` would have it default to the
3662 // implicit namespace which is `html`, unless specified otherwise.
3663 implicitNamespacePrefix: 'svg',
3664 // We want to prevent children of foreignObject from inheriting its namespace, because
3665 // the point of the element is to allow nodes from other namespaces to be inserted.
3666 preventNamespaceInheritance: true,
3667 }),
3668 'math': new HtmlTagDefinition({ implicitNamespacePrefix: 'math' }),
3669 'li': new HtmlTagDefinition({ closedByChildren: ['li'], closedByParent: true }),
3670 'dt': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'] }),
3671 'dd': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'], closedByParent: true }),
3672 'rb': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
3673 'rt': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
3674 'rtc': new HtmlTagDefinition({ closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true }),
3675 'rp': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
3676 'optgroup': new HtmlTagDefinition({ closedByChildren: ['optgroup'], closedByParent: true }),
3677 'option': new HtmlTagDefinition({ closedByChildren: ['option', 'optgroup'], closedByParent: true }),
3678 'pre': new HtmlTagDefinition({ ignoreFirstLf: true }),
3679 'listing': new HtmlTagDefinition({ ignoreFirstLf: true }),
3680 'style': new HtmlTagDefinition({ contentType: TagContentType.RAW_TEXT }),
3681 'script': new HtmlTagDefinition({ contentType: TagContentType.RAW_TEXT }),
3682 'title': new HtmlTagDefinition({
3683 // The browser supports two separate `title` tags which have to use
3684 // a different content type: `HTMLTitleElement` and `SVGTitleElement`
3685 contentType: { default: TagContentType.ESCAPABLE_RAW_TEXT, svg: TagContentType.PARSABLE_DATA }
3686 }),
3687 'textarea': new HtmlTagDefinition({ contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true }),
3688 };
3689 }
3690 // We have to make both a case-sensitive and a case-insesitive lookup, because
3691 // HTML tag names are case insensitive, whereas some SVG tags are case sensitive.
3692 return (_b = (_a = TAG_DEFINITIONS[tagName]) !== null && _a !== void 0 ? _a : TAG_DEFINITIONS[tagName.toLowerCase()]) !== null && _b !== void 0 ? _b : _DEFAULT_TAG_DEFINITION;
3693 }
3694
3695 /**
3696 * @license
3697 * Copyright Google LLC All Rights Reserved.
3698 *
3699 * Use of this source code is governed by an MIT-style license that can be
3700 * found in the LICENSE file at https://angular.io/license
3701 */
3702 const _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' + // 1: ":not("
3703 '(([\\.\\#]?)[-\\w]+)|' + // 2: "tag"; 3: "."/"#";
3704 // "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range
3705 // 4: attribute; 5: attribute_string; 6: attribute_value
3706 '(?:\\[([-.\\w*]+)(?:=([\"\']?)([^\\]\"\']*)\\5)?\\])|' + // "[name]", "[name=value]",
3707 // "[name="value"]",
3708 // "[name='value']"
3709 '(\\))|' + // 7: ")"
3710 '(\\s*,\\s*)', // 8: ","
3711 'g');
3712 /**
3713 * A css selector contains an element name,
3714 * css classes and attribute/value pairs with the purpose
3715 * of selecting subsets out of them.
3716 */
3717 class CssSelector {
3718 constructor() {
3719 this.element = null;
3720 this.classNames = [];
3721 /**
3722 * The selectors are encoded in pairs where:
3723 * - even locations are attribute names
3724 * - odd locations are attribute values.
3725 *
3726 * Example:
3727 * Selector: `[key1=value1][key2]` would parse to:
3728 * ```
3729 * ['key1', 'value1', 'key2', '']
3730 * ```
3731 */
3732 this.attrs = [];
3733 this.notSelectors = [];
3734 }
3735 static parse(selector) {
3736 const results = [];
3737 const _addResult = (res, cssSel) => {
3738 if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 &&
3739 cssSel.attrs.length == 0) {
3740 cssSel.element = '*';
3741 }
3742 res.push(cssSel);
3743 };
3744 let cssSelector = new CssSelector();
3745 let match;
3746 let current = cssSelector;
3747 let inNot = false;
3748 _SELECTOR_REGEXP.lastIndex = 0;
3749 while (match = _SELECTOR_REGEXP.exec(selector)) {
3750 if (match[1 /* NOT */]) {
3751 if (inNot) {
3752 throw new Error('Nesting :not in a selector is not allowed');
3753 }
3754 inNot = true;
3755 current = new CssSelector();
3756 cssSelector.notSelectors.push(current);
3757 }
3758 const tag = match[2 /* TAG */];
3759 if (tag) {
3760 const prefix = match[3 /* PREFIX */];
3761 if (prefix === '#') {
3762 // #hash
3763 current.addAttribute('id', tag.substr(1));
3764 }
3765 else if (prefix === '.') {
3766 // Class
3767 current.addClassName(tag.substr(1));
3768 }
3769 else {
3770 // Element
3771 current.setElement(tag);
3772 }
3773 }
3774 const attribute = match[4 /* ATTRIBUTE */];
3775 if (attribute) {
3776 current.addAttribute(attribute, match[6 /* ATTRIBUTE_VALUE */]);
3777 }
3778 if (match[7 /* NOT_END */]) {
3779 inNot = false;
3780 current = cssSelector;
3781 }
3782 if (match[8 /* SEPARATOR */]) {
3783 if (inNot) {
3784 throw new Error('Multiple selectors in :not are not supported');
3785 }
3786 _addResult(results, cssSelector);
3787 cssSelector = current = new CssSelector();
3788 }
3789 }
3790 _addResult(results, cssSelector);
3791 return results;
3792 }
3793 isElementSelector() {
3794 return this.hasElementSelector() && this.classNames.length == 0 && this.attrs.length == 0 &&
3795 this.notSelectors.length === 0;
3796 }
3797 hasElementSelector() {
3798 return !!this.element;
3799 }
3800 setElement(element = null) {
3801 this.element = element;
3802 }
3803 /** Gets a template string for an element that matches the selector. */
3804 getMatchingElementTemplate() {
3805 const tagName = this.element || 'div';
3806 const classAttr = this.classNames.length > 0 ? ` class="${this.classNames.join(' ')}"` : '';
3807 let attrs = '';
3808 for (let i = 0; i < this.attrs.length; i += 2) {
3809 const attrName = this.attrs[i];
3810 const attrValue = this.attrs[i + 1] !== '' ? `="${this.attrs[i + 1]}"` : '';
3811 attrs += ` ${attrName}${attrValue}`;
3812 }
3813 return getHtmlTagDefinition(tagName).isVoid ? `<${tagName}${classAttr}${attrs}/>` :
3814 `<${tagName}${classAttr}${attrs}></${tagName}>`;
3815 }
3816 getAttrs() {
3817 const result = [];
3818 if (this.classNames.length > 0) {
3819 result.push('class', this.classNames.join(' '));
3820 }
3821 return result.concat(this.attrs);
3822 }
3823 addAttribute(name, value = '') {
3824 this.attrs.push(name, value && value.toLowerCase() || '');
3825 }
3826 addClassName(name) {
3827 this.classNames.push(name.toLowerCase());
3828 }
3829 toString() {
3830 let res = this.element || '';
3831 if (this.classNames) {
3832 this.classNames.forEach(klass => res += `.${klass}`);
3833 }
3834 if (this.attrs) {
3835 for (let i = 0; i < this.attrs.length; i += 2) {
3836 const name = this.attrs[i];
3837 const value = this.attrs[i + 1];
3838 res += `[${name}${value ? '=' + value : ''}]`;
3839 }
3840 }
3841 this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`);
3842 return res;
3843 }
3844 }
3845 /**
3846 * Reads a list of CssSelectors and allows to calculate which ones
3847 * are contained in a given CssSelector.
3848 */
3849 class SelectorMatcher {
3850 constructor() {
3851 this._elementMap = new Map();
3852 this._elementPartialMap = new Map();
3853 this._classMap = new Map();
3854 this._classPartialMap = new Map();
3855 this._attrValueMap = new Map();
3856 this._attrValuePartialMap = new Map();
3857 this._listContexts = [];
3858 }
3859 static createNotMatcher(notSelectors) {
3860 const notMatcher = new SelectorMatcher();
3861 notMatcher.addSelectables(notSelectors, null);
3862 return notMatcher;
3863 }
3864 addSelectables(cssSelectors, callbackCtxt) {
3865 let listContext = null;
3866 if (cssSelectors.length > 1) {
3867 listContext = new SelectorListContext(cssSelectors);
3868 this._listContexts.push(listContext);
3869 }
3870 for (let i = 0; i < cssSelectors.length; i++) {
3871 this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
3872 }
3873 }
3874 /**
3875 * Add an object that can be found later on by calling `match`.
3876 * @param cssSelector A css selector
3877 * @param callbackCtxt An opaque object that will be given to the callback of the `match` function
3878 */
3879 _addSelectable(cssSelector, callbackCtxt, listContext) {
3880 let matcher = this;
3881 const element = cssSelector.element;
3882 const classNames = cssSelector.classNames;
3883 const attrs = cssSelector.attrs;
3884 const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
3885 if (element) {
3886 const isTerminal = attrs.length === 0 && classNames.length === 0;
3887 if (isTerminal) {
3888 this._addTerminal(matcher._elementMap, element, selectable);
3889 }
3890 else {
3891 matcher = this._addPartial(matcher._elementPartialMap, element);
3892 }
3893 }
3894 if (classNames) {
3895 for (let i = 0; i < classNames.length; i++) {
3896 const isTerminal = attrs.length === 0 && i === classNames.length - 1;
3897 const className = classNames[i];
3898 if (isTerminal) {
3899 this._addTerminal(matcher._classMap, className, selectable);
3900 }
3901 else {
3902 matcher = this._addPartial(matcher._classPartialMap, className);
3903 }
3904 }
3905 }
3906 if (attrs) {
3907 for (let i = 0; i < attrs.length; i += 2) {
3908 const isTerminal = i === attrs.length - 2;
3909 const name = attrs[i];
3910 const value = attrs[i + 1];
3911 if (isTerminal) {
3912 const terminalMap = matcher._attrValueMap;
3913 let terminalValuesMap = terminalMap.get(name);
3914 if (!terminalValuesMap) {
3915 terminalValuesMap = new Map();
3916 terminalMap.set(name, terminalValuesMap);
3917 }
3918 this._addTerminal(terminalValuesMap, value, selectable);
3919 }
3920 else {
3921 const partialMap = matcher._attrValuePartialMap;
3922 let partialValuesMap = partialMap.get(name);
3923 if (!partialValuesMap) {
3924 partialValuesMap = new Map();
3925 partialMap.set(name, partialValuesMap);
3926 }
3927 matcher = this._addPartial(partialValuesMap, value);
3928 }
3929 }
3930 }
3931 }
3932 _addTerminal(map, name, selectable) {
3933 let terminalList = map.get(name);
3934 if (!terminalList) {
3935 terminalList = [];
3936 map.set(name, terminalList);
3937 }
3938 terminalList.push(selectable);
3939 }
3940 _addPartial(map, name) {
3941 let matcher = map.get(name);
3942 if (!matcher) {
3943 matcher = new SelectorMatcher();
3944 map.set(name, matcher);
3945 }
3946 return matcher;
3947 }
3948 /**
3949 * Find the objects that have been added via `addSelectable`
3950 * whose css selector is contained in the given css selector.
3951 * @param cssSelector A css selector
3952 * @param matchedCallback This callback will be called with the object handed into `addSelectable`
3953 * @return boolean true if a match was found
3954 */
3955 match(cssSelector, matchedCallback) {
3956 let result = false;
3957 const element = cssSelector.element;
3958 const classNames = cssSelector.classNames;
3959 const attrs = cssSelector.attrs;
3960 for (let i = 0; i < this._listContexts.length; i++) {
3961 this._listContexts[i].alreadyMatched = false;
3962 }
3963 result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
3964 result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) ||
3965 result;
3966 if (classNames) {
3967 for (let i = 0; i < classNames.length; i++) {
3968 const className = classNames[i];
3969 result =
3970 this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
3971 result =
3972 this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) ||
3973 result;
3974 }
3975 }
3976 if (attrs) {
3977 for (let i = 0; i < attrs.length; i += 2) {
3978 const name = attrs[i];
3979 const value = attrs[i + 1];
3980 const terminalValuesMap = this._attrValueMap.get(name);
3981 if (value) {
3982 result =
3983 this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
3984 }
3985 result =
3986 this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
3987 const partialValuesMap = this._attrValuePartialMap.get(name);
3988 if (value) {
3989 result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
3990 }
3991 result =
3992 this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result;
3993 }
3994 }
3995 return result;
3996 }
3997 /** @internal */
3998 _matchTerminal(map, name, cssSelector, matchedCallback) {
3999 if (!map || typeof name !== 'string') {
4000 return false;
4001 }
4002 let selectables = map.get(name) || [];
4003 const starSelectables = map.get('*');
4004 if (starSelectables) {
4005 selectables = selectables.concat(starSelectables);
4006 }
4007 if (selectables.length === 0) {
4008 return false;
4009 }
4010 let selectable;
4011 let result = false;
4012 for (let i = 0; i < selectables.length; i++) {
4013 selectable = selectables[i];
4014 result = selectable.finalize(cssSelector, matchedCallback) || result;
4015 }
4016 return result;
4017 }
4018 /** @internal */
4019 _matchPartial(map, name, cssSelector, matchedCallback) {
4020 if (!map || typeof name !== 'string') {
4021 return false;
4022 }
4023 const nestedSelector = map.get(name);
4024 if (!nestedSelector) {
4025 return false;
4026 }
4027 // TODO(perf): get rid of recursion and measure again
4028 // TODO(perf): don't pass the whole selector into the recursion,
4029 // but only the not processed parts
4030 return nestedSelector.match(cssSelector, matchedCallback);
4031 }
4032 }
4033 class SelectorListContext {
4034 constructor(selectors) {
4035 this.selectors = selectors;
4036 this.alreadyMatched = false;
4037 }
4038 }
4039 // Store context to pass back selector and context when a selector is matched
4040 class SelectorContext {
4041 constructor(selector, cbContext, listContext) {
4042 this.selector = selector;
4043 this.cbContext = cbContext;
4044 this.listContext = listContext;
4045 this.notSelectors = selector.notSelectors;
4046 }
4047 finalize(cssSelector, callback) {
4048 let result = true;
4049 if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
4050 const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
4051 result = !notMatcher.match(cssSelector, null);
4052 }
4053 if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) {
4054 if (this.listContext) {
4055 this.listContext.alreadyMatched = true;
4056 }
4057 callback(this.selector, this.cbContext);
4058 }
4059 return result;
4060 }
4061 }
4062
4063 /**
4064 * @license
4065 * Copyright Google LLC All Rights Reserved.
4066 *
4067 * Use of this source code is governed by an MIT-style license that can be
4068 * found in the LICENSE file at https://angular.io/license
4069 */
4070 // Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not
4071 // explicitly set. This value will be changed to `true` in v12.
4072 // TODO(misko): switch the default in v12 to `true`. See: packages/core/src/metadata/di.ts
4073 const emitDistinctChangesOnlyDefaultValue = false;
4074 var ViewEncapsulation;
4075 (function (ViewEncapsulation) {
4076 ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
4077 // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
4078 ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
4079 ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
4080 })(ViewEncapsulation || (ViewEncapsulation = {}));
4081 var ChangeDetectionStrategy;
4082 (function (ChangeDetectionStrategy) {
4083 ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
4084 ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
4085 })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
4086 const CUSTOM_ELEMENTS_SCHEMA = {
4087 name: 'custom-elements'
4088 };
4089 const NO_ERRORS_SCHEMA = {
4090 name: 'no-errors-schema'
4091 };
4092 var SecurityContext;
4093 (function (SecurityContext) {
4094 SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
4095 SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
4096 SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
4097 SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
4098 SecurityContext[SecurityContext["URL"] = 4] = "URL";
4099 SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
4100 })(SecurityContext || (SecurityContext = {}));
4101 var MissingTranslationStrategy;
4102 (function (MissingTranslationStrategy) {
4103 MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error";
4104 MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning";
4105 MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore";
4106 })(MissingTranslationStrategy || (MissingTranslationStrategy = {}));
4107 function parserSelectorToSimpleSelector(selector) {
4108 const classes = selector.classNames && selector.classNames.length ?
4109 [8 /* CLASS */, ...selector.classNames] :
4110 [];
4111 const elementName = selector.element && selector.element !== '*' ? selector.element : '';
4112 return [elementName, ...selector.attrs, ...classes];
4113 }
4114 function parserSelectorToNegativeSelector(selector) {
4115 const classes = selector.classNames && selector.classNames.length ?
4116 [8 /* CLASS */, ...selector.classNames] :
4117 [];
4118 if (selector.element) {
4119 return [
4120 1 /* NOT */ | 4 /* ELEMENT */, selector.element, ...selector.attrs, ...classes
4121 ];
4122 }
4123 else if (selector.attrs.length) {
4124 return [1 /* NOT */ | 2 /* ATTRIBUTE */, ...selector.attrs, ...classes];
4125 }
4126 else {
4127 return selector.classNames && selector.classNames.length ?
4128 [1 /* NOT */ | 8 /* CLASS */, ...selector.classNames] :
4129 [];
4130 }
4131 }
4132 function parserSelectorToR3Selector(selector) {
4133 const positive = parserSelectorToSimpleSelector(selector);
4134 const negative = selector.notSelectors && selector.notSelectors.length ?
4135 selector.notSelectors.map(notSelector => parserSelectorToNegativeSelector(notSelector)) :
4136 [];
4137 return positive.concat(...negative);
4138 }
4139 function parseSelectorToR3Selector(selector) {
4140 return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : [];
4141 }
4142
4143 /**
4144 * @license
4145 * Copyright Google LLC All Rights Reserved.
4146 *
4147 * Use of this source code is governed by an MIT-style license that can be
4148 * found in the LICENSE file at https://angular.io/license
4149 */
4150 //// Types
4151 var TypeModifier;
4152 (function (TypeModifier) {
4153 TypeModifier[TypeModifier["Const"] = 0] = "Const";
4154 })(TypeModifier || (TypeModifier = {}));
4155 class Type {
4156 constructor(modifiers = []) {
4157 this.modifiers = modifiers;
4158 }
4159 hasModifier(modifier) {
4160 return this.modifiers.indexOf(modifier) !== -1;
4161 }
4162 }
4163 var BuiltinTypeName;
4164 (function (BuiltinTypeName) {
4165 BuiltinTypeName[BuiltinTypeName["Dynamic"] = 0] = "Dynamic";
4166 BuiltinTypeName[BuiltinTypeName["Bool"] = 1] = "Bool";
4167 BuiltinTypeName[BuiltinTypeName["String"] = 2] = "String";
4168 BuiltinTypeName[BuiltinTypeName["Int"] = 3] = "Int";
4169 BuiltinTypeName[BuiltinTypeName["Number"] = 4] = "Number";
4170 BuiltinTypeName[BuiltinTypeName["Function"] = 5] = "Function";
4171 BuiltinTypeName[BuiltinTypeName["Inferred"] = 6] = "Inferred";
4172 BuiltinTypeName[BuiltinTypeName["None"] = 7] = "None";
4173 })(BuiltinTypeName || (BuiltinTypeName = {}));
4174 class BuiltinType extends Type {
4175 constructor(name, modifiers) {
4176 super(modifiers);
4177 this.name = name;
4178 }
4179 visitType(visitor, context) {
4180 return visitor.visitBuiltinType(this, context);
4181 }
4182 }
4183 class ExpressionType extends Type {
4184 constructor(value, modifiers, typeParams = null) {
4185 super(modifiers);
4186 this.value = value;
4187 this.typeParams = typeParams;
4188 }
4189 visitType(visitor, context) {
4190 return visitor.visitExpressionType(this, context);
4191 }
4192 }
4193 const DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
4194 const INFERRED_TYPE = new BuiltinType(BuiltinTypeName.Inferred);
4195 const BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
4196 const INT_TYPE = new BuiltinType(BuiltinTypeName.Int);
4197 const NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
4198 const STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
4199 const FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
4200 const NONE_TYPE = new BuiltinType(BuiltinTypeName.None);
4201 ///// Expressions
4202 var UnaryOperator;
4203 (function (UnaryOperator) {
4204 UnaryOperator[UnaryOperator["Minus"] = 0] = "Minus";
4205 UnaryOperator[UnaryOperator["Plus"] = 1] = "Plus";
4206 })(UnaryOperator || (UnaryOperator = {}));
4207 var BinaryOperator;
4208 (function (BinaryOperator) {
4209 BinaryOperator[BinaryOperator["Equals"] = 0] = "Equals";
4210 BinaryOperator[BinaryOperator["NotEquals"] = 1] = "NotEquals";
4211 BinaryOperator[BinaryOperator["Identical"] = 2] = "Identical";
4212 BinaryOperator[BinaryOperator["NotIdentical"] = 3] = "NotIdentical";
4213 BinaryOperator[BinaryOperator["Minus"] = 4] = "Minus";
4214 BinaryOperator[BinaryOperator["Plus"] = 5] = "Plus";
4215 BinaryOperator[BinaryOperator["Divide"] = 6] = "Divide";
4216 BinaryOperator[BinaryOperator["Multiply"] = 7] = "Multiply";
4217 BinaryOperator[BinaryOperator["Modulo"] = 8] = "Modulo";
4218 BinaryOperator[BinaryOperator["And"] = 9] = "And";
4219 BinaryOperator[BinaryOperator["Or"] = 10] = "Or";
4220 BinaryOperator[BinaryOperator["BitwiseAnd"] = 11] = "BitwiseAnd";
4221 BinaryOperator[BinaryOperator["Lower"] = 12] = "Lower";
4222 BinaryOperator[BinaryOperator["LowerEquals"] = 13] = "LowerEquals";
4223 BinaryOperator[BinaryOperator["Bigger"] = 14] = "Bigger";
4224 BinaryOperator[BinaryOperator["BiggerEquals"] = 15] = "BiggerEquals";
4225 })(BinaryOperator || (BinaryOperator = {}));
4226 function nullSafeIsEquivalent(base, other) {
4227 if (base == null || other == null) {
4228 return base == other;
4229 }
4230 return base.isEquivalent(other);
4231 }
4232 function areAllEquivalentPredicate(base, other, equivalentPredicate) {
4233 const len = base.length;
4234 if (len !== other.length) {
4235 return false;
4236 }
4237 for (let i = 0; i < len; i++) {
4238 if (!equivalentPredicate(base[i], other[i])) {
4239 return false;
4240 }
4241 }
4242 return true;
4243 }
4244 function areAllEquivalent(base, other) {
4245 return areAllEquivalentPredicate(base, other, (baseElement, otherElement) => baseElement.isEquivalent(otherElement));
4246 }
4247 class Expression {
4248 constructor(type, sourceSpan) {
4249 this.type = type || null;
4250 this.sourceSpan = sourceSpan || null;
4251 }
4252 prop(name, sourceSpan) {
4253 return new ReadPropExpr(this, name, null, sourceSpan);
4254 }
4255 key(index, type, sourceSpan) {
4256 return new ReadKeyExpr(this, index, type, sourceSpan);
4257 }
4258 callMethod(name, params, sourceSpan) {
4259 return new InvokeMethodExpr(this, name, params, null, sourceSpan);
4260 }
4261 callFn(params, sourceSpan, pure) {
4262 return new InvokeFunctionExpr(this, params, null, sourceSpan, pure);
4263 }
4264 instantiate(params, type, sourceSpan) {
4265 return new InstantiateExpr(this, params, type, sourceSpan);
4266 }
4267 conditional(trueCase, falseCase = null, sourceSpan) {
4268 return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan);
4269 }
4270 equals(rhs, sourceSpan) {
4271 return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs, null, sourceSpan);
4272 }
4273 notEquals(rhs, sourceSpan) {
4274 return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs, null, sourceSpan);
4275 }
4276 identical(rhs, sourceSpan) {
4277 return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs, null, sourceSpan);
4278 }
4279 notIdentical(rhs, sourceSpan) {
4280 return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs, null, sourceSpan);
4281 }
4282 minus(rhs, sourceSpan) {
4283 return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs, null, sourceSpan);
4284 }
4285 plus(rhs, sourceSpan) {
4286 return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs, null, sourceSpan);
4287 }
4288 divide(rhs, sourceSpan) {
4289 return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs, null, sourceSpan);
4290 }
4291 multiply(rhs, sourceSpan) {
4292 return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs, null, sourceSpan);
4293 }
4294 modulo(rhs, sourceSpan) {
4295 return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs, null, sourceSpan);
4296 }
4297 and(rhs, sourceSpan) {
4298 return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan);
4299 }
4300 bitwiseAnd(rhs, sourceSpan, parens = true) {
4301 return new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, this, rhs, null, sourceSpan, parens);
4302 }
4303 or(rhs, sourceSpan) {
4304 return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs, null, sourceSpan);
4305 }
4306 lower(rhs, sourceSpan) {
4307 return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs, null, sourceSpan);
4308 }
4309 lowerEquals(rhs, sourceSpan) {
4310 return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs, null, sourceSpan);
4311 }
4312 bigger(rhs, sourceSpan) {
4313 return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs, null, sourceSpan);
4314 }
4315 biggerEquals(rhs, sourceSpan) {
4316 return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs, null, sourceSpan);
4317 }
4318 isBlank(sourceSpan) {
4319 // Note: We use equals by purpose here to compare to null and undefined in JS.
4320 // We use the typed null to allow strictNullChecks to narrow types.
4321 return this.equals(TYPED_NULL_EXPR, sourceSpan);
4322 }
4323 cast(type, sourceSpan) {
4324 return new CastExpr(this, type, sourceSpan);
4325 }
4326 toStmt() {
4327 return new ExpressionStatement(this, null);
4328 }
4329 }
4330 var BuiltinVar;
4331 (function (BuiltinVar) {
4332 BuiltinVar[BuiltinVar["This"] = 0] = "This";
4333 BuiltinVar[BuiltinVar["Super"] = 1] = "Super";
4334 BuiltinVar[BuiltinVar["CatchError"] = 2] = "CatchError";
4335 BuiltinVar[BuiltinVar["CatchStack"] = 3] = "CatchStack";
4336 })(BuiltinVar || (BuiltinVar = {}));
4337 class ReadVarExpr extends Expression {
4338 constructor(name, type, sourceSpan) {
4339 super(type, sourceSpan);
4340 if (typeof name === 'string') {
4341 this.name = name;
4342 this.builtin = null;
4343 }
4344 else {
4345 this.name = null;
4346 this.builtin = name;
4347 }
4348 }
4349 isEquivalent(e) {
4350 return e instanceof ReadVarExpr && this.name === e.name && this.builtin === e.builtin;
4351 }
4352 isConstant() {
4353 return false;
4354 }
4355 visitExpression(visitor, context) {
4356 return visitor.visitReadVarExpr(this, context);
4357 }
4358 set(value) {
4359 if (!this.name) {
4360 throw new Error(`Built in variable ${this.builtin} can not be assigned to.`);
4361 }
4362 return new WriteVarExpr(this.name, value, null, this.sourceSpan);
4363 }
4364 }
4365 class TypeofExpr extends Expression {
4366 constructor(expr, type, sourceSpan) {
4367 super(type, sourceSpan);
4368 this.expr = expr;
4369 }
4370 visitExpression(visitor, context) {
4371 return visitor.visitTypeofExpr(this, context);
4372 }
4373 isEquivalent(e) {
4374 return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr);
4375 }
4376 isConstant() {
4377 return this.expr.isConstant();
4378 }
4379 }
4380 class WrappedNodeExpr extends Expression {
4381 constructor(node, type, sourceSpan) {
4382 super(type, sourceSpan);
4383 this.node = node;
4384 }
4385 isEquivalent(e) {
4386 return e instanceof WrappedNodeExpr && this.node === e.node;
4387 }
4388 isConstant() {
4389 return false;
4390 }
4391 visitExpression(visitor, context) {
4392 return visitor.visitWrappedNodeExpr(this, context);
4393 }
4394 }
4395 class WriteVarExpr extends Expression {
4396 constructor(name, value, type, sourceSpan) {
4397 super(type || value.type, sourceSpan);
4398 this.name = name;
4399 this.value = value;
4400 }
4401 isEquivalent(e) {
4402 return e instanceof WriteVarExpr && this.name === e.name && this.value.isEquivalent(e.value);
4403 }
4404 isConstant() {
4405 return false;
4406 }
4407 visitExpression(visitor, context) {
4408 return visitor.visitWriteVarExpr(this, context);
4409 }
4410 toDeclStmt(type, modifiers) {
4411 return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
4412 }
4413 toConstDecl() {
4414 return this.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]);
4415 }
4416 }
4417 class WriteKeyExpr extends Expression {
4418 constructor(receiver, index, value, type, sourceSpan) {
4419 super(type || value.type, sourceSpan);
4420 this.receiver = receiver;
4421 this.index = index;
4422 this.value = value;
4423 }
4424 isEquivalent(e) {
4425 return e instanceof WriteKeyExpr && this.receiver.isEquivalent(e.receiver) &&
4426 this.index.isEquivalent(e.index) && this.value.isEquivalent(e.value);
4427 }
4428 isConstant() {
4429 return false;
4430 }
4431 visitExpression(visitor, context) {
4432 return visitor.visitWriteKeyExpr(this, context);
4433 }
4434 }
4435 class WritePropExpr extends Expression {
4436 constructor(receiver, name, value, type, sourceSpan) {
4437 super(type || value.type, sourceSpan);
4438 this.receiver = receiver;
4439 this.name = name;
4440 this.value = value;
4441 }
4442 isEquivalent(e) {
4443 return e instanceof WritePropExpr && this.receiver.isEquivalent(e.receiver) &&
4444 this.name === e.name && this.value.isEquivalent(e.value);
4445 }
4446 isConstant() {
4447 return false;
4448 }
4449 visitExpression(visitor, context) {
4450 return visitor.visitWritePropExpr(this, context);
4451 }
4452 }
4453 var BuiltinMethod;
4454 (function (BuiltinMethod) {
4455 BuiltinMethod[BuiltinMethod["ConcatArray"] = 0] = "ConcatArray";
4456 BuiltinMethod[BuiltinMethod["SubscribeObservable"] = 1] = "SubscribeObservable";
4457 BuiltinMethod[BuiltinMethod["Bind"] = 2] = "Bind";
4458 })(BuiltinMethod || (BuiltinMethod = {}));
4459 class InvokeMethodExpr extends Expression {
4460 constructor(receiver, method, args, type, sourceSpan) {
4461 super(type, sourceSpan);
4462 this.receiver = receiver;
4463 this.args = args;
4464 if (typeof method === 'string') {
4465 this.name = method;
4466 this.builtin = null;
4467 }
4468 else {
4469 this.name = null;
4470 this.builtin = method;
4471 }
4472 }
4473 isEquivalent(e) {
4474 return e instanceof InvokeMethodExpr && this.receiver.isEquivalent(e.receiver) &&
4475 this.name === e.name && this.builtin === e.builtin && areAllEquivalent(this.args, e.args);
4476 }
4477 isConstant() {
4478 return false;
4479 }
4480 visitExpression(visitor, context) {
4481 return visitor.visitInvokeMethodExpr(this, context);
4482 }
4483 }
4484 class InvokeFunctionExpr extends Expression {
4485 constructor(fn, args, type, sourceSpan, pure = false) {
4486 super(type, sourceSpan);
4487 this.fn = fn;
4488 this.args = args;
4489 this.pure = pure;
4490 }
4491 isEquivalent(e) {
4492 return e instanceof InvokeFunctionExpr && this.fn.isEquivalent(e.fn) &&
4493 areAllEquivalent(this.args, e.args) && this.pure === e.pure;
4494 }
4495 isConstant() {
4496 return false;
4497 }
4498 visitExpression(visitor, context) {
4499 return visitor.visitInvokeFunctionExpr(this, context);
4500 }
4501 }
4502 class TaggedTemplateExpr extends Expression {
4503 constructor(tag, template, type, sourceSpan) {
4504 super(type, sourceSpan);
4505 this.tag = tag;
4506 this.template = template;
4507 }
4508 isEquivalent(e) {
4509 return e instanceof TaggedTemplateExpr && this.tag.isEquivalent(e.tag) &&
4510 areAllEquivalentPredicate(this.template.elements, e.template.elements, (a, b) => a.text === b.text) &&
4511 areAllEquivalent(this.template.expressions, e.template.expressions);
4512 }
4513 isConstant() {
4514 return false;
4515 }
4516 visitExpression(visitor, context) {
4517 return visitor.visitTaggedTemplateExpr(this, context);
4518 }
4519 }
4520 class InstantiateExpr extends Expression {
4521 constructor(classExpr, args, type, sourceSpan) {
4522 super(type, sourceSpan);
4523 this.classExpr = classExpr;
4524 this.args = args;
4525 }
4526 isEquivalent(e) {
4527 return e instanceof InstantiateExpr && this.classExpr.isEquivalent(e.classExpr) &&
4528 areAllEquivalent(this.args, e.args);
4529 }
4530 isConstant() {
4531 return false;
4532 }
4533 visitExpression(visitor, context) {
4534 return visitor.visitInstantiateExpr(this, context);
4535 }
4536 }
4537 class LiteralExpr extends Expression {
4538 constructor(value, type, sourceSpan) {
4539 super(type, sourceSpan);
4540 this.value = value;
4541 }
4542 isEquivalent(e) {
4543 return e instanceof LiteralExpr && this.value === e.value;
4544 }
4545 isConstant() {
4546 return true;
4547 }
4548 visitExpression(visitor, context) {
4549 return visitor.visitLiteralExpr(this, context);
4550 }
4551 }
4552 class TemplateLiteral {
4553 constructor(elements, expressions) {
4554 this.elements = elements;
4555 this.expressions = expressions;
4556 }
4557 }
4558 class TemplateLiteralElement {
4559 constructor(text, sourceSpan, rawText) {
4560 var _a;
4561 this.text = text;
4562 this.sourceSpan = sourceSpan;
4563 // If `rawText` is not provided, try to extract the raw string from its
4564 // associated `sourceSpan`. If that is also not available, "fake" the raw
4565 // string instead by escaping the following control sequences:
4566 // - "\" would otherwise indicate that the next character is a control character.
4567 // - "`" and "${" are template string control sequences that would otherwise prematurely
4568 // indicate the end of the template literal element.
4569 this.rawText = (_a = rawText !== null && rawText !== void 0 ? rawText : sourceSpan === null || sourceSpan === void 0 ? void 0 : sourceSpan.toString()) !== null && _a !== void 0 ? _a : escapeForTemplateLiteral(escapeSlashes(text));
4570 }
4571 }
4572 class MessagePiece {
4573 constructor(text, sourceSpan) {
4574 this.text = text;
4575 this.sourceSpan = sourceSpan;
4576 }
4577 }
4578 class LiteralPiece extends MessagePiece {
4579 }
4580 class PlaceholderPiece extends MessagePiece {
4581 }
4582 class LocalizedString extends Expression {
4583 constructor(metaBlock, messageParts, placeHolderNames, expressions, sourceSpan) {
4584 super(STRING_TYPE, sourceSpan);
4585 this.metaBlock = metaBlock;
4586 this.messageParts = messageParts;
4587 this.placeHolderNames = placeHolderNames;
4588 this.expressions = expressions;
4589 }
4590 isEquivalent(e) {
4591 // return e instanceof LocalizedString && this.message === e.message;
4592 return false;
4593 }
4594 isConstant() {
4595 return false;
4596 }
4597 visitExpression(visitor, context) {
4598 return visitor.visitLocalizedString(this, context);
4599 }
4600 /**
4601 * Serialize the given `meta` and `messagePart` into "cooked" and "raw" strings that can be used
4602 * in a `$localize` tagged string. The format of the metadata is the same as that parsed by
4603 * `parseI18nMeta()`.
4604 *
4605 * @param meta The metadata to serialize
4606 * @param messagePart The first part of the tagged string
4607 */
4608 serializeI18nHead() {
4609 const MEANING_SEPARATOR = '|';
4610 const ID_SEPARATOR = '@@';
4611 const LEGACY_ID_INDICATOR = '␟';
4612 let metaBlock = this.metaBlock.description || '';
4613 if (this.metaBlock.meaning) {
4614 metaBlock = `${this.metaBlock.meaning}${MEANING_SEPARATOR}${metaBlock}`;
4615 }
4616 if (this.metaBlock.customId) {
4617 metaBlock = `${metaBlock}${ID_SEPARATOR}${this.metaBlock.customId}`;
4618 }
4619 if (this.metaBlock.legacyIds) {
4620 this.metaBlock.legacyIds.forEach(legacyId => {
4621 metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`;
4622 });
4623 }
4624 return createCookedRawString(metaBlock, this.messageParts[0].text, this.getMessagePartSourceSpan(0));
4625 }
4626 getMessagePartSourceSpan(i) {
4627 var _a, _b;
4628 return (_b = (_a = this.messageParts[i]) === null || _a === void 0 ? void 0 : _a.sourceSpan) !== null && _b !== void 0 ? _b : this.sourceSpan;
4629 }
4630 getPlaceholderSourceSpan(i) {
4631 var _a, _b, _c, _d;
4632 return (_d = (_b = (_a = this.placeHolderNames[i]) === null || _a === void 0 ? void 0 : _a.sourceSpan) !== null && _b !== void 0 ? _b : (_c = this.expressions[i]) === null || _c === void 0 ? void 0 : _c.sourceSpan) !== null && _d !== void 0 ? _d : this.sourceSpan;
4633 }
4634 /**
4635 * Serialize the given `placeholderName` and `messagePart` into "cooked" and "raw" strings that
4636 * can be used in a `$localize` tagged string.
4637 *
4638 * @param placeholderName The placeholder name to serialize
4639 * @param messagePart The following message string after this placeholder
4640 */
4641 serializeI18nTemplatePart(partIndex) {
4642 const placeholderName = this.placeHolderNames[partIndex - 1].text;
4643 const messagePart = this.messageParts[partIndex];
4644 return createCookedRawString(placeholderName, messagePart.text, this.getMessagePartSourceSpan(partIndex));
4645 }
4646 }
4647 const escapeSlashes = (str) => str.replace(/\\/g, '\\\\');
4648 const escapeStartingColon = (str) => str.replace(/^:/, '\\:');
4649 const escapeColons = (str) => str.replace(/:/g, '\\:');
4650 const escapeForTemplateLiteral = (str) => str.replace(/`/g, '\\`').replace(/\${/g, '$\\{');
4651 /**
4652 * Creates a `{cooked, raw}` object from the `metaBlock` and `messagePart`.
4653 *
4654 * The `raw` text must have various character sequences escaped:
4655 * * "\" would otherwise indicate that the next character is a control character.
4656 * * "`" and "${" are template string control sequences that would otherwise prematurely indicate
4657 * the end of a message part.
4658 * * ":" inside a metablock would prematurely indicate the end of the metablock.
4659 * * ":" at the start of a messagePart with no metablock would erroneously indicate the start of a
4660 * metablock.
4661 *
4662 * @param metaBlock Any metadata that should be prepended to the string
4663 * @param messagePart The message part of the string
4664 */
4665 function createCookedRawString(metaBlock, messagePart, range) {
4666 if (metaBlock === '') {
4667 return {
4668 cooked: messagePart,
4669 raw: escapeForTemplateLiteral(escapeStartingColon(escapeSlashes(messagePart))),
4670 range,
4671 };
4672 }
4673 else {
4674 return {
4675 cooked: `:${metaBlock}:${messagePart}`,
4676 raw: escapeForTemplateLiteral(`:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`),
4677 range,
4678 };
4679 }
4680 }
4681 class ExternalExpr extends Expression {
4682 constructor(value, type, typeParams = null, sourceSpan) {
4683 super(type, sourceSpan);
4684 this.value = value;
4685 this.typeParams = typeParams;
4686 }
4687 isEquivalent(e) {
4688 return e instanceof ExternalExpr && this.value.name === e.value.name &&
4689 this.value.moduleName === e.value.moduleName && this.value.runtime === e.value.runtime;
4690 }
4691 isConstant() {
4692 return false;
4693 }
4694 visitExpression(visitor, context) {
4695 return visitor.visitExternalExpr(this, context);
4696 }
4697 }
4698 class ExternalReference {
4699 constructor(moduleName, name, runtime) {
4700 this.moduleName = moduleName;
4701 this.name = name;
4702 this.runtime = runtime;
4703 }
4704 }
4705 class ConditionalExpr extends Expression {
4706 constructor(condition, trueCase, falseCase = null, type, sourceSpan) {
4707 super(type || trueCase.type, sourceSpan);
4708 this.condition = condition;
4709 this.falseCase = falseCase;
4710 this.trueCase = trueCase;
4711 }
4712 isEquivalent(e) {
4713 return e instanceof ConditionalExpr && this.condition.isEquivalent(e.condition) &&
4714 this.trueCase.isEquivalent(e.trueCase) && nullSafeIsEquivalent(this.falseCase, e.falseCase);
4715 }
4716 isConstant() {
4717 return false;
4718 }
4719 visitExpression(visitor, context) {
4720 return visitor.visitConditionalExpr(this, context);
4721 }
4722 }
4723 class NotExpr extends Expression {
4724 constructor(condition, sourceSpan) {
4725 super(BOOL_TYPE, sourceSpan);
4726 this.condition = condition;
4727 }
4728 isEquivalent(e) {
4729 return e instanceof NotExpr && this.condition.isEquivalent(e.condition);
4730 }
4731 isConstant() {
4732 return false;
4733 }
4734 visitExpression(visitor, context) {
4735 return visitor.visitNotExpr(this, context);
4736 }
4737 }
4738 class AssertNotNull extends Expression {
4739 constructor(condition, sourceSpan) {
4740 super(condition.type, sourceSpan);
4741 this.condition = condition;
4742 }
4743 isEquivalent(e) {
4744 return e instanceof AssertNotNull && this.condition.isEquivalent(e.condition);
4745 }
4746 isConstant() {
4747 return false;
4748 }
4749 visitExpression(visitor, context) {
4750 return visitor.visitAssertNotNullExpr(this, context);
4751 }
4752 }
4753 class CastExpr extends Expression {
4754 constructor(value, type, sourceSpan) {
4755 super(type, sourceSpan);
4756 this.value = value;
4757 }
4758 isEquivalent(e) {
4759 return e instanceof CastExpr && this.value.isEquivalent(e.value);
4760 }
4761 isConstant() {
4762 return false;
4763 }
4764 visitExpression(visitor, context) {
4765 return visitor.visitCastExpr(this, context);
4766 }
4767 }
4768 class FnParam {
4769 constructor(name, type = null) {
4770 this.name = name;
4771 this.type = type;
4772 }
4773 isEquivalent(param) {
4774 return this.name === param.name;
4775 }
4776 }
4777 class FunctionExpr extends Expression {
4778 constructor(params, statements, type, sourceSpan, name) {
4779 super(type, sourceSpan);
4780 this.params = params;
4781 this.statements = statements;
4782 this.name = name;
4783 }
4784 isEquivalent(e) {
4785 return e instanceof FunctionExpr && areAllEquivalent(this.params, e.params) &&
4786 areAllEquivalent(this.statements, e.statements);
4787 }
4788 isConstant() {
4789 return false;
4790 }
4791 visitExpression(visitor, context) {
4792 return visitor.visitFunctionExpr(this, context);
4793 }
4794 toDeclStmt(name, modifiers) {
4795 return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
4796 }
4797 }
4798 class UnaryOperatorExpr extends Expression {
4799 constructor(operator, expr, type, sourceSpan, parens = true) {
4800 super(type || NUMBER_TYPE, sourceSpan);
4801 this.operator = operator;
4802 this.expr = expr;
4803 this.parens = parens;
4804 }
4805 isEquivalent(e) {
4806 return e instanceof UnaryOperatorExpr && this.operator === e.operator &&
4807 this.expr.isEquivalent(e.expr);
4808 }
4809 isConstant() {
4810 return false;
4811 }
4812 visitExpression(visitor, context) {
4813 return visitor.visitUnaryOperatorExpr(this, context);
4814 }
4815 }
4816 class BinaryOperatorExpr extends Expression {
4817 constructor(operator, lhs, rhs, type, sourceSpan, parens = true) {
4818 super(type || lhs.type, sourceSpan);
4819 this.operator = operator;
4820 this.rhs = rhs;
4821 this.parens = parens;
4822 this.lhs = lhs;
4823 }
4824 isEquivalent(e) {
4825 return e instanceof BinaryOperatorExpr && this.operator === e.operator &&
4826 this.lhs.isEquivalent(e.lhs) && this.rhs.isEquivalent(e.rhs);
4827 }
4828 isConstant() {
4829 return false;
4830 }
4831 visitExpression(visitor, context) {
4832 return visitor.visitBinaryOperatorExpr(this, context);
4833 }
4834 }
4835 class ReadPropExpr extends Expression {
4836 constructor(receiver, name, type, sourceSpan) {
4837 super(type, sourceSpan);
4838 this.receiver = receiver;
4839 this.name = name;
4840 }
4841 isEquivalent(e) {
4842 return e instanceof ReadPropExpr && this.receiver.isEquivalent(e.receiver) &&
4843 this.name === e.name;
4844 }
4845 isConstant() {
4846 return false;
4847 }
4848 visitExpression(visitor, context) {
4849 return visitor.visitReadPropExpr(this, context);
4850 }
4851 set(value) {
4852 return new WritePropExpr(this.receiver, this.name, value, null, this.sourceSpan);
4853 }
4854 }
4855 class ReadKeyExpr extends Expression {
4856 constructor(receiver, index, type, sourceSpan) {
4857 super(type, sourceSpan);
4858 this.receiver = receiver;
4859 this.index = index;
4860 }
4861 isEquivalent(e) {
4862 return e instanceof ReadKeyExpr && this.receiver.isEquivalent(e.receiver) &&
4863 this.index.isEquivalent(e.index);
4864 }
4865 isConstant() {
4866 return false;
4867 }
4868 visitExpression(visitor, context) {
4869 return visitor.visitReadKeyExpr(this, context);
4870 }
4871 set(value) {
4872 return new WriteKeyExpr(this.receiver, this.index, value, null, this.sourceSpan);
4873 }
4874 }
4875 class LiteralArrayExpr extends Expression {
4876 constructor(entries, type, sourceSpan) {
4877 super(type, sourceSpan);
4878 this.entries = entries;
4879 }
4880 isConstant() {
4881 return this.entries.every(e => e.isConstant());
4882 }
4883 isEquivalent(e) {
4884 return e instanceof LiteralArrayExpr && areAllEquivalent(this.entries, e.entries);
4885 }
4886 visitExpression(visitor, context) {
4887 return visitor.visitLiteralArrayExpr(this, context);
4888 }
4889 }
4890 class LiteralMapEntry {
4891 constructor(key, value, quoted) {
4892 this.key = key;
4893 this.value = value;
4894 this.quoted = quoted;
4895 }
4896 isEquivalent(e) {
4897 return this.key === e.key && this.value.isEquivalent(e.value);
4898 }
4899 }
4900 class LiteralMapExpr extends Expression {
4901 constructor(entries, type, sourceSpan) {
4902 super(type, sourceSpan);
4903 this.entries = entries;
4904 this.valueType = null;
4905 if (type) {
4906 this.valueType = type.valueType;
4907 }
4908 }
4909 isEquivalent(e) {
4910 return e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries);
4911 }
4912 isConstant() {
4913 return this.entries.every(e => e.value.isConstant());
4914 }
4915 visitExpression(visitor, context) {
4916 return visitor.visitLiteralMapExpr(this, context);
4917 }
4918 }
4919 const THIS_EXPR = new ReadVarExpr(BuiltinVar.This, null, null);
4920 const SUPER_EXPR = new ReadVarExpr(BuiltinVar.Super, null, null);
4921 const CATCH_ERROR_VAR = new ReadVarExpr(BuiltinVar.CatchError, null, null);
4922 const CATCH_STACK_VAR = new ReadVarExpr(BuiltinVar.CatchStack, null, null);
4923 const NULL_EXPR = new LiteralExpr(null, null, null);
4924 const TYPED_NULL_EXPR = new LiteralExpr(null, INFERRED_TYPE, null);
4925 //// Statements
4926 var StmtModifier;
4927 (function (StmtModifier) {
4928 StmtModifier[StmtModifier["Final"] = 0] = "Final";
4929 StmtModifier[StmtModifier["Private"] = 1] = "Private";
4930 StmtModifier[StmtModifier["Exported"] = 2] = "Exported";
4931 StmtModifier[StmtModifier["Static"] = 3] = "Static";
4932 })(StmtModifier || (StmtModifier = {}));
4933 class LeadingComment {
4934 constructor(text, multiline, trailingNewline) {
4935 this.text = text;
4936 this.multiline = multiline;
4937 this.trailingNewline = trailingNewline;
4938 }
4939 toString() {
4940 return this.multiline ? ` ${this.text} ` : this.text;
4941 }
4942 }
4943 class JSDocComment extends LeadingComment {
4944 constructor(tags) {
4945 super('', /* multiline */ true, /* trailingNewline */ true);
4946 this.tags = tags;
4947 }
4948 toString() {
4949 return serializeTags(this.tags);
4950 }
4951 }
4952 class Statement {
4953 constructor(modifiers = [], sourceSpan = null, leadingComments) {
4954 this.modifiers = modifiers;
4955 this.sourceSpan = sourceSpan;
4956 this.leadingComments = leadingComments;
4957 }
4958 hasModifier(modifier) {
4959 return this.modifiers.indexOf(modifier) !== -1;
4960 }
4961 addLeadingComment(leadingComment) {
4962 var _a;
4963 this.leadingComments = (_a = this.leadingComments) !== null && _a !== void 0 ? _a : [];
4964 this.leadingComments.push(leadingComment);
4965 }
4966 }
4967 class DeclareVarStmt extends Statement {
4968 constructor(name, value, type, modifiers, sourceSpan, leadingComments) {
4969 super(modifiers, sourceSpan, leadingComments);
4970 this.name = name;
4971 this.value = value;
4972 this.type = type || (value && value.type) || null;
4973 }
4974 isEquivalent(stmt) {
4975 return stmt instanceof DeclareVarStmt && this.name === stmt.name &&
4976 (this.value ? !!stmt.value && this.value.isEquivalent(stmt.value) : !stmt.value);
4977 }
4978 visitStatement(visitor, context) {
4979 return visitor.visitDeclareVarStmt(this, context);
4980 }
4981 }
4982 class DeclareFunctionStmt extends Statement {
4983 constructor(name, params, statements, type, modifiers, sourceSpan, leadingComments) {
4984 super(modifiers, sourceSpan, leadingComments);
4985 this.name = name;
4986 this.params = params;
4987 this.statements = statements;
4988 this.type = type || null;
4989 }
4990 isEquivalent(stmt) {
4991 return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) &&
4992 areAllEquivalent(this.statements, stmt.statements);
4993 }
4994 visitStatement(visitor, context) {
4995 return visitor.visitDeclareFunctionStmt(this, context);
4996 }
4997 }
4998 class ExpressionStatement extends Statement {
4999 constructor(expr, sourceSpan, leadingComments) {
5000 super([], sourceSpan, leadingComments);
5001 this.expr = expr;
5002 }
5003 isEquivalent(stmt) {
5004 return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr);
5005 }
5006 visitStatement(visitor, context) {
5007 return visitor.visitExpressionStmt(this, context);
5008 }
5009 }
5010 class ReturnStatement extends Statement {
5011 constructor(value, sourceSpan = null, leadingComments) {
5012 super([], sourceSpan, leadingComments);
5013 this.value = value;
5014 }
5015 isEquivalent(stmt) {
5016 return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value);
5017 }
5018 visitStatement(visitor, context) {
5019 return visitor.visitReturnStmt(this, context);
5020 }
5021 }
5022 class IfStmt extends Statement {
5023 constructor(condition, trueCase, falseCase = [], sourceSpan, leadingComments) {
5024 super([], sourceSpan, leadingComments);
5025 this.condition = condition;
5026 this.trueCase = trueCase;
5027 this.falseCase = falseCase;
5028 }
5029 isEquivalent(stmt) {
5030 return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) &&
5031 areAllEquivalent(this.trueCase, stmt.trueCase) &&
5032 areAllEquivalent(this.falseCase, stmt.falseCase);
5033 }
5034 visitStatement(visitor, context) {
5035 return visitor.visitIfStmt(this, context);
5036 }
5037 }
5038 function jsDocComment(tags = []) {
5039 return new JSDocComment(tags);
5040 }
5041 function variable(name, type, sourceSpan) {
5042 return new ReadVarExpr(name, type, sourceSpan);
5043 }
5044 function importExpr(id, typeParams = null, sourceSpan) {
5045 return new ExternalExpr(id, null, typeParams, sourceSpan);
5046 }
5047 function expressionType(expr, typeModifiers, typeParams) {
5048 return new ExpressionType(expr, typeModifiers, typeParams);
5049 }
5050 function typeofExpr(expr) {
5051 return new TypeofExpr(expr);
5052 }
5053 function literalArr(values, type, sourceSpan) {
5054 return new LiteralArrayExpr(values, type, sourceSpan);
5055 }
5056 function literalMap(values, type = null) {
5057 return new LiteralMapExpr(values.map(e => new LiteralMapEntry(e.key, e.value, e.quoted)), type, null);
5058 }
5059 function not(expr, sourceSpan) {
5060 return new NotExpr(expr, sourceSpan);
5061 }
5062 function assertNotNull(expr, sourceSpan) {
5063 return new AssertNotNull(expr, sourceSpan);
5064 }
5065 function fn(params, body, type, sourceSpan, name) {
5066 return new FunctionExpr(params, body, type, sourceSpan, name);
5067 }
5068 function ifStmt(condition, thenClause, elseClause, sourceSpan, leadingComments) {
5069 return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments);
5070 }
5071 function taggedTemplate(tag, template, type, sourceSpan) {
5072 return new TaggedTemplateExpr(tag, template, type, sourceSpan);
5073 }
5074 function literal(value, type, sourceSpan) {
5075 return new LiteralExpr(value, type, sourceSpan);
5076 }
5077 function localizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan) {
5078 return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan);
5079 }
5080 function isNull(exp) {
5081 return exp instanceof LiteralExpr && exp.value === null;
5082 }
5083 /*
5084 * Serializes a `Tag` into a string.
5085 * Returns a string like " @foo {bar} baz" (note the leading whitespace before `@foo`).
5086 */
5087 function tagToString(tag) {
5088 let out = '';
5089 if (tag.tagName) {
5090 out += ` @${tag.tagName}`;
5091 }
5092 if (tag.text) {
5093 if (tag.text.match(/\/\*|\*\//)) {
5094 throw new Error('JSDoc text cannot contain "/*" and "*/"');
5095 }
5096 out += ' ' + tag.text.replace(/@/g, '\\@');
5097 }
5098 return out;
5099 }
5100 function serializeTags(tags) {
5101 if (tags.length === 0)
5102 return '';
5103 if (tags.length === 1 && tags[0].tagName && !tags[0].text) {
5104 // The JSDOC comment is a single simple tag: e.g `/** @tagname */`.
5105 return `*${tagToString(tags[0])} `;
5106 }
5107 let out = '*\n';
5108 for (const tag of tags) {
5109 out += ' *';
5110 // If the tagToString is multi-line, insert " * " prefixes on lines.
5111 out += tagToString(tag).replace(/\n/g, '\n * ');
5112 out += '\n';
5113 }
5114 out += ' ';
5115 return out;
5116 }
5117
5118 /**
5119 * @license
5120 * Copyright Google LLC All Rights Reserved.
5121 *
5122 * Use of this source code is governed by an MIT-style license that can be
5123 * found in the LICENSE file at https://angular.io/license
5124 */
5125 const CONSTANT_PREFIX = '_c';
5126 /**
5127 * `ConstantPool` tries to reuse literal factories when two or more literals are identical.
5128 * We determine whether literals are identical by creating a key out of their AST using the
5129 * `KeyVisitor`. This constant is used to replace dynamic expressions which can't be safely
5130 * converted into a key. E.g. given an expression `{foo: bar()}`, since we don't know what
5131 * the result of `bar` will be, we create a key that looks like `{foo: <unknown>}`. Note
5132 * that we use a variable, rather than something like `null` in order to avoid collisions.
5133 */
5134 const UNKNOWN_VALUE_KEY = variable('<unknown>');
5135 /**
5136 * Context to use when producing a key.
5137 *
5138 * This ensures we see the constant not the reference variable when producing
5139 * a key.
5140 */
5141 const KEY_CONTEXT = {};
5142 /**
5143 * Generally all primitive values are excluded from the `ConstantPool`, but there is an exclusion
5144 * for strings that reach a certain length threshold. This constant defines the length threshold for
5145 * strings.
5146 */
5147 const POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS = 50;
5148 /**
5149 * A node that is a place-holder that allows the node to be replaced when the actual
5150 * node is known.
5151 *
5152 * This allows the constant pool to change an expression from a direct reference to
5153 * a constant to a shared constant. It returns a fix-up node that is later allowed to
5154 * change the referenced expression.
5155 */
5156 class FixupExpression extends Expression {
5157 constructor(resolved) {
5158 super(resolved.type);
5159 this.resolved = resolved;
5160 this.original = resolved;
5161 }
5162 visitExpression(visitor, context) {
5163 if (context === KEY_CONTEXT) {
5164 // When producing a key we want to traverse the constant not the
5165 // variable used to refer to it.
5166 return this.original.visitExpression(visitor, context);
5167 }
5168 else {
5169 return this.resolved.visitExpression(visitor, context);
5170 }
5171 }
5172 isEquivalent(e) {
5173 return e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved);
5174 }
5175 isConstant() {
5176 return true;
5177 }
5178 fixup(expression) {
5179 this.resolved = expression;
5180 this.shared = true;
5181 }
5182 }
5183 /**
5184 * A constant pool allows a code emitter to share constant in an output context.
5185 *
5186 * The constant pool also supports sharing access to ivy definitions references.
5187 */
5188 class ConstantPool {
5189 constructor(isClosureCompilerEnabled = false) {
5190 this.isClosureCompilerEnabled = isClosureCompilerEnabled;
5191 this.statements = [];
5192 this.literals = new Map();
5193 this.literalFactories = new Map();
5194 this.injectorDefinitions = new Map();
5195 this.directiveDefinitions = new Map();
5196 this.componentDefinitions = new Map();
5197 this.pipeDefinitions = new Map();
5198 this.nextNameIndex = 0;
5199 }
5200 getConstLiteral(literal, forceShared) {
5201 if ((literal instanceof LiteralExpr && !isLongStringLiteral(literal)) ||
5202 literal instanceof FixupExpression) {
5203 // Do no put simple literals into the constant pool or try to produce a constant for a
5204 // reference to a constant.
5205 return literal;
5206 }
5207 const key = this.keyOf(literal);
5208 let fixup = this.literals.get(key);
5209 let newValue = false;
5210 if (!fixup) {
5211 fixup = new FixupExpression(literal);
5212 this.literals.set(key, fixup);
5213 newValue = true;
5214 }
5215 if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
5216 // Replace the expression with a variable
5217 const name = this.freshName();
5218 let definition;
5219 let usage;
5220 if (this.isClosureCompilerEnabled && isLongStringLiteral(literal)) {
5221 // For string literals, Closure will **always** inline the string at
5222 // **all** usages, duplicating it each time. For large strings, this
5223 // unnecessarily bloats bundle size. To work around this restriction, we
5224 // wrap the string in a function, and call that function for each usage.
5225 // This tricks Closure into using inline logic for functions instead of
5226 // string literals. Function calls are only inlined if the body is small
5227 // enough to be worth it. By doing this, very large strings will be
5228 // shared across multiple usages, rather than duplicating the string at
5229 // each usage site.
5230 //
5231 // const myStr = function() { return "very very very long string"; };
5232 // const usage1 = myStr();
5233 // const usage2 = myStr();
5234 definition = variable(name).set(new FunctionExpr([], // Params.
5235 [
5236 // Statements.
5237 new ReturnStatement(literal),
5238 ]));
5239 usage = variable(name).callFn([]);
5240 }
5241 else {
5242 // Just declare and use the variable directly, without a function call
5243 // indirection. This saves a few bytes and avoids an unncessary call.
5244 definition = variable(name).set(literal);
5245 usage = variable(name);
5246 }
5247 this.statements.push(definition.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]));
5248 fixup.fixup(usage);
5249 }
5250 return fixup;
5251 }
5252 getDefinition(type, kind, ctx, forceShared = false) {
5253 const definitions = this.definitionsOf(kind);
5254 let fixup = definitions.get(type);
5255 let newValue = false;
5256 if (!fixup) {
5257 const property = this.propertyNameOf(kind);
5258 fixup = new FixupExpression(ctx.importExpr(type).prop(property));
5259 definitions.set(type, fixup);
5260 newValue = true;
5261 }
5262 if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
5263 const name = this.freshName();
5264 this.statements.push(variable(name).set(fixup.resolved).toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]));
5265 fixup.fixup(variable(name));
5266 }
5267 return fixup;
5268 }
5269 getLiteralFactory(literal) {
5270 // Create a pure function that builds an array of a mix of constant and variable expressions
5271 if (literal instanceof LiteralArrayExpr) {
5272 const argumentsForKey = literal.entries.map(e => e.isConstant() ? e : UNKNOWN_VALUE_KEY);
5273 const key = this.keyOf(literalArr(argumentsForKey));
5274 return this._getLiteralFactory(key, literal.entries, entries => literalArr(entries));
5275 }
5276 else {
5277 const expressionForKey = literalMap(literal.entries.map(e => ({
5278 key: e.key,
5279 value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY,
5280 quoted: e.quoted
5281 })));
5282 const key = this.keyOf(expressionForKey);
5283 return this._getLiteralFactory(key, literal.entries.map(e => e.value), entries => literalMap(entries.map((value, index) => ({
5284 key: literal.entries[index].key,
5285 value,
5286 quoted: literal.entries[index].quoted
5287 }))));
5288 }
5289 }
5290 _getLiteralFactory(key, values, resultMap) {
5291 let literalFactory = this.literalFactories.get(key);
5292 const literalFactoryArguments = values.filter((e => !e.isConstant()));
5293 if (!literalFactory) {
5294 const resultExpressions = values.map((e, index) => e.isConstant() ? this.getConstLiteral(e, true) : variable(`a${index}`));
5295 const parameters = resultExpressions.filter(isVariable).map(e => new FnParam(e.name, DYNAMIC_TYPE));
5296 const pureFunctionDeclaration = fn(parameters, [new ReturnStatement(resultMap(resultExpressions))], INFERRED_TYPE);
5297 const name = this.freshName();
5298 this.statements.push(variable(name).set(pureFunctionDeclaration).toDeclStmt(INFERRED_TYPE, [
5299 StmtModifier.Final
5300 ]));
5301 literalFactory = variable(name);
5302 this.literalFactories.set(key, literalFactory);
5303 }
5304 return { literalFactory, literalFactoryArguments };
5305 }
5306 /**
5307 * Produce a unique name.
5308 *
5309 * The name might be unique among different prefixes if any of the prefixes end in
5310 * a digit so the prefix should be a constant string (not based on user input) and
5311 * must not end in a digit.
5312 */
5313 uniqueName(prefix) {
5314 return `${prefix}${this.nextNameIndex++}`;
5315 }
5316 definitionsOf(kind) {
5317 switch (kind) {
5318 case 2 /* Component */:
5319 return this.componentDefinitions;
5320 case 1 /* Directive */:
5321 return this.directiveDefinitions;
5322 case 0 /* Injector */:
5323 return this.injectorDefinitions;
5324 case 3 /* Pipe */:
5325 return this.pipeDefinitions;
5326 }
5327 }
5328 propertyNameOf(kind) {
5329 switch (kind) {
5330 case 2 /* Component */:
5331 return 'ɵcmp';
5332 case 1 /* Directive */:
5333 return 'ɵdir';
5334 case 0 /* Injector */:
5335 return 'ɵinj';
5336 case 3 /* Pipe */:
5337 return 'ɵpipe';
5338 }
5339 }
5340 freshName() {
5341 return this.uniqueName(CONSTANT_PREFIX);
5342 }
5343 keyOf(expression) {
5344 return expression.visitExpression(new KeyVisitor(), KEY_CONTEXT);
5345 }
5346 }
5347 /**
5348 * Visitor used to determine if 2 expressions are equivalent and can be shared in the
5349 * `ConstantPool`.
5350 *
5351 * When the id (string) generated by the visitor is equal, expressions are considered equivalent.
5352 */
5353 class KeyVisitor {
5354 constructor() {
5355 this.visitWrappedNodeExpr = invalid;
5356 this.visitWriteVarExpr = invalid;
5357 this.visitWriteKeyExpr = invalid;
5358 this.visitWritePropExpr = invalid;
5359 this.visitInvokeMethodExpr = invalid;
5360 this.visitInvokeFunctionExpr = invalid;
5361 this.visitTaggedTemplateExpr = invalid;
5362 this.visitInstantiateExpr = invalid;
5363 this.visitConditionalExpr = invalid;
5364 this.visitNotExpr = invalid;
5365 this.visitAssertNotNullExpr = invalid;
5366 this.visitCastExpr = invalid;
5367 this.visitFunctionExpr = invalid;
5368 this.visitUnaryOperatorExpr = invalid;
5369 this.visitBinaryOperatorExpr = invalid;
5370 this.visitReadPropExpr = invalid;
5371 this.visitReadKeyExpr = invalid;
5372 this.visitCommaExpr = invalid;
5373 this.visitLocalizedString = invalid;
5374 }
5375 visitLiteralExpr(ast) {
5376 return `${typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value}`;
5377 }
5378 visitLiteralArrayExpr(ast, context) {
5379 return `[${ast.entries.map(entry => entry.visitExpression(this, context)).join(',')}]`;
5380 }
5381 visitLiteralMapExpr(ast, context) {
5382 const mapKey = (entry) => {
5383 const quote = entry.quoted ? '"' : '';
5384 return `${quote}${entry.key}${quote}`;
5385 };
5386 const mapEntry = (entry) => `${mapKey(entry)}:${entry.value.visitExpression(this, context)}`;
5387 return `{${ast.entries.map(mapEntry).join(',')}`;
5388 }
5389 visitExternalExpr(ast) {
5390 return ast.value.moduleName ? `EX:${ast.value.moduleName}:${ast.value.name}` :
5391 `EX:${ast.value.runtime.name}`;
5392 }
5393 visitReadVarExpr(node) {
5394 return `VAR:${node.name}`;
5395 }
5396 visitTypeofExpr(node, context) {
5397 return `TYPEOF:${node.expr.visitExpression(this, context)}`;
5398 }
5399 }
5400 function invalid(arg) {
5401 throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
5402 }
5403 function isVariable(e) {
5404 return e instanceof ReadVarExpr;
5405 }
5406 function isLongStringLiteral(expr) {
5407 return expr instanceof LiteralExpr && typeof expr.value === 'string' &&
5408 expr.value.length >= POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS;
5409 }
5410
5411 /**
5412 * @license
5413 * Copyright Google LLC All Rights Reserved.
5414 *
5415 * Use of this source code is governed by an MIT-style license that can be
5416 * found in the LICENSE file at https://angular.io/license
5417 */
5418 const CORE = '@angular/core';
5419 class Identifiers {
5420 }
5421 Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS = {
5422 name: 'ANALYZE_FOR_ENTRY_COMPONENTS',
5423 moduleName: CORE,
5424 };
5425 Identifiers.ElementRef = { name: 'ElementRef', moduleName: CORE };
5426 Identifiers.NgModuleRef = { name: 'NgModuleRef', moduleName: CORE };
5427 Identifiers.ViewContainerRef = { name: 'ViewContainerRef', moduleName: CORE };
5428 Identifiers.ChangeDetectorRef = {
5429 name: 'ChangeDetectorRef',
5430 moduleName: CORE,
5431 };
5432 Identifiers.QueryList = { name: 'QueryList', moduleName: CORE };
5433 Identifiers.TemplateRef = { name: 'TemplateRef', moduleName: CORE };
5434 Identifiers.Renderer2 = { name: 'Renderer2', moduleName: CORE };
5435 Identifiers.CodegenComponentFactoryResolver = {
5436 name: 'ɵCodegenComponentFactoryResolver',
5437 moduleName: CORE,
5438 };
5439 Identifiers.ComponentFactoryResolver = {
5440 name: 'ComponentFactoryResolver',
5441 moduleName: CORE,
5442 };
5443 Identifiers.ComponentFactory = { name: 'ComponentFactory', moduleName: CORE };
5444 Identifiers.ComponentRef = { name: 'ComponentRef', moduleName: CORE };
5445 Identifiers.NgModuleFactory = { name: 'NgModuleFactory', moduleName: CORE };
5446 Identifiers.createModuleFactory = {
5447 name: 'ɵcmf',
5448 moduleName: CORE,
5449 };
5450 Identifiers.moduleDef = {
5451 name: 'ɵmod',
5452 moduleName: CORE,
5453 };
5454 Identifiers.moduleProviderDef = {
5455 name: 'ɵmpd',
5456 moduleName: CORE,
5457 };
5458 Identifiers.RegisterModuleFactoryFn = {
5459 name: 'ɵregisterModuleFactory',
5460 moduleName: CORE,
5461 };
5462 Identifiers.inject = { name: 'ɵɵinject', moduleName: CORE };
5463 Identifiers.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE };
5464 Identifiers.INJECTOR = { name: 'INJECTOR', moduleName: CORE };
5465 Identifiers.Injector = { name: 'Injector', moduleName: CORE };
5466 Identifiers.ɵɵdefineInjectable = { name: 'ɵɵdefineInjectable', moduleName: CORE };
5467 Identifiers.InjectableDef = { name: 'ɵɵInjectableDef', moduleName: CORE };
5468 Identifiers.ViewEncapsulation = {
5469 name: 'ViewEncapsulation',
5470 moduleName: CORE,
5471 };
5472 Identifiers.ChangeDetectionStrategy = {
5473 name: 'ChangeDetectionStrategy',
5474 moduleName: CORE,
5475 };
5476 Identifiers.SecurityContext = {
5477 name: 'SecurityContext',
5478 moduleName: CORE,
5479 };
5480 Identifiers.LOCALE_ID = { name: 'LOCALE_ID', moduleName: CORE };
5481 Identifiers.TRANSLATIONS_FORMAT = {
5482 name: 'TRANSLATIONS_FORMAT',
5483 moduleName: CORE,
5484 };
5485 Identifiers.inlineInterpolate = {
5486 name: 'ɵinlineInterpolate',
5487 moduleName: CORE,
5488 };
5489 Identifiers.interpolate = { name: 'ɵinterpolate', moduleName: CORE };
5490 Identifiers.EMPTY_ARRAY = { name: 'ɵEMPTY_ARRAY', moduleName: CORE };
5491 Identifiers.EMPTY_MAP = { name: 'ɵEMPTY_MAP', moduleName: CORE };
5492 Identifiers.Renderer = { name: 'Renderer', moduleName: CORE };
5493 Identifiers.viewDef = { name: 'ɵvid', moduleName: CORE };
5494 Identifiers.elementDef = { name: 'ɵeld', moduleName: CORE };
5495 Identifiers.anchorDef = { name: 'ɵand', moduleName: CORE };
5496 Identifiers.textDef = { name: 'ɵted', moduleName: CORE };
5497 Identifiers.directiveDef = { name: 'ɵdid', moduleName: CORE };
5498 Identifiers.providerDef = { name: 'ɵprd', moduleName: CORE };
5499 Identifiers.queryDef = { name: 'ɵqud', moduleName: CORE };
5500 Identifiers.pureArrayDef = { name: 'ɵpad', moduleName: CORE };
5501 Identifiers.pureObjectDef = { name: 'ɵpod', moduleName: CORE };
5502 Identifiers.purePipeDef = { name: 'ɵppd', moduleName: CORE };
5503 Identifiers.pipeDef = { name: 'ɵpid', moduleName: CORE };
5504 Identifiers.nodeValue = { name: 'ɵnov', moduleName: CORE };
5505 Identifiers.ngContentDef = { name: 'ɵncd', moduleName: CORE };
5506 Identifiers.unwrapValue = { name: 'ɵunv', moduleName: CORE };
5507 Identifiers.createRendererType2 = { name: 'ɵcrt', moduleName: CORE };
5508 // type only
5509 Identifiers.RendererType2 = {
5510 name: 'RendererType2',
5511 moduleName: CORE,
5512 };
5513 // type only
5514 Identifiers.ViewDefinition = {
5515 name: 'ɵViewDefinition',
5516 moduleName: CORE,
5517 };
5518 Identifiers.createComponentFactory = { name: 'ɵccf', moduleName: CORE };
5519 Identifiers.setClassMetadata = { name: 'ɵsetClassMetadata', moduleName: CORE };
5520
5521 /**
5522 * @license
5523 * Copyright Google LLC All Rights Reserved.
5524 *
5525 * Use of this source code is governed by an MIT-style license that can be
5526 * found in the LICENSE file at https://angular.io/license
5527 */
5528 /**
5529 * A token representing the a reference to a static type.
5530 *
5531 * This token is unique for a filePath and name and can be used as a hash table key.
5532 */
5533 class StaticSymbol {
5534 constructor(filePath, name, members) {
5535 this.filePath = filePath;
5536 this.name = name;
5537 this.members = members;
5538 }
5539 assertNoMembers() {
5540 if (this.members.length) {
5541 throw new Error(`Illegal state: symbol without members expected, but got ${JSON.stringify(this)}.`);
5542 }
5543 }
5544 }
5545
5546 /**
5547 * @license
5548 * Copyright Google LLC All Rights Reserved.
5549 *
5550 * Use of this source code is governed by an MIT-style license that can be
5551 * found in the LICENSE file at https://angular.io/license
5552 */
5553 const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
5554 function dashCaseToCamelCase(input) {
5555 return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase());
5556 }
5557 function splitAtColon(input, defaultValues) {
5558 return _splitAt(input, ':', defaultValues);
5559 }
5560 function splitAtPeriod(input, defaultValues) {
5561 return _splitAt(input, '.', defaultValues);
5562 }
5563 function _splitAt(input, character, defaultValues) {
5564 const characterIndex = input.indexOf(character);
5565 if (characterIndex == -1)
5566 return defaultValues;
5567 return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
5568 }
5569 function error(msg) {
5570 throw new Error(`Internal Error: ${msg}`);
5571 }
5572 function utf8Encode(str) {
5573 let encoded = [];
5574 for (let index = 0; index < str.length; index++) {
5575 let codePoint = str.charCodeAt(index);
5576 // decode surrogate
5577 // see https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
5578 if (codePoint >= 0xd800 && codePoint <= 0xdbff && str.length > (index + 1)) {
5579 const low = str.charCodeAt(index + 1);
5580 if (low >= 0xdc00 && low <= 0xdfff) {
5581 index++;
5582 codePoint = ((codePoint - 0xd800) << 10) + low - 0xdc00 + 0x10000;
5583 }
5584 }
5585 if (codePoint <= 0x7f) {
5586 encoded.push(codePoint);
5587 }
5588 else if (codePoint <= 0x7ff) {
5589 encoded.push(((codePoint >> 6) & 0x1F) | 0xc0, (codePoint & 0x3f) | 0x80);
5590 }
5591 else if (codePoint <= 0xffff) {
5592 encoded.push((codePoint >> 12) | 0xe0, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
5593 }
5594 else if (codePoint <= 0x1fffff) {
5595 encoded.push(((codePoint >> 18) & 0x07) | 0xf0, ((codePoint >> 12) & 0x3f) | 0x80, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
5596 }
5597 }
5598 return encoded;
5599 }
5600 function stringify$1(token) {
5601 if (typeof token === 'string') {
5602 return token;
5603 }
5604 if (Array.isArray(token)) {
5605 return '[' + token.map(stringify$1).join(', ') + ']';
5606 }
5607 if (token == null) {
5608 return '' + token;
5609 }
5610 if (token.overriddenName) {
5611 return `${token.overriddenName}`;
5612 }
5613 if (token.name) {
5614 return `${token.name}`;
5615 }
5616 if (!token.toString) {
5617 return 'object';
5618 }
5619 // WARNING: do not try to `JSON.stringify(token)` here
5620 // see https://github.com/angular/angular/issues/23440
5621 const res = token.toString();
5622 if (res == null) {
5623 return '' + res;
5624 }
5625 const newLineIndex = res.indexOf('\n');
5626 return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
5627 }
5628 class Version {
5629 constructor(full) {
5630 this.full = full;
5631 const splits = full.split('.');
5632 this.major = splits[0];
5633 this.minor = splits[1];
5634 this.patch = splits.slice(2).join('.');
5635 }
5636 }
5637 const __window = typeof window !== 'undefined' && window;
5638 const __self = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
5639 self instanceof WorkerGlobalScope && self;
5640 const __global = typeof global !== 'undefined' && global;
5641 // Check __global first, because in Node tests both __global and __window may be defined and _global
5642 // should be __global in that case.
5643 const _global = __global || __window || __self;
5644 function newArray(size, value) {
5645 const list = [];
5646 for (let i = 0; i < size; i++) {
5647 list.push(value);
5648 }
5649 return list;
5650 }
5651 /**
5652 * Partitions a given array into 2 arrays, based on a boolean value returned by the condition
5653 * function.
5654 *
5655 * @param arr Input array that should be partitioned
5656 * @param conditionFn Condition function that is called for each item in a given array and returns a
5657 * boolean value.
5658 */
5659 function partitionArray(arr, conditionFn) {
5660 const truthy = [];
5661 const falsy = [];
5662 for (const item of arr) {
5663 (conditionFn(item) ? truthy : falsy).push(item);
5664 }
5665 return [truthy, falsy];
5666 }
5667
5668 /**
5669 * @license
5670 * Copyright Google LLC All Rights Reserved.
5671 *
5672 * Use of this source code is governed by an MIT-style license that can be
5673 * found in the LICENSE file at https://angular.io/license
5674 */
5675 function sanitizeIdentifier(name) {
5676 return name.replace(/\W/g, '_');
5677 }
5678 let _anonymousTypeIndex = 0;
5679 function identifierName(compileIdentifier) {
5680 if (!compileIdentifier || !compileIdentifier.reference) {
5681 return null;
5682 }
5683 const ref = compileIdentifier.reference;
5684 if (ref instanceof StaticSymbol) {
5685 return ref.name;
5686 }
5687 if (ref['__anonymousType']) {
5688 return ref['__anonymousType'];
5689 }
5690 let identifier = stringify$1(ref);
5691 if (identifier.indexOf('(') >= 0) {
5692 // case: anonymous functions!
5693 identifier = `anonymous_${_anonymousTypeIndex++}`;
5694 ref['__anonymousType'] = identifier;
5695 }
5696 else {
5697 identifier = sanitizeIdentifier(identifier);
5698 }
5699 return identifier;
5700 }
5701 var CompileSummaryKind;
5702 (function (CompileSummaryKind) {
5703 CompileSummaryKind[CompileSummaryKind["Pipe"] = 0] = "Pipe";
5704 CompileSummaryKind[CompileSummaryKind["Directive"] = 1] = "Directive";
5705 CompileSummaryKind[CompileSummaryKind["NgModule"] = 2] = "NgModule";
5706 CompileSummaryKind[CompileSummaryKind["Injectable"] = 3] = "Injectable";
5707 })(CompileSummaryKind || (CompileSummaryKind = {}));
5708 function flatten(list) {
5709 return list.reduce((flat, item) => {
5710 const flatItem = Array.isArray(item) ? flatten(item) : item;
5711 return flat.concat(flatItem);
5712 }, []);
5713 }
5714
5715 /**
5716 * @license
5717 * Copyright Google LLC All Rights Reserved.
5718 *
5719 * Use of this source code is governed by an MIT-style license that can be
5720 * found in the LICENSE file at https://angular.io/license
5721 */
5722 const CORE$1 = '@angular/core';
5723 class Identifiers$1 {
5724 }
5725 /* Methods */
5726 Identifiers$1.NEW_METHOD = 'factory';
5727 Identifiers$1.TRANSFORM_METHOD = 'transform';
5728 Identifiers$1.PATCH_DEPS = 'patchedDeps';
5729 Identifiers$1.core = { name: null, moduleName: CORE$1 };
5730 /* Instructions */
5731 Identifiers$1.namespaceHTML = { name: 'ɵɵnamespaceHTML', moduleName: CORE$1 };
5732 Identifiers$1.namespaceMathML = { name: 'ɵɵnamespaceMathML', moduleName: CORE$1 };
5733 Identifiers$1.namespaceSVG = { name: 'ɵɵnamespaceSVG', moduleName: CORE$1 };
5734 Identifiers$1.element = { name: 'ɵɵelement', moduleName: CORE$1 };
5735 Identifiers$1.elementStart = { name: 'ɵɵelementStart', moduleName: CORE$1 };
5736 Identifiers$1.elementEnd = { name: 'ɵɵelementEnd', moduleName: CORE$1 };
5737 Identifiers$1.advance = { name: 'ɵɵadvance', moduleName: CORE$1 };
5738 Identifiers$1.syntheticHostProperty = { name: 'ɵɵsyntheticHostProperty', moduleName: CORE$1 };
5739 Identifiers$1.syntheticHostListener = { name: 'ɵɵsyntheticHostListener', moduleName: CORE$1 };
5740 Identifiers$1.attribute = { name: 'ɵɵattribute', moduleName: CORE$1 };
5741 Identifiers$1.attributeInterpolate1 = { name: 'ɵɵattributeInterpolate1', moduleName: CORE$1 };
5742 Identifiers$1.attributeInterpolate2 = { name: 'ɵɵattributeInterpolate2', moduleName: CORE$1 };
5743 Identifiers$1.attributeInterpolate3 = { name: 'ɵɵattributeInterpolate3', moduleName: CORE$1 };
5744 Identifiers$1.attributeInterpolate4 = { name: 'ɵɵattributeInterpolate4', moduleName: CORE$1 };
5745 Identifiers$1.attributeInterpolate5 = { name: 'ɵɵattributeInterpolate5', moduleName: CORE$1 };
5746 Identifiers$1.attributeInterpolate6 = { name: 'ɵɵattributeInterpolate6', moduleName: CORE$1 };
5747 Identifiers$1.attributeInterpolate7 = { name: 'ɵɵattributeInterpolate7', moduleName: CORE$1 };
5748 Identifiers$1.attributeInterpolate8 = { name: 'ɵɵattributeInterpolate8', moduleName: CORE$1 };
5749 Identifiers$1.attributeInterpolateV = { name: 'ɵɵattributeInterpolateV', moduleName: CORE$1 };
5750 Identifiers$1.classProp = { name: 'ɵɵclassProp', moduleName: CORE$1 };
5751 Identifiers$1.elementContainerStart = { name: 'ɵɵelementContainerStart', moduleName: CORE$1 };
5752 Identifiers$1.elementContainerEnd = { name: 'ɵɵelementContainerEnd', moduleName: CORE$1 };
5753 Identifiers$1.elementContainer = { name: 'ɵɵelementContainer', moduleName: CORE$1 };
5754 Identifiers$1.styleMap = { name: 'ɵɵstyleMap', moduleName: CORE$1 };
5755 Identifiers$1.styleMapInterpolate1 = { name: 'ɵɵstyleMapInterpolate1', moduleName: CORE$1 };
5756 Identifiers$1.styleMapInterpolate2 = { name: 'ɵɵstyleMapInterpolate2', moduleName: CORE$1 };
5757 Identifiers$1.styleMapInterpolate3 = { name: 'ɵɵstyleMapInterpolate3', moduleName: CORE$1 };
5758 Identifiers$1.styleMapInterpolate4 = { name: 'ɵɵstyleMapInterpolate4', moduleName: CORE$1 };
5759 Identifiers$1.styleMapInterpolate5 = { name: 'ɵɵstyleMapInterpolate5', moduleName: CORE$1 };
5760 Identifiers$1.styleMapInterpolate6 = { name: 'ɵɵstyleMapInterpolate6', moduleName: CORE$1 };
5761 Identifiers$1.styleMapInterpolate7 = { name: 'ɵɵstyleMapInterpolate7', moduleName: CORE$1 };
5762 Identifiers$1.styleMapInterpolate8 = { name: 'ɵɵstyleMapInterpolate8', moduleName: CORE$1 };
5763 Identifiers$1.styleMapInterpolateV = { name: 'ɵɵstyleMapInterpolateV', moduleName: CORE$1 };
5764 Identifiers$1.classMap = { name: 'ɵɵclassMap', moduleName: CORE$1 };
5765 Identifiers$1.classMapInterpolate1 = { name: 'ɵɵclassMapInterpolate1', moduleName: CORE$1 };
5766 Identifiers$1.classMapInterpolate2 = { name: 'ɵɵclassMapInterpolate2', moduleName: CORE$1 };
5767 Identifiers$1.classMapInterpolate3 = { name: 'ɵɵclassMapInterpolate3', moduleName: CORE$1 };
5768 Identifiers$1.classMapInterpolate4 = { name: 'ɵɵclassMapInterpolate4', moduleName: CORE$1 };
5769 Identifiers$1.classMapInterpolate5 = { name: 'ɵɵclassMapInterpolate5', moduleName: CORE$1 };
5770 Identifiers$1.classMapInterpolate6 = { name: 'ɵɵclassMapInterpolate6', moduleName: CORE$1 };
5771 Identifiers$1.classMapInterpolate7 = { name: 'ɵɵclassMapInterpolate7', moduleName: CORE$1 };
5772 Identifiers$1.classMapInterpolate8 = { name: 'ɵɵclassMapInterpolate8', moduleName: CORE$1 };
5773 Identifiers$1.classMapInterpolateV = { name: 'ɵɵclassMapInterpolateV', moduleName: CORE$1 };
5774 Identifiers$1.styleProp = { name: 'ɵɵstyleProp', moduleName: CORE$1 };
5775 Identifiers$1.stylePropInterpolate1 = { name: 'ɵɵstylePropInterpolate1', moduleName: CORE$1 };
5776 Identifiers$1.stylePropInterpolate2 = { name: 'ɵɵstylePropInterpolate2', moduleName: CORE$1 };
5777 Identifiers$1.stylePropInterpolate3 = { name: 'ɵɵstylePropInterpolate3', moduleName: CORE$1 };
5778 Identifiers$1.stylePropInterpolate4 = { name: 'ɵɵstylePropInterpolate4', moduleName: CORE$1 };
5779 Identifiers$1.stylePropInterpolate5 = { name: 'ɵɵstylePropInterpolate5', moduleName: CORE$1 };
5780 Identifiers$1.stylePropInterpolate6 = { name: 'ɵɵstylePropInterpolate6', moduleName: CORE$1 };
5781 Identifiers$1.stylePropInterpolate7 = { name: 'ɵɵstylePropInterpolate7', moduleName: CORE$1 };
5782 Identifiers$1.stylePropInterpolate8 = { name: 'ɵɵstylePropInterpolate8', moduleName: CORE$1 };
5783 Identifiers$1.stylePropInterpolateV = { name: 'ɵɵstylePropInterpolateV', moduleName: CORE$1 };
5784 Identifiers$1.nextContext = { name: 'ɵɵnextContext', moduleName: CORE$1 };
5785 Identifiers$1.templateCreate = { name: 'ɵɵtemplate', moduleName: CORE$1 };
5786 Identifiers$1.text = { name: 'ɵɵtext', moduleName: CORE$1 };
5787 Identifiers$1.enableBindings = { name: 'ɵɵenableBindings', moduleName: CORE$1 };
5788 Identifiers$1.disableBindings = { name: 'ɵɵdisableBindings', moduleName: CORE$1 };
5789 Identifiers$1.getCurrentView = { name: 'ɵɵgetCurrentView', moduleName: CORE$1 };
5790 Identifiers$1.textInterpolate = { name: 'ɵɵtextInterpolate', moduleName: CORE$1 };
5791 Identifiers$1.textInterpolate1 = { name: 'ɵɵtextInterpolate1', moduleName: CORE$1 };
5792 Identifiers$1.textInterpolate2 = { name: 'ɵɵtextInterpolate2', moduleName: CORE$1 };
5793 Identifiers$1.textInterpolate3 = { name: 'ɵɵtextInterpolate3', moduleName: CORE$1 };
5794 Identifiers$1.textInterpolate4 = { name: 'ɵɵtextInterpolate4', moduleName: CORE$1 };
5795 Identifiers$1.textInterpolate5 = { name: 'ɵɵtextInterpolate5', moduleName: CORE$1 };
5796 Identifiers$1.textInterpolate6 = { name: 'ɵɵtextInterpolate6', moduleName: CORE$1 };
5797 Identifiers$1.textInterpolate7 = { name: 'ɵɵtextInterpolate7', moduleName: CORE$1 };
5798 Identifiers$1.textInterpolate8 = { name: 'ɵɵtextInterpolate8', moduleName: CORE$1 };
5799 Identifiers$1.textInterpolateV = { name: 'ɵɵtextInterpolateV', moduleName: CORE$1 };
5800 Identifiers$1.restoreView = { name: 'ɵɵrestoreView', moduleName: CORE$1 };
5801 Identifiers$1.pureFunction0 = { name: 'ɵɵpureFunction0', moduleName: CORE$1 };
5802 Identifiers$1.pureFunction1 = { name: 'ɵɵpureFunction1', moduleName: CORE$1 };
5803 Identifiers$1.pureFunction2 = { name: 'ɵɵpureFunction2', moduleName: CORE$1 };
5804 Identifiers$1.pureFunction3 = { name: 'ɵɵpureFunction3', moduleName: CORE$1 };
5805 Identifiers$1.pureFunction4 = { name: 'ɵɵpureFunction4', moduleName: CORE$1 };
5806 Identifiers$1.pureFunction5 = { name: 'ɵɵpureFunction5', moduleName: CORE$1 };
5807 Identifiers$1.pureFunction6 = { name: 'ɵɵpureFunction6', moduleName: CORE$1 };
5808 Identifiers$1.pureFunction7 = { name: 'ɵɵpureFunction7', moduleName: CORE$1 };
5809 Identifiers$1.pureFunction8 = { name: 'ɵɵpureFunction8', moduleName: CORE$1 };
5810 Identifiers$1.pureFunctionV = { name: 'ɵɵpureFunctionV', moduleName: CORE$1 };
5811 Identifiers$1.pipeBind1 = { name: 'ɵɵpipeBind1', moduleName: CORE$1 };
5812 Identifiers$1.pipeBind2 = { name: 'ɵɵpipeBind2', moduleName: CORE$1 };
5813 Identifiers$1.pipeBind3 = { name: 'ɵɵpipeBind3', moduleName: CORE$1 };
5814 Identifiers$1.pipeBind4 = { name: 'ɵɵpipeBind4', moduleName: CORE$1 };
5815 Identifiers$1.pipeBindV = { name: 'ɵɵpipeBindV', moduleName: CORE$1 };
5816 Identifiers$1.hostProperty = { name: 'ɵɵhostProperty', moduleName: CORE$1 };
5817 Identifiers$1.property = { name: 'ɵɵproperty', moduleName: CORE$1 };
5818 Identifiers$1.propertyInterpolate = { name: 'ɵɵpropertyInterpolate', moduleName: CORE$1 };
5819 Identifiers$1.propertyInterpolate1 = { name: 'ɵɵpropertyInterpolate1', moduleName: CORE$1 };
5820 Identifiers$1.propertyInterpolate2 = { name: 'ɵɵpropertyInterpolate2', moduleName: CORE$1 };
5821 Identifiers$1.propertyInterpolate3 = { name: 'ɵɵpropertyInterpolate3', moduleName: CORE$1 };
5822 Identifiers$1.propertyInterpolate4 = { name: 'ɵɵpropertyInterpolate4', moduleName: CORE$1 };
5823 Identifiers$1.propertyInterpolate5 = { name: 'ɵɵpropertyInterpolate5', moduleName: CORE$1 };
5824 Identifiers$1.propertyInterpolate6 = { name: 'ɵɵpropertyInterpolate6', moduleName: CORE$1 };
5825 Identifiers$1.propertyInterpolate7 = { name: 'ɵɵpropertyInterpolate7', moduleName: CORE$1 };
5826 Identifiers$1.propertyInterpolate8 = { name: 'ɵɵpropertyInterpolate8', moduleName: CORE$1 };
5827 Identifiers$1.propertyInterpolateV = { name: 'ɵɵpropertyInterpolateV', moduleName: CORE$1 };
5828 Identifiers$1.i18n = { name: 'ɵɵi18n', moduleName: CORE$1 };
5829 Identifiers$1.i18nAttributes = { name: 'ɵɵi18nAttributes', moduleName: CORE$1 };
5830 Identifiers$1.i18nExp = { name: 'ɵɵi18nExp', moduleName: CORE$1 };
5831 Identifiers$1.i18nStart = { name: 'ɵɵi18nStart', moduleName: CORE$1 };
5832 Identifiers$1.i18nEnd = { name: 'ɵɵi18nEnd', moduleName: CORE$1 };
5833 Identifiers$1.i18nApply = { name: 'ɵɵi18nApply', moduleName: CORE$1 };
5834 Identifiers$1.i18nPostprocess = { name: 'ɵɵi18nPostprocess', moduleName: CORE$1 };
5835 Identifiers$1.pipe = { name: 'ɵɵpipe', moduleName: CORE$1 };
5836 Identifiers$1.projection = { name: 'ɵɵprojection', moduleName: CORE$1 };
5837 Identifiers$1.projectionDef = { name: 'ɵɵprojectionDef', moduleName: CORE$1 };
5838 Identifiers$1.reference = { name: 'ɵɵreference', moduleName: CORE$1 };
5839 Identifiers$1.inject = { name: 'ɵɵinject', moduleName: CORE$1 };
5840 Identifiers$1.injectAttribute = { name: 'ɵɵinjectAttribute', moduleName: CORE$1 };
5841 Identifiers$1.injectPipeChangeDetectorRef = { name: 'ɵɵinjectPipeChangeDetectorRef', moduleName: CORE$1 };
5842 Identifiers$1.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE$1 };
5843 Identifiers$1.invalidFactory = { name: 'ɵɵinvalidFactory', moduleName: CORE$1 };
5844 Identifiers$1.invalidFactoryDep = { name: 'ɵɵinvalidFactoryDep', moduleName: CORE$1 };
5845 Identifiers$1.templateRefExtractor = { name: 'ɵɵtemplateRefExtractor', moduleName: CORE$1 };
5846 Identifiers$1.forwardRef = { name: 'forwardRef', moduleName: CORE$1 };
5847 Identifiers$1.resolveForwardRef = { name: 'resolveForwardRef', moduleName: CORE$1 };
5848 Identifiers$1.resolveWindow = { name: 'ɵɵresolveWindow', moduleName: CORE$1 };
5849 Identifiers$1.resolveDocument = { name: 'ɵɵresolveDocument', moduleName: CORE$1 };
5850 Identifiers$1.resolveBody = { name: 'ɵɵresolveBody', moduleName: CORE$1 };
5851 Identifiers$1.defineComponent = { name: 'ɵɵdefineComponent', moduleName: CORE$1 };
5852 Identifiers$1.declareComponent = { name: 'ɵɵngDeclareComponent', moduleName: CORE$1 };
5853 Identifiers$1.setComponentScope = { name: 'ɵɵsetComponentScope', moduleName: CORE$1 };
5854 Identifiers$1.ChangeDetectionStrategy = {
5855 name: 'ChangeDetectionStrategy',
5856 moduleName: CORE$1,
5857 };
5858 Identifiers$1.ViewEncapsulation = {
5859 name: 'ViewEncapsulation',
5860 moduleName: CORE$1,
5861 };
5862 Identifiers$1.ComponentDefWithMeta = {
5863 name: 'ɵɵComponentDefWithMeta',
5864 moduleName: CORE$1,
5865 };
5866 Identifiers$1.FactoryDef = {
5867 name: 'ɵɵFactoryDef',
5868 moduleName: CORE$1,
5869 };
5870 Identifiers$1.defineDirective = { name: 'ɵɵdefineDirective', moduleName: CORE$1 };
5871 Identifiers$1.declareDirective = { name: 'ɵɵngDeclareDirective', moduleName: CORE$1 };
5872 Identifiers$1.DirectiveDefWithMeta = {
5873 name: 'ɵɵDirectiveDefWithMeta',
5874 moduleName: CORE$1,
5875 };
5876 Identifiers$1.InjectorDef = {
5877 name: 'ɵɵInjectorDef',
5878 moduleName: CORE$1,
5879 };
5880 Identifiers$1.defineInjector = {
5881 name: 'ɵɵdefineInjector',
5882 moduleName: CORE$1,
5883 };
5884 Identifiers$1.NgModuleDefWithMeta = {
5885 name: 'ɵɵNgModuleDefWithMeta',
5886 moduleName: CORE$1,
5887 };
5888 Identifiers$1.ModuleWithProviders = {
5889 name: 'ModuleWithProviders',
5890 moduleName: CORE$1,
5891 };
5892 Identifiers$1.defineNgModule = { name: 'ɵɵdefineNgModule', moduleName: CORE$1 };
5893 Identifiers$1.setNgModuleScope = { name: 'ɵɵsetNgModuleScope', moduleName: CORE$1 };
5894 Identifiers$1.PipeDefWithMeta = { name: 'ɵɵPipeDefWithMeta', moduleName: CORE$1 };
5895 Identifiers$1.definePipe = { name: 'ɵɵdefinePipe', moduleName: CORE$1 };
5896 Identifiers$1.declarePipe = { name: 'ɵɵngDeclarePipe', moduleName: CORE$1 };
5897 Identifiers$1.queryRefresh = { name: 'ɵɵqueryRefresh', moduleName: CORE$1 };
5898 Identifiers$1.viewQuery = { name: 'ɵɵviewQuery', moduleName: CORE$1 };
5899 Identifiers$1.loadQuery = { name: 'ɵɵloadQuery', moduleName: CORE$1 };
5900 Identifiers$1.contentQuery = { name: 'ɵɵcontentQuery', moduleName: CORE$1 };
5901 Identifiers$1.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE$1 };
5902 Identifiers$1.InheritDefinitionFeature = { name: 'ɵɵInheritDefinitionFeature', moduleName: CORE$1 };
5903 Identifiers$1.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE$1 };
5904 Identifiers$1.ProvidersFeature = { name: 'ɵɵProvidersFeature', moduleName: CORE$1 };
5905 Identifiers$1.listener = { name: 'ɵɵlistener', moduleName: CORE$1 };
5906 Identifiers$1.getFactoryOf = {
5907 name: 'ɵɵgetFactoryOf',
5908 moduleName: CORE$1,
5909 };
5910 Identifiers$1.getInheritedFactory = {
5911 name: 'ɵɵgetInheritedFactory',
5912 moduleName: CORE$1,
5913 };
5914 // sanitization-related functions
5915 Identifiers$1.sanitizeHtml = { name: 'ɵɵsanitizeHtml', moduleName: CORE$1 };
5916 Identifiers$1.sanitizeStyle = { name: 'ɵɵsanitizeStyle', moduleName: CORE$1 };
5917 Identifiers$1.sanitizeResourceUrl = { name: 'ɵɵsanitizeResourceUrl', moduleName: CORE$1 };
5918 Identifiers$1.sanitizeScript = { name: 'ɵɵsanitizeScript', moduleName: CORE$1 };
5919 Identifiers$1.sanitizeUrl = { name: 'ɵɵsanitizeUrl', moduleName: CORE$1 };
5920 Identifiers$1.sanitizeUrlOrResourceUrl = { name: 'ɵɵsanitizeUrlOrResourceUrl', moduleName: CORE$1 };
5921 Identifiers$1.trustConstantHtml = { name: 'ɵɵtrustConstantHtml', moduleName: CORE$1 };
5922 Identifiers$1.trustConstantResourceUrl = { name: 'ɵɵtrustConstantResourceUrl', moduleName: CORE$1 };
5923
5924 /**
5925 * @license
5926 * Copyright Google LLC All Rights Reserved.
5927 *
5928 * Use of this source code is governed by an MIT-style license that can be
5929 * found in the LICENSE file at https://angular.io/license
5930 */
5931 // https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
5932 const VERSION = 3;
5933 const JS_B64_PREFIX = '# sourceMappingURL=data:application/json;base64,';
5934 class SourceMapGenerator {
5935 constructor(file = null) {
5936 this.file = file;
5937 this.sourcesContent = new Map();
5938 this.lines = [];
5939 this.lastCol0 = 0;
5940 this.hasMappings = false;
5941 }
5942 // The content is `null` when the content is expected to be loaded using the URL
5943 addSource(url, content = null) {
5944 if (!this.sourcesContent.has(url)) {
5945 this.sourcesContent.set(url, content);
5946 }
5947 return this;
5948 }
5949 addLine() {
5950 this.lines.push([]);
5951 this.lastCol0 = 0;
5952 return this;
5953 }
5954 addMapping(col0, sourceUrl, sourceLine0, sourceCol0) {
5955 if (!this.currentLine) {
5956 throw new Error(`A line must be added before mappings can be added`);
5957 }
5958 if (sourceUrl != null && !this.sourcesContent.has(sourceUrl)) {
5959 throw new Error(`Unknown source file "${sourceUrl}"`);
5960 }
5961 if (col0 == null) {
5962 throw new Error(`The column in the generated code must be provided`);
5963 }
5964 if (col0 < this.lastCol0) {
5965 throw new Error(`Mapping should be added in output order`);
5966 }
5967 if (sourceUrl && (sourceLine0 == null || sourceCol0 == null)) {
5968 throw new Error(`The source location must be provided when a source url is provided`);
5969 }
5970 this.hasMappings = true;
5971 this.lastCol0 = col0;
5972 this.currentLine.push({ col0, sourceUrl, sourceLine0, sourceCol0 });
5973 return this;
5974 }
5975 /**
5976 * @internal strip this from published d.ts files due to
5977 * https://github.com/microsoft/TypeScript/issues/36216
5978 */
5979 get currentLine() {
5980 return this.lines.slice(-1)[0];
5981 }
5982 toJSON() {
5983 if (!this.hasMappings) {
5984 return null;
5985 }
5986 const sourcesIndex = new Map();
5987 const sources = [];
5988 const sourcesContent = [];
5989 Array.from(this.sourcesContent.keys()).forEach((url, i) => {
5990 sourcesIndex.set(url, i);
5991 sources.push(url);
5992 sourcesContent.push(this.sourcesContent.get(url) || null);
5993 });
5994 let mappings = '';
5995 let lastCol0 = 0;
5996 let lastSourceIndex = 0;
5997 let lastSourceLine0 = 0;
5998 let lastSourceCol0 = 0;
5999 this.lines.forEach(segments => {
6000 lastCol0 = 0;
6001 mappings += segments
6002 .map(segment => {
6003 // zero-based starting column of the line in the generated code
6004 let segAsStr = toBase64VLQ(segment.col0 - lastCol0);
6005 lastCol0 = segment.col0;
6006 if (segment.sourceUrl != null) {
6007 // zero-based index into the “sources” list
6008 segAsStr +=
6009 toBase64VLQ(sourcesIndex.get(segment.sourceUrl) - lastSourceIndex);
6010 lastSourceIndex = sourcesIndex.get(segment.sourceUrl);
6011 // the zero-based starting line in the original source
6012 segAsStr += toBase64VLQ(segment.sourceLine0 - lastSourceLine0);
6013 lastSourceLine0 = segment.sourceLine0;
6014 // the zero-based starting column in the original source
6015 segAsStr += toBase64VLQ(segment.sourceCol0 - lastSourceCol0);
6016 lastSourceCol0 = segment.sourceCol0;
6017 }
6018 return segAsStr;
6019 })
6020 .join(',');
6021 mappings += ';';
6022 });
6023 mappings = mappings.slice(0, -1);
6024 return {
6025 'file': this.file || '',
6026 'version': VERSION,
6027 'sourceRoot': '',
6028 'sources': sources,
6029 'sourcesContent': sourcesContent,
6030 'mappings': mappings,
6031 };
6032 }
6033 toJsComment() {
6034 return this.hasMappings ? '//' + JS_B64_PREFIX + toBase64String(JSON.stringify(this, null, 0)) :
6035 '';
6036 }
6037 }
6038 function toBase64String(value) {
6039 let b64 = '';
6040 const encoded = utf8Encode(value);
6041 for (let i = 0; i < encoded.length;) {
6042 const i1 = encoded[i++];
6043 const i2 = i < encoded.length ? encoded[i++] : null;
6044 const i3 = i < encoded.length ? encoded[i++] : null;
6045 b64 += toBase64Digit(i1 >> 2);
6046 b64 += toBase64Digit(((i1 & 3) << 4) | (i2 === null ? 0 : i2 >> 4));
6047 b64 += i2 === null ? '=' : toBase64Digit(((i2 & 15) << 2) | (i3 === null ? 0 : i3 >> 6));
6048 b64 += i2 === null || i3 === null ? '=' : toBase64Digit(i3 & 63);
6049 }
6050 return b64;
6051 }
6052 function toBase64VLQ(value) {
6053 value = value < 0 ? ((-value) << 1) + 1 : value << 1;
6054 let out = '';
6055 do {
6056 let digit = value & 31;
6057 value = value >> 5;
6058 if (value > 0) {
6059 digit = digit | 32;
6060 }
6061 out += toBase64Digit(digit);
6062 } while (value > 0);
6063 return out;
6064 }
6065 const B64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
6066 function toBase64Digit(value) {
6067 if (value < 0 || value >= 64) {
6068 throw new Error(`Can only encode value in the range [0, 63]`);
6069 }
6070 return B64_DIGITS[value];
6071 }
6072
6073 /**
6074 * @license
6075 * Copyright Google LLC All Rights Reserved.
6076 *
6077 * Use of this source code is governed by an MIT-style license that can be
6078 * found in the LICENSE file at https://angular.io/license
6079 */
6080 const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
6081 const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
6082 const _INDENT_WITH = ' ';
6083 const CATCH_ERROR_VAR$1 = variable('error', null, null);
6084 const CATCH_STACK_VAR$1 = variable('stack', null, null);
6085 class _EmittedLine {
6086 constructor(indent) {
6087 this.indent = indent;
6088 this.partsLength = 0;
6089 this.parts = [];
6090 this.srcSpans = [];
6091 }
6092 }
6093 class EmitterVisitorContext {
6094 constructor(_indent) {
6095 this._indent = _indent;
6096 this._classes = [];
6097 this._preambleLineCount = 0;
6098 this._lines = [new _EmittedLine(_indent)];
6099 }
6100 static createRoot() {
6101 return new EmitterVisitorContext(0);
6102 }
6103 /**
6104 * @internal strip this from published d.ts files due to
6105 * https://github.com/microsoft/TypeScript/issues/36216
6106 */
6107 get _currentLine() {
6108 return this._lines[this._lines.length - 1];
6109 }
6110 println(from, lastPart = '') {
6111 this.print(from || null, lastPart, true);
6112 }
6113 lineIsEmpty() {
6114 return this._currentLine.parts.length === 0;
6115 }
6116 lineLength() {
6117 return this._currentLine.indent * _INDENT_WITH.length + this._currentLine.partsLength;
6118 }
6119 print(from, part, newLine = false) {
6120 if (part.length > 0) {
6121 this._currentLine.parts.push(part);
6122 this._currentLine.partsLength += part.length;
6123 this._currentLine.srcSpans.push(from && from.sourceSpan || null);
6124 }
6125 if (newLine) {
6126 this._lines.push(new _EmittedLine(this._indent));
6127 }
6128 }
6129 removeEmptyLastLine() {
6130 if (this.lineIsEmpty()) {
6131 this._lines.pop();
6132 }
6133 }
6134 incIndent() {
6135 this._indent++;
6136 if (this.lineIsEmpty()) {
6137 this._currentLine.indent = this._indent;
6138 }
6139 }
6140 decIndent() {
6141 this._indent--;
6142 if (this.lineIsEmpty()) {
6143 this._currentLine.indent = this._indent;
6144 }
6145 }
6146 pushClass(clazz) {
6147 this._classes.push(clazz);
6148 }
6149 popClass() {
6150 return this._classes.pop();
6151 }
6152 get currentClass() {
6153 return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null;
6154 }
6155 toSource() {
6156 return this.sourceLines
6157 .map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '')
6158 .join('\n');
6159 }
6160 toSourceMapGenerator(genFilePath, startsAtLine = 0) {
6161 const map = new SourceMapGenerator(genFilePath);
6162 let firstOffsetMapped = false;
6163 const mapFirstOffsetIfNeeded = () => {
6164 if (!firstOffsetMapped) {
6165 // Add a single space so that tools won't try to load the file from disk.
6166 // Note: We are using virtual urls like `ng:///`, so we have to
6167 // provide a content here.
6168 map.addSource(genFilePath, ' ').addMapping(0, genFilePath, 0, 0);
6169 firstOffsetMapped = true;
6170 }
6171 };
6172 for (let i = 0; i < startsAtLine; i++) {
6173 map.addLine();
6174 mapFirstOffsetIfNeeded();
6175 }
6176 this.sourceLines.forEach((line, lineIdx) => {
6177 map.addLine();
6178 const spans = line.srcSpans;
6179 const parts = line.parts;
6180 let col0 = line.indent * _INDENT_WITH.length;
6181 let spanIdx = 0;
6182 // skip leading parts without source spans
6183 while (spanIdx < spans.length && !spans[spanIdx]) {
6184 col0 += parts[spanIdx].length;
6185 spanIdx++;
6186 }
6187 if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) {
6188 firstOffsetMapped = true;
6189 }
6190 else {
6191 mapFirstOffsetIfNeeded();
6192 }
6193 while (spanIdx < spans.length) {
6194 const span = spans[spanIdx];
6195 const source = span.start.file;
6196 const sourceLine = span.start.line;
6197 const sourceCol = span.start.col;
6198 map.addSource(source.url, source.content)
6199 .addMapping(col0, source.url, sourceLine, sourceCol);
6200 col0 += parts[spanIdx].length;
6201 spanIdx++;
6202 // assign parts without span or the same span to the previous segment
6203 while (spanIdx < spans.length && (span === spans[spanIdx] || !spans[spanIdx])) {
6204 col0 += parts[spanIdx].length;
6205 spanIdx++;
6206 }
6207 }
6208 });
6209 return map;
6210 }
6211 setPreambleLineCount(count) {
6212 return this._preambleLineCount = count;
6213 }
6214 spanOf(line, column) {
6215 const emittedLine = this._lines[line - this._preambleLineCount];
6216 if (emittedLine) {
6217 let columnsLeft = column - _createIndent(emittedLine.indent).length;
6218 for (let partIndex = 0; partIndex < emittedLine.parts.length; partIndex++) {
6219 const part = emittedLine.parts[partIndex];
6220 if (part.length > columnsLeft) {
6221 return emittedLine.srcSpans[partIndex];
6222 }
6223 columnsLeft -= part.length;
6224 }
6225 }
6226 return null;
6227 }
6228 /**
6229 * @internal strip this from published d.ts files due to
6230 * https://github.com/microsoft/TypeScript/issues/36216
6231 */
6232 get sourceLines() {
6233 if (this._lines.length && this._lines[this._lines.length - 1].parts.length === 0) {
6234 return this._lines.slice(0, -1);
6235 }
6236 return this._lines;
6237 }
6238 }
6239 class AbstractEmitterVisitor {
6240 constructor(_escapeDollarInStrings) {
6241 this._escapeDollarInStrings = _escapeDollarInStrings;
6242 }
6243 printLeadingComments(stmt, ctx) {
6244 if (stmt.leadingComments === undefined) {
6245 return;
6246 }
6247 for (const comment of stmt.leadingComments) {
6248 if (comment instanceof JSDocComment) {
6249 ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline);
6250 }
6251 else {
6252 if (comment.multiline) {
6253 ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline);
6254 }
6255 else {
6256 comment.text.split('\n').forEach((line) => {
6257 ctx.println(stmt, `// ${line}`);
6258 });
6259 }
6260 }
6261 }
6262 }
6263 visitExpressionStmt(stmt, ctx) {
6264 this.printLeadingComments(stmt, ctx);
6265 stmt.expr.visitExpression(this, ctx);
6266 ctx.println(stmt, ';');
6267 return null;
6268 }
6269 visitReturnStmt(stmt, ctx) {
6270 this.printLeadingComments(stmt, ctx);
6271 ctx.print(stmt, `return `);
6272 stmt.value.visitExpression(this, ctx);
6273 ctx.println(stmt, ';');
6274 return null;
6275 }
6276 visitIfStmt(stmt, ctx) {
6277 this.printLeadingComments(stmt, ctx);
6278 ctx.print(stmt, `if (`);
6279 stmt.condition.visitExpression(this, ctx);
6280 ctx.print(stmt, `) {`);
6281 const hasElseCase = stmt.falseCase != null && stmt.falseCase.length > 0;
6282 if (stmt.trueCase.length <= 1 && !hasElseCase) {
6283 ctx.print(stmt, ` `);
6284 this.visitAllStatements(stmt.trueCase, ctx);
6285 ctx.removeEmptyLastLine();
6286 ctx.print(stmt, ` `);
6287 }
6288 else {
6289 ctx.println();
6290 ctx.incIndent();
6291 this.visitAllStatements(stmt.trueCase, ctx);
6292 ctx.decIndent();
6293 if (hasElseCase) {
6294 ctx.println(stmt, `} else {`);
6295 ctx.incIndent();
6296 this.visitAllStatements(stmt.falseCase, ctx);
6297 ctx.decIndent();
6298 }
6299 }
6300 ctx.println(stmt, `}`);
6301 return null;
6302 }
6303 visitThrowStmt(stmt, ctx) {
6304 this.printLeadingComments(stmt, ctx);
6305 ctx.print(stmt, `throw `);
6306 stmt.error.visitExpression(this, ctx);
6307 ctx.println(stmt, `;`);
6308 return null;
6309 }
6310 visitWriteVarExpr(expr, ctx) {
6311 const lineWasEmpty = ctx.lineIsEmpty();
6312 if (!lineWasEmpty) {
6313 ctx.print(expr, '(');
6314 }
6315 ctx.print(expr, `${expr.name} = `);
6316 expr.value.visitExpression(this, ctx);
6317 if (!lineWasEmpty) {
6318 ctx.print(expr, ')');
6319 }
6320 return null;
6321 }
6322 visitWriteKeyExpr(expr, ctx) {
6323 const lineWasEmpty = ctx.lineIsEmpty();
6324 if (!lineWasEmpty) {
6325 ctx.print(expr, '(');
6326 }
6327 expr.receiver.visitExpression(this, ctx);
6328 ctx.print(expr, `[`);
6329 expr.index.visitExpression(this, ctx);
6330 ctx.print(expr, `] = `);
6331 expr.value.visitExpression(this, ctx);
6332 if (!lineWasEmpty) {
6333 ctx.print(expr, ')');
6334 }
6335 return null;
6336 }
6337 visitWritePropExpr(expr, ctx) {
6338 const lineWasEmpty = ctx.lineIsEmpty();
6339 if (!lineWasEmpty) {
6340 ctx.print(expr, '(');
6341 }
6342 expr.receiver.visitExpression(this, ctx);
6343 ctx.print(expr, `.${expr.name} = `);
6344 expr.value.visitExpression(this, ctx);
6345 if (!lineWasEmpty) {
6346 ctx.print(expr, ')');
6347 }
6348 return null;
6349 }
6350 visitInvokeMethodExpr(expr, ctx) {
6351 expr.receiver.visitExpression(this, ctx);
6352 let name = expr.name;
6353 if (expr.builtin != null) {
6354 name = this.getBuiltinMethodName(expr.builtin);
6355 if (name == null) {
6356 // some builtins just mean to skip the call.
6357 return null;
6358 }
6359 }
6360 ctx.print(expr, `.${name}(`);
6361 this.visitAllExpressions(expr.args, ctx, `,`);
6362 ctx.print(expr, `)`);
6363 return null;
6364 }
6365 visitInvokeFunctionExpr(expr, ctx) {
6366 expr.fn.visitExpression(this, ctx);
6367 ctx.print(expr, `(`);
6368 this.visitAllExpressions(expr.args, ctx, ',');
6369 ctx.print(expr, `)`);
6370 return null;
6371 }
6372 visitTaggedTemplateExpr(expr, ctx) {
6373 expr.tag.visitExpression(this, ctx);
6374 ctx.print(expr, '`' + expr.template.elements[0].rawText);
6375 for (let i = 1; i < expr.template.elements.length; i++) {
6376 ctx.print(expr, '${');
6377 expr.template.expressions[i - 1].visitExpression(this, ctx);
6378 ctx.print(expr, `}${expr.template.elements[i].rawText}`);
6379 }
6380 ctx.print(expr, '`');
6381 return null;
6382 }
6383 visitWrappedNodeExpr(ast, ctx) {
6384 throw new Error('Abstract emitter cannot visit WrappedNodeExpr.');
6385 }
6386 visitTypeofExpr(expr, ctx) {
6387 ctx.print(expr, 'typeof ');
6388 expr.expr.visitExpression(this, ctx);
6389 }
6390 visitReadVarExpr(ast, ctx) {
6391 let varName = ast.name;
6392 if (ast.builtin != null) {
6393 switch (ast.builtin) {
6394 case BuiltinVar.Super:
6395 varName = 'super';
6396 break;
6397 case BuiltinVar.This:
6398 varName = 'this';
6399 break;
6400 case BuiltinVar.CatchError:
6401 varName = CATCH_ERROR_VAR$1.name;
6402 break;
6403 case BuiltinVar.CatchStack:
6404 varName = CATCH_STACK_VAR$1.name;
6405 break;
6406 default:
6407 throw new Error(`Unknown builtin variable ${ast.builtin}`);
6408 }
6409 }
6410 ctx.print(ast, varName);
6411 return null;
6412 }
6413 visitInstantiateExpr(ast, ctx) {
6414 ctx.print(ast, `new `);
6415 ast.classExpr.visitExpression(this, ctx);
6416 ctx.print(ast, `(`);
6417 this.visitAllExpressions(ast.args, ctx, ',');
6418 ctx.print(ast, `)`);
6419 return null;
6420 }
6421 visitLiteralExpr(ast, ctx) {
6422 const value = ast.value;
6423 if (typeof value === 'string') {
6424 ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings));
6425 }
6426 else {
6427 ctx.print(ast, `${value}`);
6428 }
6429 return null;
6430 }
6431 visitLocalizedString(ast, ctx) {
6432 const head = ast.serializeI18nHead();
6433 ctx.print(ast, '$localize `' + head.raw);
6434 for (let i = 1; i < ast.messageParts.length; i++) {
6435 ctx.print(ast, '${');
6436 ast.expressions[i - 1].visitExpression(this, ctx);
6437 ctx.print(ast, `}${ast.serializeI18nTemplatePart(i).raw}`);
6438 }
6439 ctx.print(ast, '`');
6440 return null;
6441 }
6442 visitConditionalExpr(ast, ctx) {
6443 ctx.print(ast, `(`);
6444 ast.condition.visitExpression(this, ctx);
6445 ctx.print(ast, '? ');
6446 ast.trueCase.visitExpression(this, ctx);
6447 ctx.print(ast, ': ');
6448 ast.falseCase.visitExpression(this, ctx);
6449 ctx.print(ast, `)`);
6450 return null;
6451 }
6452 visitNotExpr(ast, ctx) {
6453 ctx.print(ast, '!');
6454 ast.condition.visitExpression(this, ctx);
6455 return null;
6456 }
6457 visitAssertNotNullExpr(ast, ctx) {
6458 ast.condition.visitExpression(this, ctx);
6459 return null;
6460 }
6461 visitUnaryOperatorExpr(ast, ctx) {
6462 let opStr;
6463 switch (ast.operator) {
6464 case UnaryOperator.Plus:
6465 opStr = '+';
6466 break;
6467 case UnaryOperator.Minus:
6468 opStr = '-';
6469 break;
6470 default:
6471 throw new Error(`Unknown operator ${ast.operator}`);
6472 }
6473 if (ast.parens)
6474 ctx.print(ast, `(`);
6475 ctx.print(ast, opStr);
6476 ast.expr.visitExpression(this, ctx);
6477 if (ast.parens)
6478 ctx.print(ast, `)`);
6479 return null;
6480 }
6481 visitBinaryOperatorExpr(ast, ctx) {
6482 let opStr;
6483 switch (ast.operator) {
6484 case BinaryOperator.Equals:
6485 opStr = '==';
6486 break;
6487 case BinaryOperator.Identical:
6488 opStr = '===';
6489 break;
6490 case BinaryOperator.NotEquals:
6491 opStr = '!=';
6492 break;
6493 case BinaryOperator.NotIdentical:
6494 opStr = '!==';
6495 break;
6496 case BinaryOperator.And:
6497 opStr = '&&';
6498 break;
6499 case BinaryOperator.BitwiseAnd:
6500 opStr = '&';
6501 break;
6502 case BinaryOperator.Or:
6503 opStr = '||';
6504 break;
6505 case BinaryOperator.Plus:
6506 opStr = '+';
6507 break;
6508 case BinaryOperator.Minus:
6509 opStr = '-';
6510 break;
6511 case BinaryOperator.Divide:
6512 opStr = '/';
6513 break;
6514 case BinaryOperator.Multiply:
6515 opStr = '*';
6516 break;
6517 case BinaryOperator.Modulo:
6518 opStr = '%';
6519 break;
6520 case BinaryOperator.Lower:
6521 opStr = '<';
6522 break;
6523 case BinaryOperator.LowerEquals:
6524 opStr = '<=';
6525 break;
6526 case BinaryOperator.Bigger:
6527 opStr = '>';
6528 break;
6529 case BinaryOperator.BiggerEquals:
6530 opStr = '>=';
6531 break;
6532 default:
6533 throw new Error(`Unknown operator ${ast.operator}`);
6534 }
6535 if (ast.parens)
6536 ctx.print(ast, `(`);
6537 ast.lhs.visitExpression(this, ctx);
6538 ctx.print(ast, ` ${opStr} `);
6539 ast.rhs.visitExpression(this, ctx);
6540 if (ast.parens)
6541 ctx.print(ast, `)`);
6542 return null;
6543 }
6544 visitReadPropExpr(ast, ctx) {
6545 ast.receiver.visitExpression(this, ctx);
6546 ctx.print(ast, `.`);
6547 ctx.print(ast, ast.name);
6548 return null;
6549 }
6550 visitReadKeyExpr(ast, ctx) {
6551 ast.receiver.visitExpression(this, ctx);
6552 ctx.print(ast, `[`);
6553 ast.index.visitExpression(this, ctx);
6554 ctx.print(ast, `]`);
6555 return null;
6556 }
6557 visitLiteralArrayExpr(ast, ctx) {
6558 ctx.print(ast, `[`);
6559 this.visitAllExpressions(ast.entries, ctx, ',');
6560 ctx.print(ast, `]`);
6561 return null;
6562 }
6563 visitLiteralMapExpr(ast, ctx) {
6564 ctx.print(ast, `{`);
6565 this.visitAllObjects(entry => {
6566 ctx.print(ast, `${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`);
6567 entry.value.visitExpression(this, ctx);
6568 }, ast.entries, ctx, ',');
6569 ctx.print(ast, `}`);
6570 return null;
6571 }
6572 visitCommaExpr(ast, ctx) {
6573 ctx.print(ast, '(');
6574 this.visitAllExpressions(ast.parts, ctx, ',');
6575 ctx.print(ast, ')');
6576 return null;
6577 }
6578 visitAllExpressions(expressions, ctx, separator) {
6579 this.visitAllObjects(expr => expr.visitExpression(this, ctx), expressions, ctx, separator);
6580 }
6581 visitAllObjects(handler, expressions, ctx, separator) {
6582 let incrementedIndent = false;
6583 for (let i = 0; i < expressions.length; i++) {
6584 if (i > 0) {
6585 if (ctx.lineLength() > 80) {
6586 ctx.print(null, separator, true);
6587 if (!incrementedIndent) {
6588 // continuation are marked with double indent.
6589 ctx.incIndent();
6590 ctx.incIndent();
6591 incrementedIndent = true;
6592 }
6593 }
6594 else {
6595 ctx.print(null, separator, false);
6596 }
6597 }
6598 handler(expressions[i]);
6599 }
6600 if (incrementedIndent) {
6601 // continuation are marked with double indent.
6602 ctx.decIndent();
6603 ctx.decIndent();
6604 }
6605 }
6606 visitAllStatements(statements, ctx) {
6607 statements.forEach((stmt) => stmt.visitStatement(this, ctx));
6608 }
6609 }
6610 function escapeIdentifier(input, escapeDollar, alwaysQuote = true) {
6611 if (input == null) {
6612 return null;
6613 }
6614 const body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match) => {
6615 if (match[0] == '$') {
6616 return escapeDollar ? '\\$' : '$';
6617 }
6618 else if (match[0] == '\n') {
6619 return '\\n';
6620 }
6621 else if (match[0] == '\r') {
6622 return '\\r';
6623 }
6624 else {
6625 return `\\${match[0]}`;
6626 }
6627 });
6628 const requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body);
6629 return requiresQuotes ? `'${body}'` : body;
6630 }
6631 function _createIndent(count) {
6632 let res = '';
6633 for (let i = 0; i < count; i++) {
6634 res += _INDENT_WITH;
6635 }
6636 return res;
6637 }
6638
6639 /**
6640 * @license
6641 * Copyright Google LLC All Rights Reserved.
6642 *
6643 * Use of this source code is governed by an MIT-style license that can be
6644 * found in the LICENSE file at https://angular.io/license
6645 */
6646 /**
6647 * Convert an object map with `Expression` values into a `LiteralMapExpr`.
6648 */
6649 function mapToMapExpression(map) {
6650 const result = Object.keys(map).map(key => ({
6651 key,
6652 // The assertion here is because really TypeScript doesn't allow us to express that if the
6653 // key is present, it will have a value, but this is true in reality.
6654 value: map[key],
6655 quoted: false,
6656 }));
6657 return literalMap(result);
6658 }
6659 function typeWithParameters(type, numParams) {
6660 if (numParams === 0) {
6661 return expressionType(type);
6662 }
6663 const params = [];
6664 for (let i = 0; i < numParams; i++) {
6665 params.push(DYNAMIC_TYPE);
6666 }
6667 return expressionType(type, undefined, params);
6668 }
6669 const ANIMATE_SYMBOL_PREFIX = '@';
6670 function prepareSyntheticPropertyName(name) {
6671 return `${ANIMATE_SYMBOL_PREFIX}${name}`;
6672 }
6673 function prepareSyntheticListenerName(name, phase) {
6674 return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`;
6675 }
6676 function getSafePropertyAccessString(accessor, name) {
6677 const escapedName = escapeIdentifier(name, false, false);
6678 return escapedName !== name ? `${accessor}[${escapedName}]` : `${accessor}.${name}`;
6679 }
6680 function prepareSyntheticListenerFunctionName(name, phase) {
6681 return `animation_${name}_${phase}`;
6682 }
6683 function jitOnlyGuardedExpression(expr) {
6684 return guardedExpression('ngJitMode', expr);
6685 }
6686 function devOnlyGuardedExpression(expr) {
6687 return guardedExpression('ngDevMode', expr);
6688 }
6689 function guardedExpression(guard, expr) {
6690 const guardExpr = new ExternalExpr({ name: guard, moduleName: null });
6691 const guardNotDefined = new BinaryOperatorExpr(BinaryOperator.Identical, new TypeofExpr(guardExpr), literal('undefined'));
6692 const guardUndefinedOrTrue = new BinaryOperatorExpr(BinaryOperator.Or, guardNotDefined, guardExpr, /* type */ undefined,
6693 /* sourceSpan */ undefined, true);
6694 return new BinaryOperatorExpr(BinaryOperator.And, guardUndefinedOrTrue, expr);
6695 }
6696
6697 /**
6698 * @license
6699 * Copyright Google LLC All Rights Reserved.
6700 *
6701 * Use of this source code is governed by an MIT-style license that can be
6702 * found in the LICENSE file at https://angular.io/license
6703 */
6704 class Text {
6705 constructor(value, sourceSpan) {
6706 this.value = value;
6707 this.sourceSpan = sourceSpan;
6708 }
6709 visit(visitor) {
6710 return visitor.visitText(this);
6711 }
6712 }
6713 class BoundText {
6714 constructor(value, sourceSpan, i18n) {
6715 this.value = value;
6716 this.sourceSpan = sourceSpan;
6717 this.i18n = i18n;
6718 }
6719 visit(visitor) {
6720 return visitor.visitBoundText(this);
6721 }
6722 }
6723 /**
6724 * Represents a text attribute in the template.
6725 *
6726 * `valueSpan` may not be present in cases where there is no value `<div a></div>`.
6727 * `keySpan` may also not be present for synthetic attributes from ICU expansions.
6728 */
6729 class TextAttribute {
6730 constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) {
6731 this.name = name;
6732 this.value = value;
6733 this.sourceSpan = sourceSpan;
6734 this.keySpan = keySpan;
6735 this.valueSpan = valueSpan;
6736 this.i18n = i18n;
6737 }
6738 visit(visitor) {
6739 return visitor.visitTextAttribute(this);
6740 }
6741 }
6742 class BoundAttribute {
6743 constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan, i18n) {
6744 this.name = name;
6745 this.type = type;
6746 this.securityContext = securityContext;
6747 this.value = value;
6748 this.unit = unit;
6749 this.sourceSpan = sourceSpan;
6750 this.keySpan = keySpan;
6751 this.valueSpan = valueSpan;
6752 this.i18n = i18n;
6753 }
6754 static fromBoundElementProperty(prop, i18n) {
6755 if (prop.keySpan === undefined) {
6756 throw new Error(`Unexpected state: keySpan must be defined for bound attributes but was not for ${prop.name}: ${prop.sourceSpan}`);
6757 }
6758 return new BoundAttribute(prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n);
6759 }
6760 visit(visitor) {
6761 return visitor.visitBoundAttribute(this);
6762 }
6763 }
6764 class BoundEvent {
6765 constructor(name, type, handler, target, phase, sourceSpan, handlerSpan, keySpan) {
6766 this.name = name;
6767 this.type = type;
6768 this.handler = handler;
6769 this.target = target;
6770 this.phase = phase;
6771 this.sourceSpan = sourceSpan;
6772 this.handlerSpan = handlerSpan;
6773 this.keySpan = keySpan;
6774 }
6775 static fromParsedEvent(event) {
6776 const target = event.type === 0 /* Regular */ ? event.targetOrPhase : null;
6777 const phase = event.type === 1 /* Animation */ ? event.targetOrPhase : null;
6778 if (event.keySpan === undefined) {
6779 throw new Error(`Unexpected state: keySpan must be defined for bound event but was not for ${event.name}: ${event.sourceSpan}`);
6780 }
6781 return new BoundEvent(event.name, event.type, event.handler, target, phase, event.sourceSpan, event.handlerSpan, event.keySpan);
6782 }
6783 visit(visitor) {
6784 return visitor.visitBoundEvent(this);
6785 }
6786 }
6787 class Element {
6788 constructor(name, attributes, inputs, outputs, children, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
6789 this.name = name;
6790 this.attributes = attributes;
6791 this.inputs = inputs;
6792 this.outputs = outputs;
6793 this.children = children;
6794 this.references = references;
6795 this.sourceSpan = sourceSpan;
6796 this.startSourceSpan = startSourceSpan;
6797 this.endSourceSpan = endSourceSpan;
6798 this.i18n = i18n;
6799 }
6800 visit(visitor) {
6801 return visitor.visitElement(this);
6802 }
6803 }
6804 class Template {
6805 constructor(tagName, attributes, inputs, outputs, templateAttrs, children, references, variables, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
6806 this.tagName = tagName;
6807 this.attributes = attributes;
6808 this.inputs = inputs;
6809 this.outputs = outputs;
6810 this.templateAttrs = templateAttrs;
6811 this.children = children;
6812 this.references = references;
6813 this.variables = variables;
6814 this.sourceSpan = sourceSpan;
6815 this.startSourceSpan = startSourceSpan;
6816 this.endSourceSpan = endSourceSpan;
6817 this.i18n = i18n;
6818 }
6819 visit(visitor) {
6820 return visitor.visitTemplate(this);
6821 }
6822 }
6823 class Content {
6824 constructor(selector, attributes, sourceSpan, i18n) {
6825 this.selector = selector;
6826 this.attributes = attributes;
6827 this.sourceSpan = sourceSpan;
6828 this.i18n = i18n;
6829 this.name = 'ng-content';
6830 }
6831 visit(visitor) {
6832 return visitor.visitContent(this);
6833 }
6834 }
6835 class Variable {
6836 constructor(name, value, sourceSpan, keySpan, valueSpan) {
6837 this.name = name;
6838 this.value = value;
6839 this.sourceSpan = sourceSpan;
6840 this.keySpan = keySpan;
6841 this.valueSpan = valueSpan;
6842 }
6843 visit(visitor) {
6844 return visitor.visitVariable(this);
6845 }
6846 }
6847 class Reference {
6848 constructor(name, value, sourceSpan, keySpan, valueSpan) {
6849 this.name = name;
6850 this.value = value;
6851 this.sourceSpan = sourceSpan;
6852 this.keySpan = keySpan;
6853 this.valueSpan = valueSpan;
6854 }
6855 visit(visitor) {
6856 return visitor.visitReference(this);
6857 }
6858 }
6859 class Icu {
6860 constructor(vars, placeholders, sourceSpan, i18n) {
6861 this.vars = vars;
6862 this.placeholders = placeholders;
6863 this.sourceSpan = sourceSpan;
6864 this.i18n = i18n;
6865 }
6866 visit(visitor) {
6867 return visitor.visitIcu(this);
6868 }
6869 }
6870 class RecursiveVisitor {
6871 visitElement(element) {
6872 visitAll(this, element.attributes);
6873 visitAll(this, element.children);
6874 visitAll(this, element.references);
6875 }
6876 visitTemplate(template) {
6877 visitAll(this, template.attributes);
6878 visitAll(this, template.children);
6879 visitAll(this, template.references);
6880 visitAll(this, template.variables);
6881 }
6882 visitContent(content) { }
6883 visitVariable(variable) { }
6884 visitReference(reference) { }
6885 visitTextAttribute(attribute) { }
6886 visitBoundAttribute(attribute) { }
6887 visitBoundEvent(attribute) { }
6888 visitText(text) { }
6889 visitBoundText(text) { }
6890 visitIcu(icu) { }
6891 }
6892 function visitAll(visitor, nodes) {
6893 const result = [];
6894 if (visitor.visit) {
6895 for (const node of nodes) {
6896 const newNode = visitor.visit(node) || node.visit(visitor);
6897 }
6898 }
6899 else {
6900 for (const node of nodes) {
6901 const newNode = node.visit(visitor);
6902 if (newNode) {
6903 result.push(newNode);
6904 }
6905 }
6906 }
6907 return result;
6908 }
6909
6910 /**
6911 * @license
6912 * Copyright Google LLC All Rights Reserved.
6913 *
6914 * Use of this source code is governed by an MIT-style license that can be
6915 * found in the LICENSE file at https://angular.io/license
6916 */
6917 class Message {
6918 /**
6919 * @param nodes message AST
6920 * @param placeholders maps placeholder names to static content and their source spans
6921 * @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
6922 * @param meaning
6923 * @param description
6924 * @param customId
6925 */
6926 constructor(nodes, placeholders, placeholderToMessage, meaning, description, customId) {
6927 this.nodes = nodes;
6928 this.placeholders = placeholders;
6929 this.placeholderToMessage = placeholderToMessage;
6930 this.meaning = meaning;
6931 this.description = description;
6932 this.customId = customId;
6933 this.id = this.customId;
6934 /** The ids to use if there are no custom id and if `i18nLegacyMessageIdFormat` is not empty */
6935 this.legacyIds = [];
6936 if (nodes.length) {
6937 this.sources = [{
6938 filePath: nodes[0].sourceSpan.start.file.url,
6939 startLine: nodes[0].sourceSpan.start.line + 1,
6940 startCol: nodes[0].sourceSpan.start.col + 1,
6941 endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1,
6942 endCol: nodes[0].sourceSpan.start.col + 1
6943 }];
6944 }
6945 else {
6946 this.sources = [];
6947 }
6948 }
6949 }
6950 class Text$1 {
6951 constructor(value, sourceSpan) {
6952 this.value = value;
6953 this.sourceSpan = sourceSpan;
6954 }
6955 visit(visitor, context) {
6956 return visitor.visitText(this, context);
6957 }
6958 }
6959 // TODO(vicb): do we really need this node (vs an array) ?
6960 class Container {
6961 constructor(children, sourceSpan) {
6962 this.children = children;
6963 this.sourceSpan = sourceSpan;
6964 }
6965 visit(visitor, context) {
6966 return visitor.visitContainer(this, context);
6967 }
6968 }
6969 class Icu$1 {
6970 constructor(expression, type, cases, sourceSpan) {
6971 this.expression = expression;
6972 this.type = type;
6973 this.cases = cases;
6974 this.sourceSpan = sourceSpan;
6975 }
6976 visit(visitor, context) {
6977 return visitor.visitIcu(this, context);
6978 }
6979 }
6980 class TagPlaceholder {
6981 constructor(tag, attrs, startName, closeName, children, isVoid,
6982 // TODO sourceSpan should cover all (we need a startSourceSpan and endSourceSpan)
6983 sourceSpan, startSourceSpan, endSourceSpan) {
6984 this.tag = tag;
6985 this.attrs = attrs;
6986 this.startName = startName;
6987 this.closeName = closeName;
6988 this.children = children;
6989 this.isVoid = isVoid;
6990 this.sourceSpan = sourceSpan;
6991 this.startSourceSpan = startSourceSpan;
6992 this.endSourceSpan = endSourceSpan;
6993 }
6994 visit(visitor, context) {
6995 return visitor.visitTagPlaceholder(this, context);
6996 }
6997 }
6998 class Placeholder {
6999 constructor(value, name, sourceSpan) {
7000 this.value = value;
7001 this.name = name;
7002 this.sourceSpan = sourceSpan;
7003 }
7004 visit(visitor, context) {
7005 return visitor.visitPlaceholder(this, context);
7006 }
7007 }
7008 class IcuPlaceholder {
7009 constructor(value, name, sourceSpan) {
7010 this.value = value;
7011 this.name = name;
7012 this.sourceSpan = sourceSpan;
7013 }
7014 visit(visitor, context) {
7015 return visitor.visitIcuPlaceholder(this, context);
7016 }
7017 }
7018
7019 /**
7020 * @license
7021 * Copyright Google LLC All Rights Reserved.
7022 *
7023 * Use of this source code is governed by an MIT-style license that can be
7024 * found in the LICENSE file at https://angular.io/license
7025 */
7026 /**
7027 * Represents a big integer using a buffer of its individual digits, with the least significant
7028 * digit stored at the beginning of the array (little endian).
7029 *
7030 * For performance reasons, each instance is mutable. The addition operation can be done in-place
7031 * to reduce memory pressure of allocation for the digits array.
7032 */
7033 class BigInteger {
7034 /**
7035 * Creates a big integer using its individual digits in little endian storage.
7036 */
7037 constructor(digits) {
7038 this.digits = digits;
7039 }
7040 static zero() {
7041 return new BigInteger([0]);
7042 }
7043 static one() {
7044 return new BigInteger([1]);
7045 }
7046 /**
7047 * Creates a clone of this instance.
7048 */
7049 clone() {
7050 return new BigInteger(this.digits.slice());
7051 }
7052 /**
7053 * Returns a new big integer with the sum of `this` and `other` as its value. This does not mutate
7054 * `this` but instead returns a new instance, unlike `addToSelf`.
7055 */
7056 add(other) {
7057 const result = this.clone();
7058 result.addToSelf(other);
7059 return result;
7060 }
7061 /**
7062 * Adds `other` to the instance itself, thereby mutating its value.
7063 */
7064 addToSelf(other) {
7065 const maxNrOfDigits = Math.max(this.digits.length, other.digits.length);
7066 let carry = 0;
7067 for (let i = 0; i < maxNrOfDigits; i++) {
7068 let digitSum = carry;
7069 if (i < this.digits.length) {
7070 digitSum += this.digits[i];
7071 }
7072 if (i < other.digits.length) {
7073 digitSum += other.digits[i];
7074 }
7075 if (digitSum >= 10) {
7076 this.digits[i] = digitSum - 10;
7077 carry = 1;
7078 }
7079 else {
7080 this.digits[i] = digitSum;
7081 carry = 0;
7082 }
7083 }
7084 // Apply a remaining carry if needed.
7085 if (carry > 0) {
7086 this.digits[maxNrOfDigits] = 1;
7087 }
7088 }
7089 /**
7090 * Builds the decimal string representation of the big integer. As this is stored in
7091 * little endian, the digits are concatenated in reverse order.
7092 */
7093 toString() {
7094 let res = '';
7095 for (let i = this.digits.length - 1; i >= 0; i--) {
7096 res += this.digits[i];
7097 }
7098 return res;
7099 }
7100 }
7101 /**
7102 * Represents a big integer which is optimized for multiplication operations, as its power-of-twos
7103 * are memoized. See `multiplyBy()` for details on the multiplication algorithm.
7104 */
7105 class BigIntForMultiplication {
7106 constructor(value) {
7107 this.powerOfTwos = [value];
7108 }
7109 /**
7110 * Returns the big integer itself.
7111 */
7112 getValue() {
7113 return this.powerOfTwos[0];
7114 }
7115 /**
7116 * Computes the value for `num * b`, where `num` is a JS number and `b` is a big integer. The
7117 * value for `b` is represented by a storage model that is optimized for this computation.
7118 *
7119 * This operation is implemented in N(log2(num)) by continuous halving of the number, where the
7120 * least-significant bit (LSB) is tested in each iteration. If the bit is set, the bit's index is
7121 * used as exponent into the power-of-two multiplication of `b`.
7122 *
7123 * As an example, consider the multiplication num=42, b=1337. In binary 42 is 0b00101010 and the
7124 * algorithm unrolls into the following iterations:
7125 *
7126 * Iteration | num | LSB | b * 2^iter | Add? | product
7127 * -----------|------------|------|------------|------|--------
7128 * 0 | 0b00101010 | 0 | 1337 | No | 0
7129 * 1 | 0b00010101 | 1 | 2674 | Yes | 2674
7130 * 2 | 0b00001010 | 0 | 5348 | No | 2674
7131 * 3 | 0b00000101 | 1 | 10696 | Yes | 13370
7132 * 4 | 0b00000010 | 0 | 21392 | No | 13370
7133 * 5 | 0b00000001 | 1 | 42784 | Yes | 56154
7134 * 6 | 0b00000000 | 0 | 85568 | No | 56154
7135 *
7136 * The computed product of 56154 is indeed the correct result.
7137 *
7138 * The `BigIntForMultiplication` representation for a big integer provides memoized access to the
7139 * power-of-two values to reduce the workload in computing those values.
7140 */
7141 multiplyBy(num) {
7142 const product = BigInteger.zero();
7143 this.multiplyByAndAddTo(num, product);
7144 return product;
7145 }
7146 /**
7147 * See `multiplyBy()` for details. This function allows for the computed product to be added
7148 * directly to the provided result big integer.
7149 */
7150 multiplyByAndAddTo(num, result) {
7151 for (let exponent = 0; num !== 0; num = num >>> 1, exponent++) {
7152 if (num & 1) {
7153 const value = this.getMultipliedByPowerOfTwo(exponent);
7154 result.addToSelf(value);
7155 }
7156 }
7157 }
7158 /**
7159 * Computes and memoizes the big integer value for `this.number * 2^exponent`.
7160 */
7161 getMultipliedByPowerOfTwo(exponent) {
7162 // Compute the powers up until the requested exponent, where each value is computed from its
7163 // predecessor. This is simple as `this.number * 2^(exponent - 1)` only has to be doubled (i.e.
7164 // added to itself) to reach `this.number * 2^exponent`.
7165 for (let i = this.powerOfTwos.length; i <= exponent; i++) {
7166 const previousPower = this.powerOfTwos[i - 1];
7167 this.powerOfTwos[i] = previousPower.add(previousPower);
7168 }
7169 return this.powerOfTwos[exponent];
7170 }
7171 }
7172 /**
7173 * Represents an exponentiation operation for the provided base, of which exponents are computed and
7174 * memoized. The results are represented by a `BigIntForMultiplication` which is tailored for
7175 * multiplication operations by memoizing the power-of-twos. This effectively results in a matrix
7176 * representation that is lazily computed upon request.
7177 */
7178 class BigIntExponentiation {
7179 constructor(base) {
7180 this.base = base;
7181 this.exponents = [new BigIntForMultiplication(BigInteger.one())];
7182 }
7183 /**
7184 * Compute the value for `this.base^exponent`, resulting in a big integer that is optimized for
7185 * further multiplication operations.
7186 */
7187 toThePowerOf(exponent) {
7188 // Compute the results up until the requested exponent, where every value is computed from its
7189 // predecessor. This is because `this.base^(exponent - 1)` only has to be multiplied by `base`
7190 // to reach `this.base^exponent`.
7191 for (let i = this.exponents.length; i <= exponent; i++) {
7192 const value = this.exponents[i - 1].multiplyBy(this.base);
7193 this.exponents[i] = new BigIntForMultiplication(value);
7194 }
7195 return this.exponents[exponent];
7196 }
7197 }
7198
7199 /**
7200 * @license
7201 * Copyright Google LLC All Rights Reserved.
7202 *
7203 * Use of this source code is governed by an MIT-style license that can be
7204 * found in the LICENSE file at https://angular.io/license
7205 */
7206 /**
7207 * Compute the message id using the XLIFF1 digest.
7208 */
7209 function computeDigest(message) {
7210 return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
7211 }
7212 /**
7213 * Return the message id or compute it using the XLIFF2/XMB/$localize digest.
7214 */
7215 function decimalDigest(message) {
7216 return message.id || computeDecimalDigest(message);
7217 }
7218 /**
7219 * Compute the message id using the XLIFF2/XMB/$localize digest.
7220 */
7221 function computeDecimalDigest(message) {
7222 const visitor = new _SerializerIgnoreIcuExpVisitor();
7223 const parts = message.nodes.map(a => a.visit(visitor, null));
7224 return computeMsgId(parts.join(''), message.meaning);
7225 }
7226 /**
7227 * Serialize the i18n ast to something xml-like in order to generate an UID.
7228 *
7229 * The visitor is also used in the i18n parser tests
7230 *
7231 * @internal
7232 */
7233 class _SerializerVisitor {
7234 visitText(text, context) {
7235 return text.value;
7236 }
7237 visitContainer(container, context) {
7238 return `[${container.children.map(child => child.visit(this)).join(', ')}]`;
7239 }
7240 visitIcu(icu, context) {
7241 const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
7242 return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`;
7243 }
7244 visitTagPlaceholder(ph, context) {
7245 return ph.isVoid ?
7246 `<ph tag name="${ph.startName}"/>` :
7247 `<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`;
7248 }
7249 visitPlaceholder(ph, context) {
7250 return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`;
7251 }
7252 visitIcuPlaceholder(ph, context) {
7253 return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`;
7254 }
7255 }
7256 const serializerVisitor = new _SerializerVisitor();
7257 function serializeNodes(nodes) {
7258 return nodes.map(a => a.visit(serializerVisitor, null));
7259 }
7260 /**
7261 * Serialize the i18n ast to something xml-like in order to generate an UID.
7262 *
7263 * Ignore the ICU expressions so that message IDs stays identical if only the expression changes.
7264 *
7265 * @internal
7266 */
7267 class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor {
7268 visitIcu(icu, context) {
7269 let strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
7270 // Do not take the expression into account
7271 return `{${icu.type}, ${strCases.join(', ')}}`;
7272 }
7273 }
7274 /**
7275 * Compute the SHA1 of the given string
7276 *
7277 * see https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
7278 *
7279 * WARNING: this function has not been designed not tested with security in mind.
7280 * DO NOT USE IT IN A SECURITY SENSITIVE CONTEXT.
7281 */
7282 function sha1(str) {
7283 const utf8 = utf8Encode(str);
7284 const words32 = bytesToWords32(utf8, Endian.Big);
7285 const len = utf8.length * 8;
7286 const w = newArray(80);
7287 let a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476, e = 0xc3d2e1f0;
7288 words32[len >> 5] |= 0x80 << (24 - len % 32);
7289 words32[((len + 64 >> 9) << 4) + 15] = len;
7290 for (let i = 0; i < words32.length; i += 16) {
7291 const h0 = a, h1 = b, h2 = c, h3 = d, h4 = e;
7292 for (let j = 0; j < 80; j++) {
7293 if (j < 16) {
7294 w[j] = words32[i + j];
7295 }
7296 else {
7297 w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
7298 }
7299 const fkVal = fk(j, b, c, d);
7300 const f = fkVal[0];
7301 const k = fkVal[1];
7302 const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32);
7303 e = d;
7304 d = c;
7305 c = rol32(b, 30);
7306 b = a;
7307 a = temp;
7308 }
7309 a = add32(a, h0);
7310 b = add32(b, h1);
7311 c = add32(c, h2);
7312 d = add32(d, h3);
7313 e = add32(e, h4);
7314 }
7315 return bytesToHexString(words32ToByteString([a, b, c, d, e]));
7316 }
7317 function fk(index, b, c, d) {
7318 if (index < 20) {
7319 return [(b & c) | (~b & d), 0x5a827999];
7320 }
7321 if (index < 40) {
7322 return [b ^ c ^ d, 0x6ed9eba1];
7323 }
7324 if (index < 60) {
7325 return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
7326 }
7327 return [b ^ c ^ d, 0xca62c1d6];
7328 }
7329 /**
7330 * Compute the fingerprint of the given string
7331 *
7332 * The output is 64 bit number encoded as a decimal string
7333 *
7334 * based on:
7335 * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java
7336 */
7337 function fingerprint(str) {
7338 const utf8 = utf8Encode(str);
7339 let hi = hash32(utf8, 0);
7340 let lo = hash32(utf8, 102072);
7341 if (hi == 0 && (lo == 0 || lo == 1)) {
7342 hi = hi ^ 0x130f9bef;
7343 lo = lo ^ -0x6b5f56d8;
7344 }
7345 return [hi, lo];
7346 }
7347 function computeMsgId(msg, meaning = '') {
7348 let msgFingerprint = fingerprint(msg);
7349 if (meaning) {
7350 const meaningFingerprint = fingerprint(meaning);
7351 msgFingerprint = add64(rol64(msgFingerprint, 1), meaningFingerprint);
7352 }
7353 const hi = msgFingerprint[0];
7354 const lo = msgFingerprint[1];
7355 return wordsToDecimalString(hi & 0x7fffffff, lo);
7356 }
7357 function hash32(bytes, c) {
7358 let a = 0x9e3779b9, b = 0x9e3779b9;
7359 let i;
7360 const len = bytes.length;
7361 for (i = 0; i + 12 <= len; i += 12) {
7362 a = add32(a, wordAt(bytes, i, Endian.Little));
7363 b = add32(b, wordAt(bytes, i + 4, Endian.Little));
7364 c = add32(c, wordAt(bytes, i + 8, Endian.Little));
7365 const res = mix(a, b, c);
7366 a = res[0], b = res[1], c = res[2];
7367 }
7368 a = add32(a, wordAt(bytes, i, Endian.Little));
7369 b = add32(b, wordAt(bytes, i + 4, Endian.Little));
7370 // the first byte of c is reserved for the length
7371 c = add32(c, len);
7372 c = add32(c, wordAt(bytes, i + 8, Endian.Little) << 8);
7373 return mix(a, b, c)[2];
7374 }
7375 // clang-format off
7376 function mix(a, b, c) {
7377 a = sub32(a, b);
7378 a = sub32(a, c);
7379 a ^= c >>> 13;
7380 b = sub32(b, c);
7381 b = sub32(b, a);
7382 b ^= a << 8;
7383 c = sub32(c, a);
7384 c = sub32(c, b);
7385 c ^= b >>> 13;
7386 a = sub32(a, b);
7387 a = sub32(a, c);
7388 a ^= c >>> 12;
7389 b = sub32(b, c);
7390 b = sub32(b, a);
7391 b ^= a << 16;
7392 c = sub32(c, a);
7393 c = sub32(c, b);
7394 c ^= b >>> 5;
7395 a = sub32(a, b);
7396 a = sub32(a, c);
7397 a ^= c >>> 3;
7398 b = sub32(b, c);
7399 b = sub32(b, a);
7400 b ^= a << 10;
7401 c = sub32(c, a);
7402 c = sub32(c, b);
7403 c ^= b >>> 15;
7404 return [a, b, c];
7405 }
7406 // clang-format on
7407 // Utils
7408 var Endian;
7409 (function (Endian) {
7410 Endian[Endian["Little"] = 0] = "Little";
7411 Endian[Endian["Big"] = 1] = "Big";
7412 })(Endian || (Endian = {}));
7413 function add32(a, b) {
7414 return add32to64(a, b)[1];
7415 }
7416 function add32to64(a, b) {
7417 const low = (a & 0xffff) + (b & 0xffff);
7418 const high = (a >>> 16) + (b >>> 16) + (low >>> 16);
7419 return [high >>> 16, (high << 16) | (low & 0xffff)];
7420 }
7421 function add64(a, b) {
7422 const ah = a[0], al = a[1];
7423 const bh = b[0], bl = b[1];
7424 const result = add32to64(al, bl);
7425 const carry = result[0];
7426 const l = result[1];
7427 const h = add32(add32(ah, bh), carry);
7428 return [h, l];
7429 }
7430 function sub32(a, b) {
7431 const low = (a & 0xffff) - (b & 0xffff);
7432 const high = (a >> 16) - (b >> 16) + (low >> 16);
7433 return (high << 16) | (low & 0xffff);
7434 }
7435 // Rotate a 32b number left `count` position
7436 function rol32(a, count) {
7437 return (a << count) | (a >>> (32 - count));
7438 }
7439 // Rotate a 64b number left `count` position
7440 function rol64(num, count) {
7441 const hi = num[0], lo = num[1];
7442 const h = (hi << count) | (lo >>> (32 - count));
7443 const l = (lo << count) | (hi >>> (32 - count));
7444 return [h, l];
7445 }
7446 function bytesToWords32(bytes, endian) {
7447 const size = (bytes.length + 3) >>> 2;
7448 const words32 = [];
7449 for (let i = 0; i < size; i++) {
7450 words32[i] = wordAt(bytes, i * 4, endian);
7451 }
7452 return words32;
7453 }
7454 function byteAt(bytes, index) {
7455 return index >= bytes.length ? 0 : bytes[index];
7456 }
7457 function wordAt(bytes, index, endian) {
7458 let word = 0;
7459 if (endian === Endian.Big) {
7460 for (let i = 0; i < 4; i++) {
7461 word += byteAt(bytes, index + i) << (24 - 8 * i);
7462 }
7463 }
7464 else {
7465 for (let i = 0; i < 4; i++) {
7466 word += byteAt(bytes, index + i) << 8 * i;
7467 }
7468 }
7469 return word;
7470 }
7471 function words32ToByteString(words32) {
7472 return words32.reduce((bytes, word) => bytes.concat(word32ToByteString(word)), []);
7473 }
7474 function word32ToByteString(word) {
7475 let bytes = [];
7476 for (let i = 0; i < 4; i++) {
7477 bytes.push((word >>> 8 * (3 - i)) & 0xff);
7478 }
7479 return bytes;
7480 }
7481 function bytesToHexString(bytes) {
7482 let hex = '';
7483 for (let i = 0; i < bytes.length; i++) {
7484 const b = byteAt(bytes, i);
7485 hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16);
7486 }
7487 return hex.toLowerCase();
7488 }
7489 /**
7490 * Create a shared exponentiation pool for base-256 computations. This shared pool provides memoized
7491 * power-of-256 results with memoized power-of-two computations for efficient multiplication.
7492 *
7493 * For our purposes, this can be safely stored as a global without memory concerns. The reason is
7494 * that we encode two words, so only need the 0th (for the low word) and 4th (for the high word)
7495 * exponent.
7496 */
7497 const base256 = new BigIntExponentiation(256);
7498 /**
7499 * Represents two 32-bit words as a single decimal number. This requires a big integer storage
7500 * model as JS numbers are not accurate enough to represent the 64-bit number.
7501 *
7502 * Based on https://www.danvk.org/hex2dec.html
7503 */
7504 function wordsToDecimalString(hi, lo) {
7505 // Encode the four bytes in lo in the lower digits of the decimal number.
7506 // Note: the multiplication results in lo itself but represented by a big integer using its
7507 // decimal digits.
7508 const decimal = base256.toThePowerOf(0).multiplyBy(lo);
7509 // Encode the four bytes in hi above the four lo bytes. lo is a maximum of (2^8)^4, which is why
7510 // this multiplication factor is applied.
7511 base256.toThePowerOf(4).multiplyByAndAddTo(hi, decimal);
7512 return decimal.toString();
7513 }
7514
7515 /**
7516 * @license
7517 * Copyright Google LLC All Rights Reserved.
7518 *
7519 * Use of this source code is governed by an MIT-style license that can be
7520 * found in the LICENSE file at https://angular.io/license
7521 */
7522 // XMB/XTB placeholders can only contain A-Z, 0-9 and _
7523 function toPublicName(internalName) {
7524 return internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
7525 }
7526
7527 /**
7528 * @license
7529 * Copyright Google LLC All Rights Reserved.
7530 *
7531 * Use of this source code is governed by an MIT-style license that can be
7532 * found in the LICENSE file at https://angular.io/license
7533 */
7534 /* Closure variables holding messages must be named `MSG_[A-Z0-9]+` */
7535 const CLOSURE_TRANSLATION_VAR_PREFIX = 'MSG_';
7536 /**
7537 * Prefix for non-`goog.getMsg` i18n-related vars.
7538 * Note: the prefix uses lowercase characters intentionally due to a Closure behavior that
7539 * considers variables like `I18N_0` as constants and throws an error when their value changes.
7540 */
7541 const TRANSLATION_VAR_PREFIX = 'i18n_';
7542 /** Name of the i18n attributes **/
7543 const I18N_ATTR = 'i18n';
7544 const I18N_ATTR_PREFIX = 'i18n-';
7545 /** Prefix of var expressions used in ICUs */
7546 const I18N_ICU_VAR_PREFIX = 'VAR_';
7547 /** Prefix of ICU expressions for post processing */
7548 const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
7549 /** Placeholder wrapper for i18n expressions **/
7550 const I18N_PLACEHOLDER_SYMBOL = '�';
7551 function isI18nAttribute(name) {
7552 return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX);
7553 }
7554 function isI18nRootNode(meta) {
7555 return meta instanceof Message;
7556 }
7557 function isSingleI18nIcu(meta) {
7558 return isI18nRootNode(meta) && meta.nodes.length === 1 && meta.nodes[0] instanceof Icu$1;
7559 }
7560 function hasI18nMeta(node) {
7561 return !!node.i18n;
7562 }
7563 function hasI18nAttrs(element) {
7564 return element.attrs.some((attr) => isI18nAttribute(attr.name));
7565 }
7566 function icuFromI18nMessage(message) {
7567 return message.nodes[0];
7568 }
7569 function wrapI18nPlaceholder(content, contextId = 0) {
7570 const blockId = contextId > 0 ? `:${contextId}` : '';
7571 return `${I18N_PLACEHOLDER_SYMBOL}${content}${blockId}${I18N_PLACEHOLDER_SYMBOL}`;
7572 }
7573 function assembleI18nBoundString(strings, bindingStartIndex = 0, contextId = 0) {
7574 if (!strings.length)
7575 return '';
7576 let acc = '';
7577 const lastIdx = strings.length - 1;
7578 for (let i = 0; i < lastIdx; i++) {
7579 acc += `${strings[i]}${wrapI18nPlaceholder(bindingStartIndex + i, contextId)}`;
7580 }
7581 acc += strings[lastIdx];
7582 return acc;
7583 }
7584 function getSeqNumberGenerator(startsAt = 0) {
7585 let current = startsAt;
7586 return () => current++;
7587 }
7588 function placeholdersToParams(placeholders) {
7589 const params = {};
7590 placeholders.forEach((values, key) => {
7591 params[key] = literal(values.length > 1 ? `[${values.join('|')}]` : values[0]);
7592 });
7593 return params;
7594 }
7595 function updatePlaceholderMap(map, name, ...values) {
7596 const current = map.get(name) || [];
7597 current.push(...values);
7598 map.set(name, current);
7599 }
7600 function assembleBoundTextPlaceholders(meta, bindingStartIndex = 0, contextId = 0) {
7601 const startIdx = bindingStartIndex;
7602 const placeholders = new Map();
7603 const node = meta instanceof Message ? meta.nodes.find(node => node instanceof Container) : meta;
7604 if (node) {
7605 node
7606 .children
7607 .filter((child) => child instanceof Placeholder)
7608 .forEach((child, idx) => {
7609 const content = wrapI18nPlaceholder(startIdx + idx, contextId);
7610 updatePlaceholderMap(placeholders, child.name, content);
7611 });
7612 }
7613 return placeholders;
7614 }
7615 /**
7616 * Format the placeholder names in a map of placeholders to expressions.
7617 *
7618 * The placeholder names are converted from "internal" format (e.g. `START_TAG_DIV_1`) to "external"
7619 * format (e.g. `startTagDiv_1`).
7620 *
7621 * @param params A map of placeholder names to expressions.
7622 * @param useCamelCase whether to camelCase the placeholder name when formatting.
7623 * @returns A new map of formatted placeholder names to expressions.
7624 */
7625 function i18nFormatPlaceholderNames(params = {}, useCamelCase) {
7626 const _params = {};
7627 if (params && Object.keys(params).length) {
7628 Object.keys(params).forEach(key => _params[formatI18nPlaceholderName(key, useCamelCase)] = params[key]);
7629 }
7630 return _params;
7631 }
7632 /**
7633 * Converts internal placeholder names to public-facing format
7634 * (for example to use in goog.getMsg call).
7635 * Example: `START_TAG_DIV_1` is converted to `startTagDiv_1`.
7636 *
7637 * @param name The placeholder name that should be formatted
7638 * @returns Formatted placeholder name
7639 */
7640 function formatI18nPlaceholderName(name, useCamelCase = true) {
7641 const publicName = toPublicName(name);
7642 if (!useCamelCase) {
7643 return publicName;
7644 }
7645 const chunks = publicName.split('_');
7646 if (chunks.length === 1) {
7647 // if no "_" found - just lowercase the value
7648 return name.toLowerCase();
7649 }
7650 let postfix;
7651 // eject last element if it's a number
7652 if (/^\d+$/.test(chunks[chunks.length - 1])) {
7653 postfix = chunks.pop();
7654 }
7655 let raw = chunks.shift().toLowerCase();
7656 if (chunks.length) {
7657 raw += chunks.map(c => c.charAt(0).toUpperCase() + c.slice(1).toLowerCase()).join('');
7658 }
7659 return postfix ? `${raw}_${postfix}` : raw;
7660 }
7661 /**
7662 * Generates a prefix for translation const name.
7663 *
7664 * @param extra Additional local prefix that should be injected into translation var name
7665 * @returns Complete translation const prefix
7666 */
7667 function getTranslationConstPrefix(extra) {
7668 return `${CLOSURE_TRANSLATION_VAR_PREFIX}${extra}`.toUpperCase();
7669 }
7670 /**
7671 * Generate AST to declare a variable. E.g. `var I18N_1;`.
7672 * @param variable the name of the variable to declare.
7673 */
7674 function declareI18nVariable(variable) {
7675 return new DeclareVarStmt(variable.name, undefined, INFERRED_TYPE, undefined, variable.sourceSpan);
7676 }
7677
7678 /**
7679 * @license
7680 * Copyright Google LLC All Rights Reserved.
7681 *
7682 * Use of this source code is governed by an MIT-style license that can be
7683 * found in the LICENSE file at https://angular.io/license
7684 */
7685 /**
7686 * Checks whether an object key contains potentially unsafe chars, thus the key should be wrapped in
7687 * quotes. Note: we do not wrap all keys into quotes, as it may have impact on minification and may
7688 * bot work in some cases when object keys are mangled by minifier.
7689 *
7690 * TODO(FW-1136): this is a temporary solution, we need to come up with a better way of working with
7691 * inputs that contain potentially unsafe chars.
7692 */
7693 const UNSAFE_OBJECT_KEY_NAME_REGEXP = /[-.]/;
7694 /** Name of the temporary to use during data binding */
7695 const TEMPORARY_NAME = '_t';
7696 /** Name of the context parameter passed into a template function */
7697 const CONTEXT_NAME = 'ctx';
7698 /** Name of the RenderFlag passed into a template function */
7699 const RENDER_FLAGS = 'rf';
7700 /** The prefix reference variables */
7701 const REFERENCE_PREFIX = '_r';
7702 /** The name of the implicit context reference */
7703 const IMPLICIT_REFERENCE = '$implicit';
7704 /** Non bindable attribute name **/
7705 const NON_BINDABLE_ATTR = 'ngNonBindable';
7706 /**
7707 * Creates an allocator for a temporary variable.
7708 *
7709 * A variable declaration is added to the statements the first time the allocator is invoked.
7710 */
7711 function temporaryAllocator(statements, name) {
7712 let temp = null;
7713 return () => {
7714 if (!temp) {
7715 statements.push(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE));
7716 temp = variable(name);
7717 }
7718 return temp;
7719 };
7720 }
7721 function unsupported(feature) {
7722 if (this) {
7723 throw new Error(`Builder ${this.constructor.name} doesn't support ${feature} yet`);
7724 }
7725 throw new Error(`Feature ${feature} is not supported yet`);
7726 }
7727 function invalid$1(arg) {
7728 throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
7729 }
7730 function asLiteral(value) {
7731 if (Array.isArray(value)) {
7732 return literalArr(value.map(asLiteral));
7733 }
7734 return literal(value, INFERRED_TYPE);
7735 }
7736 function conditionallyCreateMapObjectLiteral(keys, keepDeclared) {
7737 if (Object.getOwnPropertyNames(keys).length > 0) {
7738 return mapToExpression(keys, keepDeclared);
7739 }
7740 return null;
7741 }
7742 function mapToExpression(map, keepDeclared) {
7743 return literalMap(Object.getOwnPropertyNames(map).map(key => {
7744 // canonical syntax: `dirProp: publicProp`
7745 // if there is no `:`, use dirProp = elProp
7746 const value = map[key];
7747 let declaredName;
7748 let publicName;
7749 let minifiedName;
7750 let needsDeclaredName;
7751 if (Array.isArray(value)) {
7752 [publicName, declaredName] = value;
7753 minifiedName = key;
7754 needsDeclaredName = publicName !== declaredName;
7755 }
7756 else {
7757 [declaredName, publicName] = splitAtColon(key, [key, value]);
7758 minifiedName = declaredName;
7759 // Only include the declared name if extracted from the key, i.e. the key contains a colon.
7760 // Otherwise the declared name should be omitted even if it is different from the public name,
7761 // as it may have already been minified.
7762 needsDeclaredName = publicName !== declaredName && key.includes(':');
7763 }
7764 return {
7765 key: minifiedName,
7766 // put quotes around keys that contain potentially unsafe characters
7767 quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(minifiedName),
7768 value: (keepDeclared && needsDeclaredName) ?
7769 literalArr([asLiteral(publicName), asLiteral(declaredName)]) :
7770 asLiteral(publicName)
7771 };
7772 }));
7773 }
7774 /**
7775 * Remove trailing null nodes as they are implied.
7776 */
7777 function trimTrailingNulls(parameters) {
7778 while (isNull(parameters[parameters.length - 1])) {
7779 parameters.pop();
7780 }
7781 return parameters;
7782 }
7783 function getQueryPredicate(query, constantPool) {
7784 if (Array.isArray(query.predicate)) {
7785 let predicate = [];
7786 query.predicate.forEach((selector) => {
7787 // Each item in predicates array may contain strings with comma-separated refs
7788 // (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them
7789 // as separate array entities
7790 const selectors = selector.split(',').map(token => literal(token.trim()));
7791 predicate.push(...selectors);
7792 });
7793 return constantPool.getConstLiteral(literalArr(predicate), true);
7794 }
7795 else {
7796 return query.predicate;
7797 }
7798 }
7799 /**
7800 * A representation for an object literal used during codegen of definition objects. The generic
7801 * type `T` allows to reference a documented type of the generated structure, such that the
7802 * property names that are set can be resolved to their documented declaration.
7803 */
7804 class DefinitionMap {
7805 constructor() {
7806 this.values = [];
7807 }
7808 set(key, value) {
7809 if (value) {
7810 this.values.push({ key: key, value, quoted: false });
7811 }
7812 }
7813 toLiteralMap() {
7814 return literalMap(this.values);
7815 }
7816 }
7817 /**
7818 * Extract a map of properties to values for a given element or template node, which can be used
7819 * by the directive matching machinery.
7820 *
7821 * @param elOrTpl the element or template in question
7822 * @return an object set up for directive matching. For attributes on the element/template, this
7823 * object maps a property name to its (static) value. For any bindings, this map simply maps the
7824 * property name to an empty string.
7825 */
7826 function getAttrsForDirectiveMatching(elOrTpl) {
7827 const attributesMap = {};
7828 if (elOrTpl instanceof Template && elOrTpl.tagName !== 'ng-template') {
7829 elOrTpl.templateAttrs.forEach(a => attributesMap[a.name] = '');
7830 }
7831 else {
7832 elOrTpl.attributes.forEach(a => {
7833 if (!isI18nAttribute(a.name)) {
7834 attributesMap[a.name] = a.value;
7835 }
7836 });
7837 elOrTpl.inputs.forEach(i => {
7838 attributesMap[i.name] = '';
7839 });
7840 elOrTpl.outputs.forEach(o => {
7841 attributesMap[o.name] = '';
7842 });
7843 }
7844 return attributesMap;
7845 }
7846 /** Returns a call expression to a chained instruction, e.g. `property(params[0])(params[1])`. */
7847 function chainedInstruction(reference, calls, span) {
7848 let expression = importExpr(reference, null, span);
7849 if (calls.length > 0) {
7850 for (let i = 0; i < calls.length; i++) {
7851 expression = expression.callFn(calls[i], span);
7852 }
7853 }
7854 else {
7855 // Add a blank invocation, in case the `calls` array is empty.
7856 expression = expression.callFn([], span);
7857 }
7858 return expression;
7859 }
7860 /**
7861 * Gets the number of arguments expected to be passed to a generated instruction in the case of
7862 * interpolation instructions.
7863 * @param interpolation An interpolation ast
7864 */
7865 function getInterpolationArgsLength(interpolation) {
7866 const { expressions, strings } = interpolation;
7867 if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
7868 // If the interpolation has one interpolated value, but the prefix and suffix are both empty
7869 // strings, we only pass one argument, to a special instruction like `propertyInterpolate` or
7870 // `textInterpolate`.
7871 return 1;
7872 }
7873 else {
7874 return expressions.length + strings.length;
7875 }
7876 }
7877
7878 /**
7879 * @license
7880 * Copyright Google LLC All Rights Reserved.
7881 *
7882 * Use of this source code is governed by an MIT-style license that can be
7883 * found in the LICENSE file at https://angular.io/license
7884 */
7885 var R3FactoryDelegateType;
7886 (function (R3FactoryDelegateType) {
7887 R3FactoryDelegateType[R3FactoryDelegateType["Class"] = 0] = "Class";
7888 R3FactoryDelegateType[R3FactoryDelegateType["Function"] = 1] = "Function";
7889 R3FactoryDelegateType[R3FactoryDelegateType["Factory"] = 2] = "Factory";
7890 })(R3FactoryDelegateType || (R3FactoryDelegateType = {}));
7891 var R3FactoryTarget;
7892 (function (R3FactoryTarget) {
7893 R3FactoryTarget[R3FactoryTarget["Directive"] = 0] = "Directive";
7894 R3FactoryTarget[R3FactoryTarget["Component"] = 1] = "Component";
7895 R3FactoryTarget[R3FactoryTarget["Injectable"] = 2] = "Injectable";
7896 R3FactoryTarget[R3FactoryTarget["Pipe"] = 3] = "Pipe";
7897 R3FactoryTarget[R3FactoryTarget["NgModule"] = 4] = "NgModule";
7898 })(R3FactoryTarget || (R3FactoryTarget = {}));
7899 /**
7900 * Resolved type of a dependency.
7901 *
7902 * Occasionally, dependencies will have special significance which is known statically. In that
7903 * case the `R3ResolvedDependencyType` informs the factory generator that a particular dependency
7904 * should be generated specially (usually by calling a special injection function instead of the
7905 * standard one).
7906 */
7907 var R3ResolvedDependencyType;
7908 (function (R3ResolvedDependencyType) {
7909 /**
7910 * A normal token dependency.
7911 */
7912 R3ResolvedDependencyType[R3ResolvedDependencyType["Token"] = 0] = "Token";
7913 /**
7914 * The dependency is for an attribute.
7915 *
7916 * The token expression is a string representing the attribute name.
7917 */
7918 R3ResolvedDependencyType[R3ResolvedDependencyType["Attribute"] = 1] = "Attribute";
7919 /**
7920 * Injecting the `ChangeDetectorRef` token. Needs special handling when injected into a pipe.
7921 */
7922 R3ResolvedDependencyType[R3ResolvedDependencyType["ChangeDetectorRef"] = 2] = "ChangeDetectorRef";
7923 /**
7924 * An invalid dependency (no token could be determined). An error should be thrown at runtime.
7925 */
7926 R3ResolvedDependencyType[R3ResolvedDependencyType["Invalid"] = 3] = "Invalid";
7927 })(R3ResolvedDependencyType || (R3ResolvedDependencyType = {}));
7928 /**
7929 * Construct a factory function expression for the given `R3FactoryMetadata`.
7930 */
7931 function compileFactoryFunction(meta) {
7932 const t = variable('t');
7933 const statements = [];
7934 let ctorDepsType = NONE_TYPE;
7935 // The type to instantiate via constructor invocation. If there is no delegated factory, meaning
7936 // this type is always created by constructor invocation, then this is the type-to-create
7937 // parameter provided by the user (t) if specified, or the current type if not. If there is a
7938 // delegated factory (which is used to create the current type) then this is only the type-to-
7939 // create parameter (t).
7940 const typeForCtor = !isDelegatedMetadata(meta) ?
7941 new BinaryOperatorExpr(BinaryOperator.Or, t, meta.internalType) :
7942 t;
7943 let ctorExpr = null;
7944 if (meta.deps !== null) {
7945 // There is a constructor (either explicitly or implicitly defined).
7946 if (meta.deps !== 'invalid') {
7947 ctorExpr = new InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.injectFn, meta.target === R3FactoryTarget.Pipe));
7948 ctorDepsType = createCtorDepsType(meta.deps);
7949 }
7950 }
7951 else {
7952 const baseFactory = variable(${meta.name}_BaseFactory`);
7953 const getInheritedFactory = importExpr(Identifiers$1.getInheritedFactory);
7954 const baseFactoryStmt = baseFactory
7955 .set(getInheritedFactory.callFn([meta.internalType], /* sourceSpan */ undefined, /* pure */ true))
7956 .toDeclStmt(INFERRED_TYPE, [StmtModifier.Exported, StmtModifier.Final]);
7957 statements.push(baseFactoryStmt);
7958 // There is no constructor, use the base class' factory to construct typeForCtor.
7959 ctorExpr = baseFactory.callFn([typeForCtor]);
7960 }
7961 const ctorExprFinal = ctorExpr;
7962 const body = [];
7963 let retExpr = null;
7964 function makeConditionalFactory(nonCtorExpr) {
7965 const r = variable('r');
7966 body.push(r.set(NULL_EXPR).toDeclStmt());
7967 let ctorStmt = null;
7968 if (ctorExprFinal !== null) {
7969 ctorStmt = r.set(ctorExprFinal).toStmt();
7970 }
7971 else {
7972 ctorStmt = importExpr(Identifiers$1.invalidFactory).callFn([]).toStmt();
7973 }
7974 body.push(ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()]));
7975 return r;
7976 }
7977 if (isDelegatedMetadata(meta) && meta.delegateType === R3FactoryDelegateType.Factory) {
7978 const delegateFactory = variable(${meta.name}_BaseFactory`);
7979 const getFactoryOf = importExpr(Identifiers$1.getFactoryOf);
7980 if (meta.delegate.isEquivalent(meta.internalType)) {
7981 throw new Error(`Illegal state: compiling factory that delegates to itself`);
7982 }
7983 const delegateFactoryStmt = delegateFactory.set(getFactoryOf.callFn([meta.delegate])).toDeclStmt(INFERRED_TYPE, [
7984 StmtModifier.Exported, StmtModifier.Final
7985 ]);
7986 statements.push(delegateFactoryStmt);
7987 retExpr = makeConditionalFactory(delegateFactory.callFn([]));
7988 }
7989 else if (isDelegatedMetadata(meta)) {
7990 // This type is created with a delegated factory. If a type parameter is not specified, call
7991 // the factory instead.
7992 const delegateArgs = injectDependencies(meta.delegateDeps, meta.injectFn, meta.target === R3FactoryTarget.Pipe);
7993 // Either call `new delegate(...)` or `delegate(...)` depending on meta.delegateType.
7994 const factoryExpr = new (meta.delegateType === R3FactoryDelegateType.Class ?
7995 InstantiateExpr :
7996 InvokeFunctionExpr)(meta.delegate, delegateArgs);
7997 retExpr = makeConditionalFactory(factoryExpr);
7998 }
7999 else if (isExpressionFactoryMetadata(meta)) {
8000 // TODO(alxhub): decide whether to lower the value here or in the caller
8001 retExpr = makeConditionalFactory(meta.expression);
8002 }
8003 else {
8004 retExpr = ctorExpr;
8005 }
8006 if (retExpr !== null) {
8007 body.push(new ReturnStatement(retExpr));
8008 }
8009 else {
8010 body.push(importExpr(Identifiers$1.invalidFactory).callFn([]).toStmt());
8011 }
8012 return {
8013 factory: fn([new FnParam('t', DYNAMIC_TYPE)], body, INFERRED_TYPE, undefined, `${meta.name}_Factory`),
8014 statements,
8015 type: expressionType(importExpr(Identifiers$1.FactoryDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount), ctorDepsType]))
8016 };
8017 }
8018 function injectDependencies(deps, injectFn, isPipe) {
8019 return deps.map((dep, index) => compileInjectDependency(dep, injectFn, isPipe, index));
8020 }
8021 function compileInjectDependency(dep, injectFn, isPipe, index) {
8022 // Interpret the dependency according to its resolved type.
8023 switch (dep.resolved) {
8024 case R3ResolvedDependencyType.Token:
8025 case R3ResolvedDependencyType.ChangeDetectorRef:
8026 // Build up the injection flags according to the metadata.
8027 const flags = 0 /* Default */ | (dep.self ? 2 /* Self */ : 0) |
8028 (dep.skipSelf ? 4 /* SkipSelf */ : 0) | (dep.host ? 1 /* Host */ : 0) |
8029 (dep.optional ? 8 /* Optional */ : 0);
8030 // If this dependency is optional or otherwise has non-default flags, then additional
8031 // parameters describing how to inject the dependency must be passed to the inject function
8032 // that's being used.
8033 let flagsParam = (flags !== 0 /* Default */ || dep.optional) ? literal(flags) : null;
8034 // We have a separate instruction for injecting ChangeDetectorRef into a pipe.
8035 if (isPipe && dep.resolved === R3ResolvedDependencyType.ChangeDetectorRef) {
8036 return importExpr(Identifiers$1.injectPipeChangeDetectorRef).callFn(flagsParam ? [flagsParam] : []);
8037 }
8038 // Build up the arguments to the injectFn call.
8039 const injectArgs = [dep.token];
8040 if (flagsParam) {
8041 injectArgs.push(flagsParam);
8042 }
8043 return importExpr(injectFn).callFn(injectArgs);
8044 case R3ResolvedDependencyType.Attribute:
8045 // In the case of attributes, the attribute name in question is given as the token.
8046 return importExpr(Identifiers$1.injectAttribute).callFn([dep.token]);
8047 case R3ResolvedDependencyType.Invalid:
8048 return importExpr(Identifiers$1.invalidFactoryDep).callFn([literal(index)]);
8049 default:
8050 return unsupported(`Unknown R3ResolvedDependencyType: ${R3ResolvedDependencyType[dep.resolved]}`);
8051 }
8052 }
8053 function createCtorDepsType(deps) {
8054 let hasTypes = false;
8055 const attributeTypes = deps.map(dep => {
8056 const type = createCtorDepType(dep);
8057 if (type !== null) {
8058 hasTypes = true;
8059 return type;
8060 }
8061 else {
8062 return literal(null);
8063 }
8064 });
8065 if (hasTypes) {
8066 return expressionType(literalArr(attributeTypes));
8067 }
8068 else {
8069 return NONE_TYPE;
8070 }
8071 }
8072 function createCtorDepType(dep) {
8073 const entries = [];
8074 if (dep.resolved === R3ResolvedDependencyType.Attribute) {
8075 if (dep.attribute !== null) {
8076 entries.push({ key: 'attribute', value: dep.attribute, quoted: false });
8077 }
8078 }
8079 if (dep.optional) {
8080 entries.push({ key: 'optional', value: literal(true), quoted: false });
8081 }
8082 if (dep.host) {
8083 entries.push({ key: 'host', value: literal(true), quoted: false });
8084 }
8085 if (dep.self) {
8086 entries.push({ key: 'self', value: literal(true), quoted: false });
8087 }
8088 if (dep.skipSelf) {
8089 entries.push({ key: 'skipSelf', value: literal(true), quoted: false });
8090 }
8091 return entries.length > 0 ? literalMap(entries) : null;
8092 }
8093 function isDelegatedMetadata(meta) {
8094 return meta.delegateType !== undefined;
8095 }
8096 function isExpressionFactoryMetadata(meta) {
8097 return meta.expression !== undefined;
8098 }
8099
8100 /**
8101 * @license
8102 * Copyright Google LLC All Rights Reserved.
8103 *
8104 * Use of this source code is governed by an MIT-style license that can be
8105 * found in the LICENSE file at https://angular.io/license
8106 */
8107 function compileInjectable(meta) {
8108 let result = null;
8109 const factoryMeta = {
8110 name: meta.name,
8111 type: meta.type,
8112 internalType: meta.internalType,
8113 typeArgumentCount: meta.typeArgumentCount,
8114 deps: [],
8115 injectFn: Identifiers.inject,
8116 target: R3FactoryTarget.Injectable,
8117 };
8118 if (meta.useClass !== undefined) {
8119 // meta.useClass has two modes of operation. Either deps are specified, in which case `new` is
8120 // used to instantiate the class with dependencies injected, or deps are not specified and
8121 // the factory of the class is used to instantiate it.
8122 //
8123 // A special case exists for useClass: Type where Type is the injectable type itself and no
8124 // deps are specified, in which case 'useClass' is effectively ignored.
8125 const useClassOnSelf = meta.useClass.isEquivalent(meta.internalType);
8126 let deps = undefined;
8127 if (meta.userDeps !== undefined) {
8128 deps = meta.userDeps;
8129 }
8130 if (deps !== undefined) {
8131 // factory: () => new meta.useClass(...deps)
8132 result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { delegate: meta.useClass, delegateDeps: deps, delegateType: R3FactoryDelegateType.Class }));
8133 }
8134 else if (useClassOnSelf) {
8135 result = compileFactoryFunction(factoryMeta);
8136 }
8137 else {
8138 result = delegateToFactory(meta.type.value, meta.useClass);
8139 }
8140 }
8141 else if (meta.useFactory !== undefined) {
8142 if (meta.userDeps !== undefined) {
8143 result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { delegate: meta.useFactory, delegateDeps: meta.userDeps || [], delegateType: R3FactoryDelegateType.Function }));
8144 }
8145 else {
8146 result = {
8147 statements: [],
8148 factory: fn([], [new ReturnStatement(meta.useFactory.callFn([]))])
8149 };
8150 }
8151 }
8152 else if (meta.useValue !== undefined) {
8153 // Note: it's safe to use `meta.useValue` instead of the `USE_VALUE in meta` check used for
8154 // client code because meta.useValue is an Expression which will be defined even if the actual
8155 // value is undefined.
8156 result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { expression: meta.useValue }));
8157 }
8158 else if (meta.useExisting !== undefined) {
8159 // useExisting is an `inject` call on the existing token.
8160 result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { expression: importExpr(Identifiers.inject).callFn([meta.useExisting]) }));
8161 }
8162 else {
8163 result = delegateToFactory(meta.type.value, meta.internalType);
8164 }
8165 const token = meta.internalType;
8166 const injectableProps = { token, factory: result.factory };
8167 // Only generate providedIn property if it has a non-null value
8168 if (meta.providedIn.value !== null) {
8169 injectableProps.providedIn = meta.providedIn;
8170 }
8171 const expression = importExpr(Identifiers.ɵɵdefineInjectable).callFn([mapToMapExpression(injectableProps)]);
8172 const type = new ExpressionType(importExpr(Identifiers.InjectableDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount)]));
8173 return {
8174 expression,
8175 type,
8176 statements: result.statements,
8177 };
8178 }
8179 function delegateToFactory(type, internalType) {
8180 return {
8181 statements: [],
8182 // If types are the same, we can generate `factory: type.ɵfac`
8183 // If types are different, we have to generate a wrapper function to ensure
8184 // the internal type has been resolved (`factory: function(t) { return type.ɵfac(t); }`)
8185 factory: type.node === internalType.node ?
8186 internalType.prop('ɵfac') :
8187 fn([new FnParam('t', DYNAMIC_TYPE)], [new ReturnStatement(internalType.callMethod('ɵfac', [variable('t')]))])
8188 };
8189 }
8190
8191 /**
8192 * @license
8193 * Copyright Google LLC All Rights Reserved.
8194 *
8195 * Use of this source code is governed by an MIT-style license that can be
8196 * found in the LICENSE file at https://angular.io/license
8197 */
8198 const UNUSABLE_INTERPOLATION_REGEXPS = [
8199 /^\s*$/,
8200 /[<>]/,
8201 /^[{}]$/,
8202 /&(#|[a-z])/i,
8203 /^\/\//,
8204 ];
8205 function assertInterpolationSymbols(identifier, value) {
8206 if (value != null && !(Array.isArray(value) && value.length == 2)) {
8207 throw new Error(`Expected '${identifier}' to be an array, [start, end].`);
8208 }
8209 else if (value != null) {
8210 const start = value[0];
8211 const end = value[1];
8212 // Check for unusable interpolation symbols
8213 UNUSABLE_INTERPOLATION_REGEXPS.forEach(regexp => {
8214 if (regexp.test(start) || regexp.test(end)) {
8215 throw new Error(`['${start}', '${end}'] contains unusable interpolation symbol.`);
8216 }
8217 });
8218 }
8219 }
8220
8221 /**
8222 * @license
8223 * Copyright Google LLC All Rights Reserved.
8224 *
8225 * Use of this source code is governed by an MIT-style license that can be
8226 * found in the LICENSE file at https://angular.io/license
8227 */
8228 class InterpolationConfig {
8229 constructor(start, end) {
8230 this.start = start;
8231 this.end = end;
8232 }
8233 static fromArray(markers) {
8234 if (!markers) {
8235 return DEFAULT_INTERPOLATION_CONFIG;
8236 }
8237 assertInterpolationSymbols('interpolation', markers);
8238 return new InterpolationConfig(markers[0], markers[1]);
8239 }
8240 }
8241 const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}');
8242
8243 /**
8244 * @license
8245 * Copyright Google LLC All Rights Reserved.
8246 *
8247 * Use of this source code is governed by an MIT-style license that can be
8248 * found in the LICENSE file at https://angular.io/license
8249 */
8250 /**
8251 * In TypeScript, tagged template functions expect a "template object", which is an array of
8252 * "cooked" strings plus a `raw` property that contains an array of "raw" strings. This is
8253 * typically constructed with a function called `__makeTemplateObject(cooked, raw)`, but it may not
8254 * be available in all environments.
8255 *
8256 * This is a JavaScript polyfill that uses __makeTemplateObject when it's available, but otherwise
8257 * creates an inline helper with the same functionality.
8258 *
8259 * In the inline function, if `Object.defineProperty` is available we use that to attach the `raw`
8260 * array.
8261 */
8262 const makeTemplateObjectPolyfill = '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})';
8263 class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
8264 constructor() {
8265 super(false);
8266 }
8267 visitDeclareClassStmt(stmt, ctx) {
8268 ctx.pushClass(stmt);
8269 this._visitClassConstructor(stmt, ctx);
8270 if (stmt.parent != null) {
8271 ctx.print(stmt, `${stmt.name}.prototype = Object.create(`);
8272 stmt.parent.visitExpression(this, ctx);
8273 ctx.println(stmt, `.prototype);`);
8274 }
8275 stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx));
8276 stmt.methods.forEach((method) => this._visitClassMethod(stmt, method, ctx));
8277 ctx.popClass();
8278 return null;
8279 }
8280 _visitClassConstructor(stmt, ctx) {
8281 ctx.print(stmt, `function ${stmt.name}(`);
8282 if (stmt.constructorMethod != null) {
8283 this._visitParams(stmt.constructorMethod.params, ctx);
8284 }
8285 ctx.println(stmt, `) {`);
8286 ctx.incIndent();
8287 if (stmt.constructorMethod != null) {
8288 if (stmt.constructorMethod.body.length > 0) {
8289 ctx.println(stmt, `var self = this;`);
8290 this.visitAllStatements(stmt.constructorMethod.body, ctx);
8291 }
8292 }
8293 ctx.decIndent();
8294 ctx.println(stmt, `}`);
8295 }
8296 _visitClassGetter(stmt, getter, ctx) {
8297 ctx.println(stmt, `Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`);
8298 ctx.incIndent();
8299 if (getter.body.length > 0) {
8300 ctx.println(stmt, `var self = this;`);
8301 this.visitAllStatements(getter.body, ctx);
8302 }
8303 ctx.decIndent();
8304 ctx.println(stmt, `}});`);
8305 }
8306 _visitClassMethod(stmt, method, ctx) {
8307 ctx.print(stmt, `${stmt.name}.prototype.${method.name} = function(`);
8308 this._visitParams(method.params, ctx);
8309 ctx.println(stmt, `) {`);
8310 ctx.incIndent();
8311 if (method.body.length > 0) {
8312 ctx.println(stmt, `var self = this;`);
8313 this.visitAllStatements(method.body, ctx);
8314 }
8315 ctx.decIndent();
8316 ctx.println(stmt, `};`);
8317 }
8318 visitWrappedNodeExpr(ast, ctx) {
8319 throw new Error('Cannot emit a WrappedNodeExpr in Javascript.');
8320 }
8321 visitReadVarExpr(ast, ctx) {
8322 if (ast.builtin === BuiltinVar.This) {
8323 ctx.print(ast, 'self');
8324 }
8325 else if (ast.builtin === BuiltinVar.Super) {
8326 throw new Error(`'super' needs to be handled at a parent ast node, not at the variable level!`);
8327 }
8328 else {
8329 super.visitReadVarExpr(ast, ctx);
8330 }
8331 return null;
8332 }
8333 visitDeclareVarStmt(stmt, ctx) {
8334 ctx.print(stmt, `var ${stmt.name}`);
8335 if (stmt.value) {
8336 ctx.print(stmt, ' = ');
8337 stmt.value.visitExpression(this, ctx);
8338 }
8339 ctx.println(stmt, `;`);
8340 return null;
8341 }
8342 visitCastExpr(ast, ctx) {
8343 ast.value.visitExpression(this, ctx);
8344 return null;
8345 }
8346 visitInvokeFunctionExpr(expr, ctx) {
8347 const fnExpr = expr.fn;
8348 if (fnExpr instanceof ReadVarExpr && fnExpr.builtin === BuiltinVar.Super) {
8349 ctx.currentClass.parent.visitExpression(this, ctx);
8350 ctx.print(expr, `.call(this`);
8351 if (expr.args.length > 0) {
8352 ctx.print(expr, `, `);
8353 this.visitAllExpressions(expr.args, ctx, ',');
8354 }
8355 ctx.print(expr, `)`);
8356 }
8357 else {
8358 super.visitInvokeFunctionExpr(expr, ctx);
8359 }
8360 return null;
8361 }
8362 visitTaggedTemplateExpr(ast, ctx) {
8363 // The following convoluted piece of code is effectively the downlevelled equivalent of
8364 // ```
8365 // tag`...`
8366 // ```
8367 // which is effectively like:
8368 // ```
8369 // tag(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
8370 // ```
8371 const elements = ast.template.elements;
8372 ast.tag.visitExpression(this, ctx);
8373 ctx.print(ast, `(${makeTemplateObjectPolyfill}(`);
8374 ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.text, false)).join(', ')}], `);
8375 ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.rawText, false)).join(', ')}])`);
8376 ast.template.expressions.forEach(expression => {
8377 ctx.print(ast, ', ');
8378 expression.visitExpression(this, ctx);
8379 });
8380 ctx.print(ast, ')');
8381 return null;
8382 }
8383 visitFunctionExpr(ast, ctx) {
8384 ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`);
8385 this._visitParams(ast.params, ctx);
8386 ctx.println(ast, `) {`);
8387 ctx.incIndent();
8388 this.visitAllStatements(ast.statements, ctx);
8389 ctx.decIndent();
8390 ctx.print(ast, `}`);
8391 return null;
8392 }
8393 visitDeclareFunctionStmt(stmt, ctx) {
8394 ctx.print(stmt, `function ${stmt.name}(`);
8395 this._visitParams(stmt.params, ctx);
8396 ctx.println(stmt, `) {`);
8397 ctx.incIndent();
8398 this.visitAllStatements(stmt.statements, ctx);
8399 ctx.decIndent();
8400 ctx.println(stmt, `}`);
8401 return null;
8402 }
8403 visitTryCatchStmt(stmt, ctx) {
8404 ctx.println(stmt, `try {`);
8405 ctx.incIndent();
8406 this.visitAllStatements(stmt.bodyStmts, ctx);
8407 ctx.decIndent();
8408 ctx.println(stmt, `} catch (${CATCH_ERROR_VAR$1.name}) {`);
8409 ctx.incIndent();
8410 const catchStmts = [CATCH_STACK_VAR$1.set(CATCH_ERROR_VAR$1.prop('stack')).toDeclStmt(null, [
8411 StmtModifier.Final
8412 ])].concat(stmt.catchStmts);
8413 this.visitAllStatements(catchStmts, ctx);
8414 ctx.decIndent();
8415 ctx.println(stmt, `}`);
8416 return null;
8417 }
8418 visitLocalizedString(ast, ctx) {
8419 // The following convoluted piece of code is effectively the downlevelled equivalent of
8420 // ```
8421 // $localize `...`
8422 // ```
8423 // which is effectively like:
8424 // ```
8425 // $localize(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
8426 // ```
8427 ctx.print(ast, `$localize(${makeTemplateObjectPolyfill}(`);
8428 const parts = [ast.serializeI18nHead()];
8429 for (let i = 1; i < ast.messageParts.length; i++) {
8430 parts.push(ast.serializeI18nTemplatePart(i));
8431 }
8432 ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.cooked, false)).join(', ')}], `);
8433 ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.raw, false)).join(', ')}])`);
8434 ast.expressions.forEach(expression => {
8435 ctx.print(ast, ', ');
8436 expression.visitExpression(this, ctx);
8437 });
8438 ctx.print(ast, ')');
8439 return null;
8440 }
8441 _visitParams(params, ctx) {
8442 this.visitAllObjects(param => ctx.print(null, param.name), params, ctx, ',');
8443 }
8444 getBuiltinMethodName(method) {
8445 let name;
8446 switch (method) {
8447 case BuiltinMethod.ConcatArray:
8448 name = 'concat';
8449 break;
8450 case BuiltinMethod.SubscribeObservable:
8451 name = 'subscribe';
8452 break;
8453 case BuiltinMethod.Bind:
8454 name = 'bind';
8455 break;
8456 default:
8457 throw new Error(`Unknown builtin method: ${method}`);
8458 }
8459 return name;
8460 }
8461 }
8462
8463 /**
8464 * @license
8465 * Copyright Google LLC All Rights Reserved.
8466 *
8467 * Use of this source code is governed by an MIT-style license that can be
8468 * found in the LICENSE file at https://angular.io/license
8469 */
8470 /**
8471 * The Trusted Types policy, or null if Trusted Types are not
8472 * enabled/supported, or undefined if the policy has not been created yet.
8473 */
8474 let policy;
8475 /**
8476 * Returns the Trusted Types policy, or null if Trusted Types are not
8477 * enabled/supported. The first call to this function will create the policy.
8478 */
8479 function getPolicy() {
8480 if (policy === undefined) {
8481 policy = null;
8482 if (_global.trustedTypes) {
8483 try {
8484 policy =
8485 _global.trustedTypes.createPolicy('angular#unsafe-jit', {
8486 createScript: (s) => s,
8487 });
8488 }
8489 catch (_a) {
8490 // trustedTypes.createPolicy throws if called with a name that is
8491 // already registered, even in report-only mode. Until the API changes,
8492 // catch the error not to break the applications functionally. In such
8493 // cases, the code will fall back to using strings.
8494 }
8495 }
8496 }
8497 return policy;
8498 }
8499 /**
8500 * Unsafely promote a string to a TrustedScript, falling back to strings when
8501 * Trusted Types are not available.
8502 * @security In particular, it must be assured that the provided string will
8503 * never cause an XSS vulnerability if used in a context that will be
8504 * interpreted and executed as a script by a browser, e.g. when calling eval.
8505 */
8506 function trustedScriptFromString(script) {
8507 var _a;
8508 return ((_a = getPolicy()) === null || _a === void 0 ? void 0 : _a.createScript(script)) || script;
8509 }
8510 /**
8511 * Unsafely call the Function constructor with the given string arguments.
8512 * @security This is a security-sensitive function; any use of this function
8513 * must go through security review. In particular, it must be assured that it
8514 * is only called from the JIT compiler, as use in other code can lead to XSS
8515 * vulnerabilities.
8516 */
8517 function newTrustedFunctionForJIT(...args) {
8518 if (!_global.trustedTypes) {
8519 // In environments that don't support Trusted Types, fall back to the most
8520 // straightforward implementation:
8521 return new Function(...args);
8522 }
8523 // Chrome currently does not support passing TrustedScript to the Function
8524 // constructor. The following implements the workaround proposed on the page
8525 // below, where the Chromium bug is also referenced:
8526 // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
8527 const fnArgs = args.slice(0, -1).join(',');
8528 const fnBody = args[args.length - 1];
8529 const body = `(function anonymous(${fnArgs}
8530) { ${fnBody}
8531})`;
8532 // Using eval directly confuses the compiler and prevents this module from
8533 // being stripped out of JS binaries even if not used. The global['eval']
8534 // indirection fixes that.
8535 const fn = _global['eval'](trustedScriptFromString(body));
8536 if (fn.bind === undefined) {
8537 // Workaround for a browser bug that only exists in Chrome 83, where passing
8538 // a TrustedScript to eval just returns the TrustedScript back without
8539 // evaluating it. In that case, fall back to the most straightforward
8540 // implementation:
8541 return new Function(...args);
8542 }
8543 // To completely mimic the behavior of calling "new Function", two more
8544 // things need to happen:
8545 // 1. Stringifying the resulting function should return its source code
8546 fn.toString = () => body;
8547 // 2. When calling the resulting function, `this` should refer to `global`
8548 return fn.bind(_global);
8549 // When Trusted Types support in Function constructors is widely available,
8550 // the implementation of this function can be simplified to:
8551 // return new Function(...args.map(a => trustedScriptFromString(a)));
8552 }
8553
8554 /**
8555 * @license
8556 * Copyright Google LLC All Rights Reserved.
8557 *
8558 * Use of this source code is governed by an MIT-style license that can be
8559 * found in the LICENSE file at https://angular.io/license
8560 */
8561 /**
8562 * A helper class to manage the evaluation of JIT generated code.
8563 */
8564 class JitEvaluator {
8565 /**
8566 *
8567 * @param sourceUrl The URL of the generated code.
8568 * @param statements An array of Angular statement AST nodes to be evaluated.
8569 * @param reflector A helper used when converting the statements to executable code.
8570 * @param createSourceMaps If true then create a source-map for the generated code and include it
8571 * inline as a source-map comment.
8572 * @returns A map of all the variables in the generated code.
8573 */
8574 evaluateStatements(sourceUrl, statements, reflector, createSourceMaps) {
8575 const converter = new JitEmitterVisitor(reflector);
8576 const ctx = EmitterVisitorContext.createRoot();
8577 // Ensure generated code is in strict mode
8578 if (statements.length > 0 && !isUseStrictStatement(statements[0])) {
8579 statements = [
8580 literal('use strict').toStmt(),
8581 ...statements,
8582 ];
8583 }
8584 converter.visitAllStatements(statements, ctx);
8585 converter.createReturnStmt(ctx);
8586 return this.evaluateCode(sourceUrl, ctx, converter.getArgs(), createSourceMaps);
8587 }
8588 /**
8589 * Evaluate a piece of JIT generated code.
8590 * @param sourceUrl The URL of this generated code.
8591 * @param ctx A context object that contains an AST of the code to be evaluated.
8592 * @param vars A map containing the names and values of variables that the evaluated code might
8593 * reference.
8594 * @param createSourceMap If true then create a source-map for the generated code and include it
8595 * inline as a source-map comment.
8596 * @returns The result of evaluating the code.
8597 */
8598 evaluateCode(sourceUrl, ctx, vars, createSourceMap) {
8599 let fnBody = `"use strict";${ctx.toSource()}\n//# sourceURL=${sourceUrl}`;
8600 const fnArgNames = [];
8601 const fnArgValues = [];
8602 for (const argName in vars) {
8603 fnArgValues.push(vars[argName]);
8604 fnArgNames.push(argName);
8605 }
8606 if (createSourceMap) {
8607 // using `new Function(...)` generates a header, 1 line of no arguments, 2 lines otherwise
8608 // E.g. ```
8609 // function anonymous(a,b,c
8610 // /**/) { ... }```
8611 // We don't want to hard code this fact, so we auto detect it via an empty function first.
8612 const emptyFn = newTrustedFunctionForJIT(...fnArgNames.concat('return null;')).toString();
8613 const headerLines = emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - 1;
8614 fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, headerLines).toJsComment()}`;
8615 }
8616 const fn = newTrustedFunctionForJIT(...fnArgNames.concat(fnBody));
8617 return this.executeFunction(fn, fnArgValues);
8618 }
8619 /**
8620 * Execute a JIT generated function by calling it.
8621 *
8622 * This method can be overridden in tests to capture the functions that are generated
8623 * by this `JitEvaluator` class.
8624 *
8625 * @param fn A function to execute.
8626 * @param args The arguments to pass to the function being executed.
8627 * @returns The return value of the executed function.
8628 */
8629 executeFunction(fn, args) {
8630 return fn(...args);
8631 }
8632 }
8633 /**
8634 * An Angular AST visitor that converts AST nodes into executable JavaScript code.
8635 */
8636 class JitEmitterVisitor extends AbstractJsEmitterVisitor {
8637 constructor(reflector) {
8638 super();
8639 this.reflector = reflector;
8640 this._evalArgNames = [];
8641 this._evalArgValues = [];
8642 this._evalExportedVars = [];
8643 }
8644 createReturnStmt(ctx) {
8645 const stmt = new ReturnStatement(new LiteralMapExpr(this._evalExportedVars.map(resultVar => new LiteralMapEntry(resultVar, variable(resultVar), false))));
8646 stmt.visitStatement(this, ctx);
8647 }
8648 getArgs() {
8649 const result = {};
8650 for (let i = 0; i < this._evalArgNames.length; i++) {
8651 result[this._evalArgNames[i]] = this._evalArgValues[i];
8652 }
8653 return result;
8654 }
8655 visitExternalExpr(ast, ctx) {
8656 this._emitReferenceToExternal(ast, this.reflector.resolveExternalReference(ast.value), ctx);
8657 return null;
8658 }
8659 visitWrappedNodeExpr(ast, ctx) {
8660 this._emitReferenceToExternal(ast, ast.node, ctx);
8661 return null;
8662 }
8663 visitDeclareVarStmt(stmt, ctx) {
8664 if (stmt.hasModifier(StmtModifier.Exported)) {
8665 this._evalExportedVars.push(stmt.name);
8666 }
8667 return super.visitDeclareVarStmt(stmt, ctx);
8668 }
8669 visitDeclareFunctionStmt(stmt, ctx) {
8670 if (stmt.hasModifier(StmtModifier.Exported)) {
8671 this._evalExportedVars.push(stmt.name);
8672 }
8673 return super.visitDeclareFunctionStmt(stmt, ctx);
8674 }
8675 visitDeclareClassStmt(stmt, ctx) {
8676 if (stmt.hasModifier(StmtModifier.Exported)) {
8677 this._evalExportedVars.push(stmt.name);
8678 }
8679 return super.visitDeclareClassStmt(stmt, ctx);
8680 }
8681 _emitReferenceToExternal(ast, value, ctx) {
8682 let id = this._evalArgValues.indexOf(value);
8683 if (id === -1) {
8684 id = this._evalArgValues.length;
8685 this._evalArgValues.push(value);
8686 const name = identifierName({ reference: value }) || 'val';
8687 this._evalArgNames.push(`jit_${name}_${id}`);
8688 }
8689 ctx.print(ast, this._evalArgNames[id]);
8690 }
8691 }
8692 function isUseStrictStatement(statement) {
8693 return statement.isEquivalent(literal('use strict').toStmt());
8694 }
8695
8696 /**
8697 * @license
8698 * Copyright Google LLC All Rights Reserved.
8699 *
8700 * Use of this source code is governed by an MIT-style license that can be
8701 * found in the LICENSE file at https://angular.io/license
8702 */
8703 const $EOF = 0;
8704 const $BSPACE = 8;
8705 const $TAB = 9;
8706 const $LF = 10;
8707 const $VTAB = 11;
8708 const $FF = 12;
8709 const $CR = 13;
8710 const $SPACE = 32;
8711 const $BANG = 33;
8712 const $DQ = 34;
8713 const $HASH = 35;
8714 const $$ = 36;
8715 const $PERCENT = 37;
8716 const $AMPERSAND = 38;
8717 const $SQ = 39;
8718 const $LPAREN = 40;
8719 const $RPAREN = 41;
8720 const $STAR = 42;
8721 const $PLUS = 43;
8722 const $COMMA = 44;
8723 const $MINUS = 45;
8724 const $PERIOD = 46;
8725 const $SLASH = 47;
8726 const $COLON = 58;
8727 const $SEMICOLON = 59;
8728 const $LT = 60;
8729 const $EQ = 61;
8730 const $GT = 62;
8731 const $QUESTION = 63;
8732 const $0 = 48;
8733 const $7 = 55;
8734 const $9 = 57;
8735 const $A = 65;
8736 const $E = 69;
8737 const $F = 70;
8738 const $X = 88;
8739 const $Z = 90;
8740 const $LBRACKET = 91;
8741 const $BACKSLASH = 92;
8742 const $RBRACKET = 93;
8743 const $CARET = 94;
8744 const $_ = 95;
8745 const $a = 97;
8746 const $b = 98;
8747 const $e = 101;
8748 const $f = 102;
8749 const $n = 110;
8750 const $r = 114;
8751 const $t = 116;
8752 const $u = 117;
8753 const $v = 118;
8754 const $x = 120;
8755 const $z = 122;
8756 const $LBRACE = 123;
8757 const $BAR = 124;
8758 const $RBRACE = 125;
8759 const $NBSP = 160;
8760 const $BT = 96;
8761 function isWhitespace(code) {
8762 return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
8763 }
8764 function isDigit(code) {
8765 return $0 <= code && code <= $9;
8766 }
8767 function isAsciiLetter(code) {
8768 return code >= $a && code <= $z || code >= $A && code <= $Z;
8769 }
8770 function isAsciiHexDigit(code) {
8771 return code >= $a && code <= $f || code >= $A && code <= $F || isDigit(code);
8772 }
8773 function isNewLine(code) {
8774 return code === $LF || code === $CR;
8775 }
8776 function isOctalDigit(code) {
8777 return $0 <= code && code <= $7;
8778 }
8779
8780 /**
8781 * @license
8782 * Copyright Google LLC All Rights Reserved.
8783 *
8784 * Use of this source code is governed by an MIT-style license that can be
8785 * found in the LICENSE file at https://angular.io/license
8786 */
8787 class ParseLocation {
8788 constructor(file, offset, line, col) {
8789 this.file = file;
8790 this.offset = offset;
8791 this.line = line;
8792 this.col = col;
8793 }
8794 toString() {
8795 return this.offset != null ? `${this.file.url}@${this.line}:${this.col}` : this.file.url;
8796 }
8797 moveBy(delta) {
8798 const source = this.file.content;
8799 const len = source.length;
8800 let offset = this.offset;
8801 let line = this.line;
8802 let col = this.col;
8803 while (offset > 0 && delta < 0) {
8804 offset--;
8805 delta++;
8806 const ch = source.charCodeAt(offset);
8807 if (ch == $LF) {
8808 line--;
8809 const priorLine = source.substr(0, offset - 1).lastIndexOf(String.fromCharCode($LF));
8810 col = priorLine > 0 ? offset - priorLine : offset;
8811 }
8812 else {
8813 col--;
8814 }
8815 }
8816 while (offset < len && delta > 0) {
8817 const ch = source.charCodeAt(offset);
8818 offset++;
8819 delta--;
8820 if (ch == $LF) {
8821 line++;
8822 col = 0;
8823 }
8824 else {
8825 col++;
8826 }
8827 }
8828 return new ParseLocation(this.file, offset, line, col);
8829 }
8830 // Return the source around the location
8831 // Up to `maxChars` or `maxLines` on each side of the location
8832 getContext(maxChars, maxLines) {
8833 const content = this.file.content;
8834 let startOffset = this.offset;
8835 if (startOffset != null) {
8836 if (startOffset > content.length - 1) {
8837 startOffset = content.length - 1;
8838 }
8839 let endOffset = startOffset;
8840 let ctxChars = 0;
8841 let ctxLines = 0;
8842 while (ctxChars < maxChars && startOffset > 0) {
8843 startOffset--;
8844 ctxChars++;
8845 if (content[startOffset] == '\n') {
8846 if (++ctxLines == maxLines) {
8847 break;
8848 }
8849 }
8850 }
8851 ctxChars = 0;
8852 ctxLines = 0;
8853 while (ctxChars < maxChars && endOffset < content.length - 1) {
8854 endOffset++;
8855 ctxChars++;
8856 if (content[endOffset] == '\n') {
8857 if (++ctxLines == maxLines) {
8858 break;
8859 }
8860 }
8861 }
8862 return {
8863 before: content.substring(startOffset, this.offset),
8864 after: content.substring(this.offset, endOffset + 1),
8865 };
8866 }
8867 return null;
8868 }
8869 }
8870 class ParseSourceFile {
8871 constructor(content, url) {
8872 this.content = content;
8873 this.url = url;
8874 }
8875 }
8876 class ParseSourceSpan {
8877 /**
8878 * Create an object that holds information about spans of tokens/nodes captured during
8879 * lexing/parsing of text.
8880 *
8881 * @param start
8882 * The location of the start of the span (having skipped leading trivia).
8883 * Skipping leading trivia makes source-spans more "user friendly", since things like HTML
8884 * elements will appear to begin at the start of the opening tag, rather than at the start of any
8885 * leading trivia, which could include newlines.
8886 *
8887 * @param end
8888 * The location of the end of the span.
8889 *
8890 * @param fullStart
8891 * The start of the token without skipping the leading trivia.
8892 * This is used by tooling that splits tokens further, such as extracting Angular interpolations
8893 * from text tokens. Such tooling creates new source-spans relative to the original token's
8894 * source-span. If leading trivia characters have been skipped then the new source-spans may be
8895 * incorrectly offset.
8896 *
8897 * @param details
8898 * Additional information (such as identifier names) that should be associated with the span.
8899 */
8900 constructor(start, end, fullStart = start, details = null) {
8901 this.start = start;
8902 this.end = end;
8903 this.fullStart = fullStart;
8904 this.details = details;
8905 }
8906 toString() {
8907 return this.start.file.content.substring(this.start.offset, this.end.offset);
8908 }
8909 }
8910 var ParseErrorLevel;
8911 (function (ParseErrorLevel) {
8912 ParseErrorLevel[ParseErrorLevel["WARNING"] = 0] = "WARNING";
8913 ParseErrorLevel[ParseErrorLevel["ERROR"] = 1] = "ERROR";
8914 })(ParseErrorLevel || (ParseErrorLevel = {}));
8915 class ParseError {
8916 constructor(span, msg, level = ParseErrorLevel.ERROR) {
8917 this.span = span;
8918 this.msg = msg;
8919 this.level = level;
8920 }
8921 contextualMessage() {
8922 const ctx = this.span.start.getContext(100, 3);
8923 return ctx ? `${this.msg} ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` :
8924 this.msg;
8925 }
8926 toString() {
8927 const details = this.span.details ? `, ${this.span.details}` : '';
8928 return `${this.contextualMessage()}: ${this.span.start}${details}`;
8929 }
8930 }
8931 /**
8932 * Generates Source Span object for a given R3 Type for JIT mode.
8933 *
8934 * @param kind Component or Directive.
8935 * @param typeName name of the Component or Directive.
8936 * @param sourceUrl reference to Component or Directive source.
8937 * @returns instance of ParseSourceSpan that represent a given Component or Directive.
8938 */
8939 function r3JitTypeSourceSpan(kind, typeName, sourceUrl) {
8940 const sourceFileName = `in ${kind} ${typeName} in ${sourceUrl}`;
8941 const sourceFile = new ParseSourceFile('', sourceFileName);
8942 return new ParseSourceSpan(new ParseLocation(sourceFile, -1, -1, -1), new ParseLocation(sourceFile, -1, -1, -1));
8943 }
8944
8945 /**
8946 * @license
8947 * Copyright Google LLC All Rights Reserved.
8948 *
8949 * Use of this source code is governed by an MIT-style license that can be
8950 * found in the LICENSE file at https://angular.io/license
8951 */
8952 /**
8953 * Implementation of `CompileReflector` which resolves references to @angular/core
8954 * symbols at runtime, according to a consumer-provided mapping.
8955 *
8956 * Only supports `resolveExternalReference`, all other methods throw.
8957 */
8958 class R3JitReflector {
8959 constructor(context) {
8960 this.context = context;
8961 }
8962 resolveExternalReference(ref) {
8963 // This reflector only handles @angular/core imports.
8964 if (ref.moduleName !== '@angular/core') {
8965 throw new Error(`Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.`);
8966 }
8967 if (!this.context.hasOwnProperty(ref.name)) {
8968 throw new Error(`No value provided for @angular/core symbol '${ref.name}'.`);
8969 }
8970 return this.context[ref.name];
8971 }
8972 parameters(typeOrFunc) {
8973 throw new Error('Not implemented.');
8974 }
8975 annotations(typeOrFunc) {
8976 throw new Error('Not implemented.');
8977 }
8978 shallowAnnotations(typeOrFunc) {
8979 throw new Error('Not implemented.');
8980 }
8981 tryAnnotations(typeOrFunc) {
8982 throw new Error('Not implemented.');
8983 }
8984 propMetadata(typeOrFunc) {
8985 throw new Error('Not implemented.');
8986 }
8987 hasLifecycleHook(type, lcProperty) {
8988 throw new Error('Not implemented.');
8989 }
8990 guards(typeOrFunc) {
8991 throw new Error('Not implemented.');
8992 }
8993 componentModuleUrl(type, cmpMetadata) {
8994 throw new Error('Not implemented.');
8995 }
8996 }
8997
8998 /**
8999 * @license
9000 * Copyright Google LLC All Rights Reserved.
9001 *
9002 * Use of this source code is governed by an MIT-style license that can be
9003 * found in the LICENSE file at https://angular.io/license
9004 */
9005 function mapLiteral(obj, quoted = false) {
9006 return literalMap(Object.keys(obj).map(key => ({
9007 key,
9008 quoted,
9009 value: obj[key],
9010 })));
9011 }
9012
9013 /**
9014 * @license
9015 * Copyright Google LLC All Rights Reserved.
9016 *
9017 * Use of this source code is governed by an MIT-style license that can be
9018 * found in the LICENSE file at https://angular.io/license
9019 */
9020 /**
9021 * Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`.
9022 */
9023 function compileNgModule(meta) {
9024 const { internalType, type: moduleType, bootstrap, declarations, imports, exports, schemas, containsForwardDecls, emitInline, id } = meta;
9025 const additionalStatements = [];
9026 const definitionMap = { type: internalType };
9027 // Only generate the keys in the metadata if the arrays have values.
9028 if (bootstrap.length) {
9029 definitionMap.bootstrap = refsToArray(bootstrap, containsForwardDecls);
9030 }
9031 // If requested to emit scope information inline, pass the declarations, imports and exports to
9032 // the `ɵɵdefineNgModule` call. The JIT compilation uses this.
9033 if (emitInline) {
9034 if (declarations.length) {
9035 definitionMap.declarations = refsToArray(declarations, containsForwardDecls);
9036 }
9037 if (imports.length) {
9038 definitionMap.imports = refsToArray(imports, containsForwardDecls);
9039 }
9040 if (exports.length) {
9041 definitionMap.exports = refsToArray(exports, containsForwardDecls);
9042 }
9043 }
9044 // If not emitting inline, the scope information is not passed into `ɵɵdefineNgModule` as it would
9045 // prevent tree-shaking of the declarations, imports and exports references.
9046 else {
9047 const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta);
9048 if (setNgModuleScopeCall !== null) {
9049 additionalStatements.push(setNgModuleScopeCall);
9050 }
9051 }
9052 if (schemas && schemas.length) {
9053 definitionMap.schemas = literalArr(schemas.map(ref => ref.value));
9054 }
9055 if (id) {
9056 definitionMap.id = id;
9057 }
9058 const expression = importExpr(Identifiers$1.defineNgModule).callFn([mapToMapExpression(definitionMap)]);
9059 const type = new ExpressionType(importExpr(Identifiers$1.NgModuleDefWithMeta, [
9060 new ExpressionType(moduleType.type), tupleTypeOf(declarations), tupleTypeOf(imports),
9061 tupleTypeOf(exports)
9062 ]));
9063 return { expression, type, additionalStatements };
9064 }
9065 /**
9066 * Generates a function call to `ɵɵsetNgModuleScope` with all necessary information so that the
9067 * transitive module scope can be computed during runtime in JIT mode. This call is marked pure
9068 * such that the references to declarations, imports and exports may be elided causing these
9069 * symbols to become tree-shakeable.
9070 */
9071 function generateSetNgModuleScopeCall(meta) {
9072 const { adjacentType: moduleType, declarations, imports, exports, containsForwardDecls } = meta;
9073 const scopeMap = {};
9074 if (declarations.length) {
9075 scopeMap.declarations = refsToArray(declarations, containsForwardDecls);
9076 }
9077 if (imports.length) {
9078 scopeMap.imports = refsToArray(imports, containsForwardDecls);
9079 }
9080 if (exports.length) {
9081 scopeMap.exports = refsToArray(exports, containsForwardDecls);
9082 }
9083 if (Object.keys(scopeMap).length === 0) {
9084 return null;
9085 }
9086 // setNgModuleScope(...)
9087 const fnCall = new InvokeFunctionExpr(
9088 /* fn */ importExpr(Identifiers$1.setNgModuleScope),
9089 /* args */ [moduleType, mapToMapExpression(scopeMap)]);
9090 // (ngJitMode guard) && setNgModuleScope(...)
9091 const guardedCall = jitOnlyGuardedExpression(fnCall);
9092 // function() { (ngJitMode guard) && setNgModuleScope(...); }
9093 const iife = new FunctionExpr(
9094 /* params */ [],
9095 /* statements */ [guardedCall.toStmt()]);
9096 // (function() { (ngJitMode guard) && setNgModuleScope(...); })()
9097 const iifeCall = new InvokeFunctionExpr(
9098 /* fn */ iife,
9099 /* args */ []);
9100 return iifeCall.toStmt();
9101 }
9102 function compileInjector(meta) {
9103 const result = compileFactoryFunction({
9104 name: meta.name,
9105 type: meta.type,
9106 internalType: meta.internalType,
9107 typeArgumentCount: 0,
9108 deps: meta.deps,
9109 injectFn: Identifiers$1.inject,
9110 target: R3FactoryTarget.NgModule,
9111 });
9112 const definitionMap = {
9113 factory: result.factory,
9114 };
9115 if (meta.providers !== null) {
9116 definitionMap.providers = meta.providers;
9117 }
9118 if (meta.imports.length > 0) {
9119 definitionMap.imports = literalArr(meta.imports);
9120 }
9121 const expression = importExpr(Identifiers$1.defineInjector).callFn([mapToMapExpression(definitionMap)]);
9122 const type = new ExpressionType(importExpr(Identifiers$1.InjectorDef, [new ExpressionType(meta.type.type)]));
9123 return { expression, type, statements: result.statements };
9124 }
9125 function tupleTypeOf(exp) {
9126 const types = exp.map(ref => typeofExpr(ref.type));
9127 return exp.length > 0 ? expressionType(literalArr(types)) : NONE_TYPE;
9128 }
9129 function refsToArray(refs, shouldForwardDeclare) {
9130 const values = literalArr(refs.map(ref => ref.value));
9131 return shouldForwardDeclare ? fn([], [new ReturnStatement(values)]) : values;
9132 }
9133
9134 /**
9135 * @license
9136 * Copyright Google LLC All Rights Reserved.
9137 *
9138 * Use of this source code is governed by an MIT-style license that can be
9139 * found in the LICENSE file at https://angular.io/license
9140 */
9141 function compilePipeFromMetadata(metadata) {
9142 const definitionMapValues = [];
9143 // e.g. `name: 'myPipe'`
9144 definitionMapValues.push({ key: 'name', value: literal(metadata.pipeName), quoted: false });
9145 // e.g. `type: MyPipe`
9146 definitionMapValues.push({ key: 'type', value: metadata.type.value, quoted: false });
9147 // e.g. `pure: true`
9148 definitionMapValues.push({ key: 'pure', value: literal(metadata.pure), quoted: false });
9149 const expression = importExpr(Identifiers$1.definePipe).callFn([literalMap(definitionMapValues)]);
9150 const type = createPipeType(metadata);
9151 return { expression, type };
9152 }
9153 function createPipeType(metadata) {
9154 return new ExpressionType(importExpr(Identifiers$1.PipeDefWithMeta, [
9155 typeWithParameters(metadata.type.type, metadata.typeArgumentCount),
9156 new ExpressionType(new LiteralExpr(metadata.pipeName)),
9157 ]));
9158 }
9159
9160 /**
9161 * @license
9162 * Copyright Google LLC All Rights Reserved.
9163 *
9164 * Use of this source code is governed by an MIT-style license that can be
9165 * found in the LICENSE file at https://angular.io/license
9166 */
9167 class ParserError {
9168 constructor(message, input, errLocation, ctxLocation) {
9169 this.input = input;
9170 this.errLocation = errLocation;
9171 this.ctxLocation = ctxLocation;
9172 this.message = `Parser Error: ${message} ${errLocation} [${input}] in ${ctxLocation}`;
9173 }
9174 }
9175 class ParseSpan {
9176 constructor(start, end) {
9177 this.start = start;
9178 this.end = end;
9179 }
9180 toAbsolute(absoluteOffset) {
9181 return new AbsoluteSourceSpan(absoluteOffset + this.start, absoluteOffset + this.end);
9182 }
9183 }
9184 class AST {
9185 constructor(span,
9186 /**
9187 * Absolute location of the expression AST in a source code file.
9188 */
9189 sourceSpan) {
9190 this.span = span;
9191 this.sourceSpan = sourceSpan;
9192 }
9193 visit(visitor, context = null) {
9194 return null;
9195 }
9196 toString() {
9197 return 'AST';
9198 }
9199 }
9200 class ASTWithName extends AST {
9201 constructor(span, sourceSpan, nameSpan) {
9202 super(span, sourceSpan);
9203 this.nameSpan = nameSpan;
9204 }
9205 }
9206 /**
9207 * Represents a quoted expression of the form:
9208 *
9209 * quote = prefix `:` uninterpretedExpression
9210 * prefix = identifier
9211 * uninterpretedExpression = arbitrary string
9212 *
9213 * A quoted expression is meant to be pre-processed by an AST transformer that
9214 * converts it into another AST that no longer contains quoted expressions.
9215 * It is meant to allow third-party developers to extend Angular template
9216 * expression language. The `uninterpretedExpression` part of the quote is
9217 * therefore not interpreted by the Angular's own expression parser.
9218 */
9219 class Quote extends AST {
9220 constructor(span, sourceSpan, prefix, uninterpretedExpression, location) {
9221 super(span, sourceSpan);
9222 this.prefix = prefix;
9223 this.uninterpretedExpression = uninterpretedExpression;
9224 this.location = location;
9225 }
9226 visit(visitor, context = null) {
9227 return visitor.visitQuote(this, context);
9228 }
9229 toString() {
9230 return 'Quote';
9231 }
9232 }
9233 class EmptyExpr extends AST {
9234 visit(visitor, context = null) {
9235 // do nothing
9236 }
9237 }
9238 class ImplicitReceiver extends AST {
9239 visit(visitor, context = null) {
9240 return visitor.visitImplicitReceiver(this, context);
9241 }
9242 }
9243 /**
9244 * Receiver when something is accessed through `this` (e.g. `this.foo`). Note that this class
9245 * inherits from `ImplicitReceiver`, because accessing something through `this` is treated the
9246 * same as accessing it implicitly inside of an Angular template (e.g. `[attr.title]="this.title"`
9247 * is the same as `[attr.title]="title"`.). Inheriting allows for the `this` accesses to be treated
9248 * the same as implicit ones, except for a couple of exceptions like `$event` and `$any`.
9249 * TODO: we should find a way for this class not to extend from `ImplicitReceiver` in the future.
9250 */
9251 class ThisReceiver extends ImplicitReceiver {
9252 visit(visitor, context = null) {
9253 var _a;
9254 return (_a = visitor.visitThisReceiver) === null || _a === void 0 ? void 0 : _a.call(visitor, this, context);
9255 }
9256 }
9257 /**
9258 * Multiple expressions separated by a semicolon.
9259 */
9260 class Chain extends AST {
9261 constructor(span, sourceSpan, expressions) {
9262 super(span, sourceSpan);
9263 this.expressions = expressions;
9264 }
9265 visit(visitor, context = null) {
9266 return visitor.visitChain(this, context);
9267 }
9268 }
9269 class Conditional extends AST {
9270 constructor(span, sourceSpan, condition, trueExp, falseExp) {
9271 super(span, sourceSpan);
9272 this.condition = condition;
9273 this.trueExp = trueExp;
9274 this.falseExp = falseExp;
9275 }
9276 visit(visitor, context = null) {
9277 return visitor.visitConditional(this, context);
9278 }
9279 }
9280 class PropertyRead extends ASTWithName {
9281 constructor(span, sourceSpan, nameSpan, receiver, name) {
9282 super(span, sourceSpan, nameSpan);
9283 this.receiver = receiver;
9284 this.name = name;
9285 }
9286 visit(visitor, context = null) {
9287 return visitor.visitPropertyRead(this, context);
9288 }
9289 }
9290 class PropertyWrite extends ASTWithName {
9291 constructor(span, sourceSpan, nameSpan, receiver, name, value) {
9292 super(span, sourceSpan, nameSpan);
9293 this.receiver = receiver;
9294 this.name = name;
9295 this.value = value;
9296 }
9297 visit(visitor, context = null) {
9298 return visitor.visitPropertyWrite(this, context);
9299 }
9300 }
9301 class SafePropertyRead extends ASTWithName {
9302 constructor(span, sourceSpan, nameSpan, receiver, name) {
9303 super(span, sourceSpan, nameSpan);
9304 this.receiver = receiver;
9305 this.name = name;
9306 }
9307 visit(visitor, context = null) {
9308 return visitor.visitSafePropertyRead(this, context);
9309 }
9310 }
9311 class KeyedRead extends AST {
9312 constructor(span, sourceSpan, obj, key) {
9313 super(span, sourceSpan);
9314 this.obj = obj;
9315 this.key = key;
9316 }
9317 visit(visitor, context = null) {
9318 return visitor.visitKeyedRead(this, context);
9319 }
9320 }
9321 class KeyedWrite extends AST {
9322 constructor(span, sourceSpan, obj, key, value) {
9323 super(span, sourceSpan);
9324 this.obj = obj;
9325 this.key = key;
9326 this.value = value;
9327 }
9328 visit(visitor, context = null) {
9329 return visitor.visitKeyedWrite(this, context);
9330 }
9331 }
9332 class BindingPipe extends ASTWithName {
9333 constructor(span, sourceSpan, exp, name, args, nameSpan) {
9334 super(span, sourceSpan, nameSpan);
9335 this.exp = exp;
9336 this.name = name;
9337 this.args = args;
9338 }
9339 visit(visitor, context = null) {
9340 return visitor.visitPipe(this, context);
9341 }
9342 }
9343 class LiteralPrimitive extends AST {
9344 constructor(span, sourceSpan, value) {
9345 super(span, sourceSpan);
9346 this.value = value;
9347 }
9348 visit(visitor, context = null) {
9349 return visitor.visitLiteralPrimitive(this, context);
9350 }
9351 }
9352 class LiteralArray extends AST {
9353 constructor(span, sourceSpan, expressions) {
9354 super(span, sourceSpan);
9355 this.expressions = expressions;
9356 }
9357 visit(visitor, context = null) {
9358 return visitor.visitLiteralArray(this, context);
9359 }
9360 }
9361 class LiteralMap extends AST {
9362 constructor(span, sourceSpan, keys, values) {
9363 super(span, sourceSpan);
9364 this.keys = keys;
9365 this.values = values;
9366 }
9367 visit(visitor, context = null) {
9368 return visitor.visitLiteralMap(this, context);
9369 }
9370 }
9371 class Interpolation extends AST {
9372 constructor(span, sourceSpan, strings, expressions) {
9373 super(span, sourceSpan);
9374 this.strings = strings;
9375 this.expressions = expressions;
9376 }
9377 visit(visitor, context = null) {
9378 return visitor.visitInterpolation(this, context);
9379 }
9380 }
9381 class Binary extends AST {
9382 constructor(span, sourceSpan, operation, left, right) {
9383 super(span, sourceSpan);
9384 this.operation = operation;
9385 this.left = left;
9386 this.right = right;
9387 }
9388 visit(visitor, context = null) {
9389 return visitor.visitBinary(this, context);
9390 }
9391 }
9392 /**
9393 * For backwards compatibility reasons, `Unary` inherits from `Binary` and mimics the binary AST
9394 * node that was originally used. This inheritance relation can be deleted in some future major,
9395 * after consumers have been given a chance to fully support Unary.
9396 */
9397 class Unary extends Binary {
9398 /**
9399 * During the deprecation period this constructor is private, to avoid consumers from creating
9400 * a `Unary` with the fallback properties for `Binary`.
9401 */
9402 constructor(span, sourceSpan, operator, expr, binaryOp, binaryLeft, binaryRight) {
9403 super(span, sourceSpan, binaryOp, binaryLeft, binaryRight);
9404 this.operator = operator;
9405 this.expr = expr;
9406 }
9407 /**
9408 * Creates a unary minus expression "-x", represented as `Binary` using "0 - x".
9409 */
9410 static createMinus(span, sourceSpan, expr) {
9411 return new Unary(span, sourceSpan, '-', expr, '-', new LiteralPrimitive(span, sourceSpan, 0), expr);
9412 }
9413 /**
9414 * Creates a unary plus expression "+x", represented as `Binary` using "x - 0".
9415 */
9416 static createPlus(span, sourceSpan, expr) {
9417 return new Unary(span, sourceSpan, '+', expr, '-', expr, new LiteralPrimitive(span, sourceSpan, 0));
9418 }
9419 visit(visitor, context = null) {
9420 if (visitor.visitUnary !== undefined) {
9421 return visitor.visitUnary(this, context);
9422 }
9423 return visitor.visitBinary(this, context);
9424 }
9425 }
9426 class PrefixNot extends AST {
9427 constructor(span, sourceSpan, expression) {
9428 super(span, sourceSpan);
9429 this.expression = expression;
9430 }
9431 visit(visitor, context = null) {
9432 return visitor.visitPrefixNot(this, context);
9433 }
9434 }
9435 class NonNullAssert extends AST {
9436 constructor(span, sourceSpan, expression) {
9437 super(span, sourceSpan);
9438 this.expression = expression;
9439 }
9440 visit(visitor, context = null) {
9441 return visitor.visitNonNullAssert(this, context);
9442 }
9443 }
9444 class MethodCall extends ASTWithName {
9445 constructor(span, sourceSpan, nameSpan, receiver, name, args) {
9446 super(span, sourceSpan, nameSpan);
9447 this.receiver = receiver;
9448 this.name = name;
9449 this.args = args;
9450 }
9451 visit(visitor, context = null) {
9452 return visitor.visitMethodCall(this, context);
9453 }
9454 }
9455 class SafeMethodCall extends ASTWithName {
9456 constructor(span, sourceSpan, nameSpan, receiver, name, args) {
9457 super(span, sourceSpan, nameSpan);
9458 this.receiver = receiver;
9459 this.name = name;
9460 this.args = args;
9461 }
9462 visit(visitor, context = null) {
9463 return visitor.visitSafeMethodCall(this, context);
9464 }
9465 }
9466 class FunctionCall extends AST {
9467 constructor(span, sourceSpan, target, args) {
9468 super(span, sourceSpan);
9469 this.target = target;
9470 this.args = args;
9471 }
9472 visit(visitor, context = null) {
9473 return visitor.visitFunctionCall(this, context);
9474 }
9475 }
9476 /**
9477 * Records the absolute position of a text span in a source file, where `start` and `end` are the
9478 * starting and ending byte offsets, respectively, of the text span in a source file.
9479 */
9480 class AbsoluteSourceSpan {
9481 constructor(start, end) {
9482 this.start = start;
9483 this.end = end;
9484 }
9485 }
9486 class ASTWithSource extends AST {
9487 constructor(ast, source, location, absoluteOffset, errors) {
9488 super(new ParseSpan(0, source === null ? 0 : source.length), new AbsoluteSourceSpan(absoluteOffset, source === null ? absoluteOffset : absoluteOffset + source.length));
9489 this.ast = ast;
9490 this.source = source;
9491 this.location = location;
9492 this.errors = errors;
9493 }
9494 visit(visitor, context = null) {
9495 if (visitor.visitASTWithSource) {
9496 return visitor.visitASTWithSource(this, context);
9497 }
9498 return this.ast.visit(visitor, context);
9499 }
9500 toString() {
9501 return `${this.source} in ${this.location}`;
9502 }
9503 }
9504 class VariableBinding {
9505 /**
9506 * @param sourceSpan entire span of the binding.
9507 * @param key name of the LHS along with its span.
9508 * @param value optional value for the RHS along with its span.
9509 */
9510 constructor(sourceSpan, key, value) {
9511 this.sourceSpan = sourceSpan;
9512 this.key = key;
9513 this.value = value;
9514 }
9515 }
9516 class ExpressionBinding {
9517 /**
9518 * @param sourceSpan entire span of the binding.
9519 * @param key binding name, like ngForOf, ngForTrackBy, ngIf, along with its
9520 * span. Note that the length of the span may not be the same as
9521 * `key.source.length`. For example,
9522 * 1. key.source = ngFor, key.span is for "ngFor"
9523 * 2. key.source = ngForOf, key.span is for "of"
9524 * 3. key.source = ngForTrackBy, key.span is for "trackBy"
9525 * @param value optional expression for the RHS.
9526 */
9527 constructor(sourceSpan, key, value) {
9528 this.sourceSpan = sourceSpan;
9529 this.key = key;
9530 this.value = value;
9531 }
9532 }
9533 class RecursiveAstVisitor {
9534 visit(ast, context) {
9535 // The default implementation just visits every node.
9536 // Classes that extend RecursiveAstVisitor should override this function
9537 // to selectively visit the specified node.
9538 ast.visit(this, context);
9539 }
9540 visitUnary(ast, context) {
9541 this.visit(ast.expr, context);
9542 }
9543 visitBinary(ast, context) {
9544 this.visit(ast.left, context);
9545 this.visit(ast.right, context);
9546 }
9547 visitChain(ast, context) {
9548 this.visitAll(ast.expressions, context);
9549 }
9550 visitConditional(ast, context) {
9551 this.visit(ast.condition, context);
9552 this.visit(ast.trueExp, context);
9553 this.visit(ast.falseExp, context);
9554 }
9555 visitPipe(ast, context) {
9556 this.visit(ast.exp, context);
9557 this.visitAll(ast.args, context);
9558 }
9559 visitFunctionCall(ast, context) {
9560 if (ast.target) {
9561 this.visit(ast.target, context);
9562 }
9563 this.visitAll(ast.args, context);
9564 }
9565 visitImplicitReceiver(ast, context) { }
9566 visitThisReceiver(ast, context) { }
9567 visitInterpolation(ast, context) {
9568 this.visitAll(ast.expressions, context);
9569 }
9570 visitKeyedRead(ast, context) {
9571 this.visit(ast.obj, context);
9572 this.visit(ast.key, context);
9573 }
9574 visitKeyedWrite(ast, context) {
9575 this.visit(ast.obj, context);
9576 this.visit(ast.key, context);
9577 this.visit(ast.value, context);
9578 }
9579 visitLiteralArray(ast, context) {
9580 this.visitAll(ast.expressions, context);
9581 }
9582 visitLiteralMap(ast, context) {
9583 this.visitAll(ast.values, context);
9584 }
9585 visitLiteralPrimitive(ast, context) { }
9586 visitMethodCall(ast, context) {
9587 this.visit(ast.receiver, context);
9588 this.visitAll(ast.args, context);
9589 }
9590 visitPrefixNot(ast, context) {
9591 this.visit(ast.expression, context);
9592 }
9593 visitNonNullAssert(ast, context) {
9594 this.visit(ast.expression, context);
9595 }
9596 visitPropertyRead(ast, context) {
9597 this.visit(ast.receiver, context);
9598 }
9599 visitPropertyWrite(ast, context) {
9600 this.visit(ast.receiver, context);
9601 this.visit(ast.value, context);
9602 }
9603 visitSafePropertyRead(ast, context) {
9604 this.visit(ast.receiver, context);
9605 }
9606 visitSafeMethodCall(ast, context) {
9607 this.visit(ast.receiver, context);
9608 this.visitAll(ast.args, context);
9609 }
9610 visitQuote(ast, context) { }
9611 // This is not part of the AstVisitor interface, just a helper method
9612 visitAll(asts, context) {
9613 for (const ast of asts) {
9614 this.visit(ast, context);
9615 }
9616 }
9617 }
9618 class AstTransformer {
9619 visitImplicitReceiver(ast, context) {
9620 return ast;
9621 }
9622 visitThisReceiver(ast, context) {
9623 return ast;
9624 }
9625 visitInterpolation(ast, context) {
9626 return new Interpolation(ast.span, ast.sourceSpan, ast.strings, this.visitAll(ast.expressions));
9627 }
9628 visitLiteralPrimitive(ast, context) {
9629 return new LiteralPrimitive(ast.span, ast.sourceSpan, ast.value);
9630 }
9631 visitPropertyRead(ast, context) {
9632 return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name);
9633 }
9634 visitPropertyWrite(ast, context) {
9635 return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, ast.value.visit(this));
9636 }
9637 visitSafePropertyRead(ast, context) {
9638 return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name);
9639 }
9640 visitMethodCall(ast, context) {
9641 return new MethodCall(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
9642 }
9643 visitSafeMethodCall(ast, context) {
9644 return new SafeMethodCall(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
9645 }
9646 visitFunctionCall(ast, context) {
9647 return new FunctionCall(ast.span, ast.sourceSpan, ast.target.visit(this), this.visitAll(ast.args));
9648 }
9649 visitLiteralArray(ast, context) {
9650 return new LiteralArray(ast.span, ast.sourceSpan, this.visitAll(ast.expressions));
9651 }
9652 visitLiteralMap(ast, context) {
9653 return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, this.visitAll(ast.values));
9654 }
9655 visitUnary(ast, context) {
9656 switch (ast.operator) {
9657 case '+':
9658 return Unary.createPlus(ast.span, ast.sourceSpan, ast.expr.visit(this));
9659 case '-':
9660 return Unary.createMinus(ast.span, ast.sourceSpan, ast.expr.visit(this));
9661 default:
9662 throw new Error(`Unknown unary operator ${ast.operator}`);
9663 }
9664 }
9665 visitBinary(ast, context) {
9666 return new Binary(ast.span, ast.sourceSpan, ast.operation, ast.left.visit(this), ast.right.visit(this));
9667 }
9668 visitPrefixNot(ast, context) {
9669 return new PrefixNot(ast.span, ast.sourceSpan, ast.expression.visit(this));
9670 }
9671 visitNonNullAssert(ast, context) {
9672 return new NonNullAssert(ast.span, ast.sourceSpan, ast.expression.visit(this));
9673 }
9674 visitConditional(ast, context) {
9675 return new Conditional(ast.span, ast.sourceSpan, ast.condition.visit(this), ast.trueExp.visit(this), ast.falseExp.visit(this));
9676 }
9677 visitPipe(ast, context) {
9678 return new BindingPipe(ast.span, ast.sourceSpan, ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.nameSpan);
9679 }
9680 visitKeyedRead(ast, context) {
9681 return new KeyedRead(ast.span, ast.sourceSpan, ast.obj.visit(this), ast.key.visit(this));
9682 }
9683 visitKeyedWrite(ast, context) {
9684 return new KeyedWrite(ast.span, ast.sourceSpan, ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
9685 }
9686 visitAll(asts) {
9687 const res = [];
9688 for (let i = 0; i < asts.length; ++i) {
9689 res[i] = asts[i].visit(this);
9690 }
9691 return res;
9692 }
9693 visitChain(ast, context) {
9694 return new Chain(ast.span, ast.sourceSpan, this.visitAll(ast.expressions));
9695 }
9696 visitQuote(ast, context) {
9697 return new Quote(ast.span, ast.sourceSpan, ast.prefix, ast.uninterpretedExpression, ast.location);
9698 }
9699 }
9700 // A transformer that only creates new nodes if the transformer makes a change or
9701 // a change is made a child node.
9702 class AstMemoryEfficientTransformer {
9703 visitImplicitReceiver(ast, context) {
9704 return ast;
9705 }
9706 visitThisReceiver(ast, context) {
9707 return ast;
9708 }
9709 visitInterpolation(ast, context) {
9710 const expressions = this.visitAll(ast.expressions);
9711 if (expressions !== ast.expressions)
9712 return new Interpolation(ast.span, ast.sourceSpan, ast.strings, expressions);
9713 return ast;
9714 }
9715 visitLiteralPrimitive(ast, context) {
9716 return ast;
9717 }
9718 visitPropertyRead(ast, context) {
9719 const receiver = ast.receiver.visit(this);
9720 if (receiver !== ast.receiver) {
9721 return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name);
9722 }
9723 return ast;
9724 }
9725 visitPropertyWrite(ast, context) {
9726 const receiver = ast.receiver.visit(this);
9727 const value = ast.value.visit(this);
9728 if (receiver !== ast.receiver || value !== ast.value) {
9729 return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, value);
9730 }
9731 return ast;
9732 }
9733 visitSafePropertyRead(ast, context) {
9734 const receiver = ast.receiver.visit(this);
9735 if (receiver !== ast.receiver) {
9736 return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name);
9737 }
9738 return ast;
9739 }
9740 visitMethodCall(ast, context) {
9741 const receiver = ast.receiver.visit(this);
9742 const args = this.visitAll(ast.args);
9743 if (receiver !== ast.receiver || args !== ast.args) {
9744 return new MethodCall(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args);
9745 }
9746 return ast;
9747 }
9748 visitSafeMethodCall(ast, context) {
9749 const receiver = ast.receiver.visit(this);
9750 const args = this.visitAll(ast.args);
9751 if (receiver !== ast.receiver || args !== ast.args) {
9752 return new SafeMethodCall(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args);
9753 }
9754 return ast;
9755 }
9756 visitFunctionCall(ast, context) {
9757 const target = ast.target && ast.target.visit(this);
9758 const args = this.visitAll(ast.args);
9759 if (target !== ast.target || args !== ast.args) {
9760 return new FunctionCall(ast.span, ast.sourceSpan, target, args);
9761 }
9762 return ast;
9763 }
9764 visitLiteralArray(ast, context) {
9765 const expressions = this.visitAll(ast.expressions);
9766 if (expressions !== ast.expressions) {
9767 return new LiteralArray(ast.span, ast.sourceSpan, expressions);
9768 }
9769 return ast;
9770 }
9771 visitLiteralMap(ast, context) {
9772 const values = this.visitAll(ast.values);
9773 if (values !== ast.values) {
9774 return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, values);
9775 }
9776 return ast;
9777 }
9778 visitUnary(ast, context) {
9779 const expr = ast.expr.visit(this);
9780 if (expr !== ast.expr) {
9781 switch (ast.operator) {
9782 case '+':
9783 return Unary.createPlus(ast.span, ast.sourceSpan, expr);
9784 case '-':
9785 return Unary.createMinus(ast.span, ast.sourceSpan, expr);
9786 default:
9787 throw new Error(`Unknown unary operator ${ast.operator}`);
9788 }
9789 }
9790 return ast;
9791 }
9792 visitBinary(ast, context) {
9793 const left = ast.left.visit(this);
9794 const right = ast.right.visit(this);
9795 if (left !== ast.left || right !== ast.right) {
9796 return new Binary(ast.span, ast.sourceSpan, ast.operation, left, right);
9797 }
9798 return ast;
9799 }
9800 visitPrefixNot(ast, context) {
9801 const expression = ast.expression.visit(this);
9802 if (expression !== ast.expression) {
9803 return new PrefixNot(ast.span, ast.sourceSpan, expression);
9804 }
9805 return ast;
9806 }
9807 visitNonNullAssert(ast, context) {
9808 const expression = ast.expression.visit(this);
9809 if (expression !== ast.expression) {
9810 return new NonNullAssert(ast.span, ast.sourceSpan, expression);
9811 }
9812 return ast;
9813 }
9814 visitConditional(ast, context) {
9815 const condition = ast.condition.visit(this);
9816 const trueExp = ast.trueExp.visit(this);
9817 const falseExp = ast.falseExp.visit(this);
9818 if (condition !== ast.condition || trueExp !== ast.trueExp || falseExp !== ast.falseExp) {
9819 return new Conditional(ast.span, ast.sourceSpan, condition, trueExp, falseExp);
9820 }
9821 return ast;
9822 }
9823 visitPipe(ast, context) {
9824 const exp = ast.exp.visit(this);
9825 const args = this.visitAll(ast.args);
9826 if (exp !== ast.exp || args !== ast.args) {
9827 return new BindingPipe(ast.span, ast.sourceSpan, exp, ast.name, args, ast.nameSpan);
9828 }
9829 return ast;
9830 }
9831 visitKeyedRead(ast, context) {
9832 const obj = ast.obj.visit(this);
9833 const key = ast.key.visit(this);
9834 if (obj !== ast.obj || key !== ast.key) {
9835 return new KeyedRead(ast.span, ast.sourceSpan, obj, key);
9836 }
9837 return ast;
9838 }
9839 visitKeyedWrite(ast, context) {
9840 const obj = ast.obj.visit(this);
9841 const key = ast.key.visit(this);
9842 const value = ast.value.visit(this);
9843 if (obj !== ast.obj || key !== ast.key || value !== ast.value) {
9844 return new KeyedWrite(ast.span, ast.sourceSpan, obj, key, value);
9845 }
9846 return ast;
9847 }
9848 visitAll(asts) {
9849 const res = [];
9850 let modified = false;
9851 for (let i = 0; i < asts.length; ++i) {
9852 const original = asts[i];
9853 const value = original.visit(this);
9854 res[i] = value;
9855 modified = modified || value !== original;
9856 }
9857 return modified ? res : asts;
9858 }
9859 visitChain(ast, context) {
9860 const expressions = this.visitAll(ast.expressions);
9861 if (expressions !== ast.expressions) {
9862 return new Chain(ast.span, ast.sourceSpan, expressions);
9863 }
9864 return ast;
9865 }
9866 visitQuote(ast, context) {
9867 return ast;
9868 }
9869 }
9870 // Bindings
9871 class ParsedProperty {
9872 constructor(name, expression, type,
9873 // TODO(FW-2095): `keySpan` should really be required but allows `undefined` so VE does
9874 // not need to be updated. Make `keySpan` required when VE is removed.
9875 sourceSpan, keySpan, valueSpan) {
9876 this.name = name;
9877 this.expression = expression;
9878 this.type = type;
9879 this.sourceSpan = sourceSpan;
9880 this.keySpan = keySpan;
9881 this.valueSpan = valueSpan;
9882 this.isLiteral = this.type === ParsedPropertyType.LITERAL_ATTR;
9883 this.isAnimation = this.type === ParsedPropertyType.ANIMATION;
9884 }
9885 }
9886 var ParsedPropertyType;
9887 (function (ParsedPropertyType) {
9888 ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT";
9889 ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR";
9890 ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION";
9891 })(ParsedPropertyType || (ParsedPropertyType = {}));
9892 class ParsedEvent {
9893 // Regular events have a target
9894 // Animation events have a phase
9895 constructor(name, targetOrPhase, type, handler, sourceSpan,
9896 // TODO(FW-2095): keySpan should be required but was made optional to avoid changing VE
9897 handlerSpan, keySpan) {
9898 this.name = name;
9899 this.targetOrPhase = targetOrPhase;
9900 this.type = type;
9901 this.handler = handler;
9902 this.sourceSpan = sourceSpan;
9903 this.handlerSpan = handlerSpan;
9904 this.keySpan = keySpan;
9905 }
9906 }
9907 /**
9908 * ParsedVariable represents a variable declaration in a microsyntax expression.
9909 */
9910 class ParsedVariable {
9911 constructor(name, value, sourceSpan, keySpan, valueSpan) {
9912 this.name = name;
9913 this.value = value;
9914 this.sourceSpan = sourceSpan;
9915 this.keySpan = keySpan;
9916 this.valueSpan = valueSpan;
9917 }
9918 }
9919 class BoundElementProperty {
9920 constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan) {
9921 this.name = name;
9922 this.type = type;
9923 this.securityContext = securityContext;
9924 this.value = value;
9925 this.unit = unit;
9926 this.sourceSpan = sourceSpan;
9927 this.keySpan = keySpan;
9928 this.valueSpan = valueSpan;
9929 }
9930 }
9931
9932 /**
9933 * @license
9934 * Copyright Google LLC All Rights Reserved.
9935 *
9936 * Use of this source code is governed by an MIT-style license that can be
9937 * found in the LICENSE file at https://angular.io/license
9938 */
9939 class EventHandlerVars {
9940 }
9941 EventHandlerVars.event = variable('$event');
9942 class ConvertActionBindingResult {
9943 constructor(
9944 /**
9945 * Render2 compatible statements,
9946 */
9947 stmts,
9948 /**
9949 * Variable name used with render2 compatible statements.
9950 */
9951 allowDefault) {
9952 this.stmts = stmts;
9953 this.allowDefault = allowDefault;
9954 /**
9955 * This is bit of a hack. It converts statements which render2 expects to statements which are
9956 * expected by render3.
9957 *
9958 * Example: `<div click="doSomething($event)">` will generate:
9959 *
9960 * Render3:
9961 * ```
9962 * const pd_b:any = ((<any>ctx.doSomething($event)) !== false);
9963 * return pd_b;
9964 * ```
9965 *
9966 * but render2 expects:
9967 * ```
9968 * return ctx.doSomething($event);
9969 * ```
9970 */
9971 // TODO(misko): remove this hack once we no longer support ViewEngine.
9972 this.render3Stmts = stmts.map((statement) => {
9973 if (statement instanceof DeclareVarStmt && statement.name == allowDefault.name &&
9974 statement.value instanceof BinaryOperatorExpr) {
9975 const lhs = statement.value.lhs;
9976 return new ReturnStatement(lhs.value);
9977 }
9978 return statement;
9979 });
9980 }
9981 }
9982 /**
9983 * Converts the given expression AST into an executable output AST, assuming the expression is
9984 * used in an action binding (e.g. an event handler).
9985 */
9986 function convertActionBinding(localResolver, implicitReceiver, action, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses, globals) {
9987 if (!localResolver) {
9988 localResolver = new DefaultLocalResolver(globals);
9989 }
9990 const actionWithoutBuiltins = convertPropertyBindingBuiltins({
9991 createLiteralArrayConverter: (argCount) => {
9992 // Note: no caching for literal arrays in actions.
9993 return (args) => literalArr(args);
9994 },
9995 createLiteralMapConverter: (keys) => {
9996 // Note: no caching for literal maps in actions.
9997 return (values) => {
9998 const entries = keys.map((k, i) => ({
9999 key: k.key,
10000 value: values[i],
10001 quoted: k.quoted,
10002 }));
10003 return literalMap(entries);
10004 };
10005 },
10006 createPipeConverter: (name) => {
10007 throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
10008 }
10009 }, action);
10010 const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses);
10011 const actionStmts = [];
10012 flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
10013 prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
10014 if (visitor.usesImplicitReceiver) {
10015 localResolver.notifyImplicitReceiverUse();
10016 }
10017 const lastIndex = actionStmts.length - 1;
10018 let preventDefaultVar = null;
10019 if (lastIndex >= 0) {
10020 const lastStatement = actionStmts[lastIndex];
10021 const returnExpr = convertStmtIntoExpression(lastStatement);
10022 if (returnExpr) {
10023 // Note: We need to cast the result of the method call to dynamic,
10024 // as it might be a void method!
10025 preventDefaultVar = createPreventDefaultVar(bindingId);
10026 actionStmts[lastIndex] =
10027 preventDefaultVar.set(returnExpr.cast(DYNAMIC_TYPE).notIdentical(literal(false)))
10028 .toDeclStmt(null, [StmtModifier.Final]);
10029 }
10030 }
10031 return new ConvertActionBindingResult(actionStmts, preventDefaultVar);
10032 }
10033 function convertPropertyBindingBuiltins(converterFactory, ast) {
10034 return convertBuiltins(converterFactory, ast);
10035 }
10036 class ConvertPropertyBindingResult {
10037 constructor(stmts, currValExpr) {
10038 this.stmts = stmts;
10039 this.currValExpr = currValExpr;
10040 }
10041 }
10042 var BindingForm;
10043 (function (BindingForm) {
10044 // The general form of binding expression, supports all expressions.
10045 BindingForm[BindingForm["General"] = 0] = "General";
10046 // Try to generate a simple binding (no temporaries or statements)
10047 // otherwise generate a general binding
10048 BindingForm[BindingForm["TrySimple"] = 1] = "TrySimple";
10049 // Inlines assignment of temporaries into the generated expression. The result may still
10050 // have statements attached for declarations of temporary variables.
10051 // This is the only relevant form for Ivy, the other forms are only used in ViewEngine.
10052 BindingForm[BindingForm["Expression"] = 2] = "Expression";
10053 })(BindingForm || (BindingForm = {}));
10054 /**
10055 * Converts the given expression AST into an executable output AST, assuming the expression
10056 * is used in property binding. The expression has to be preprocessed via
10057 * `convertPropertyBindingBuiltins`.
10058 */
10059 function convertPropertyBinding(localResolver, implicitReceiver, expressionWithoutBuiltins, bindingId, form, interpolationFunction) {
10060 if (!localResolver) {
10061 localResolver = new DefaultLocalResolver();
10062 }
10063 const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
10064 const outputExpr = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
10065 const stmts = getStatementsFromVisitor(visitor, bindingId);
10066 if (visitor.usesImplicitReceiver) {
10067 localResolver.notifyImplicitReceiverUse();
10068 }
10069 if (visitor.temporaryCount === 0 && form == BindingForm.TrySimple) {
10070 return new ConvertPropertyBindingResult([], outputExpr);
10071 }
10072 else if (form === BindingForm.Expression) {
10073 return new ConvertPropertyBindingResult(stmts, outputExpr);
10074 }
10075 const currValExpr = createCurrValueExpr(bindingId);
10076 stmts.push(currValExpr.set(outputExpr).toDeclStmt(DYNAMIC_TYPE, [StmtModifier.Final]));
10077 return new ConvertPropertyBindingResult(stmts, currValExpr);
10078 }
10079 /**
10080 * Given some expression, such as a binding or interpolation expression, and a context expression to
10081 * look values up on, visit each facet of the given expression resolving values from the context
10082 * expression such that a list of arguments can be derived from the found values that can be used as
10083 * arguments to an external update instruction.
10084 *
10085 * @param localResolver The resolver to use to look up expressions by name appropriately
10086 * @param contextVariableExpression The expression representing the context variable used to create
10087 * the final argument expressions
10088 * @param expressionWithArgumentsToExtract The expression to visit to figure out what values need to
10089 * be resolved and what arguments list to build.
10090 * @param bindingId A name prefix used to create temporary variable names if they're needed for the
10091 * arguments generated
10092 * @returns An array of expressions that can be passed as arguments to instruction expressions like
10093 * `o.importExpr(R3.propertyInterpolate).callFn(result)`
10094 */
10095 function convertUpdateArguments(localResolver, contextVariableExpression, expressionWithArgumentsToExtract, bindingId) {
10096 const visitor = new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, undefined);
10097 const outputExpr = expressionWithArgumentsToExtract.visit(visitor, _Mode.Expression);
10098 if (visitor.usesImplicitReceiver) {
10099 localResolver.notifyImplicitReceiverUse();
10100 }
10101 const stmts = getStatementsFromVisitor(visitor, bindingId);
10102 // Removing the first argument, because it was a length for ViewEngine, not Ivy.
10103 let args = outputExpr.args.slice(1);
10104 if (expressionWithArgumentsToExtract instanceof Interpolation) {
10105 // If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the
10106 // args returned to just the value, because we're going to pass it to a special instruction.
10107 const strings = expressionWithArgumentsToExtract.strings;
10108 if (args.length === 3 && strings[0] === '' && strings[1] === '') {
10109 // Single argument interpolate instructions.
10110 args = [args[1]];
10111 }
10112 else if (args.length >= 19) {
10113 // 19 or more arguments must be passed to the `interpolateV`-style instructions, which accept
10114 // an array of arguments
10115 args = [literalArr(args)];
10116 }
10117 }
10118 return { stmts, args };
10119 }
10120 function getStatementsFromVisitor(visitor, bindingId) {
10121 const stmts = [];
10122 for (let i = 0; i < visitor.temporaryCount; i++) {
10123 stmts.push(temporaryDeclaration(bindingId, i));
10124 }
10125 return stmts;
10126 }
10127 function convertBuiltins(converterFactory, ast) {
10128 const visitor = new _BuiltinAstConverter(converterFactory);
10129 return ast.visit(visitor);
10130 }
10131 function temporaryName(bindingId, temporaryNumber) {
10132 return `tmp_${bindingId}_${temporaryNumber}`;
10133 }
10134 function temporaryDeclaration(bindingId, temporaryNumber) {
10135 return new DeclareVarStmt(temporaryName(bindingId, temporaryNumber), NULL_EXPR);
10136 }
10137 function prependTemporaryDecls(temporaryCount, bindingId, statements) {
10138 for (let i = temporaryCount - 1; i >= 0; i--) {
10139 statements.unshift(temporaryDeclaration(bindingId, i));
10140 }
10141 }
10142 var _Mode;
10143 (function (_Mode) {
10144 _Mode[_Mode["Statement"] = 0] = "Statement";
10145 _Mode[_Mode["Expression"] = 1] = "Expression";
10146 })(_Mode || (_Mode = {}));
10147 function ensureStatementMode(mode, ast) {
10148 if (mode !== _Mode.Statement) {
10149 throw new Error(`Expected a statement, but saw ${ast}`);
10150 }
10151 }
10152 function ensureExpressionMode(mode, ast) {
10153 if (mode !== _Mode.Expression) {
10154 throw new Error(`Expected an expression, but saw ${ast}`);
10155 }
10156 }
10157 function convertToStatementIfNeeded(mode, expr) {
10158 if (mode === _Mode.Statement) {
10159 return expr.toStmt();
10160 }
10161 else {
10162 return expr;
10163 }
10164 }
10165 class _BuiltinAstConverter extends AstTransformer {
10166 constructor(_converterFactory) {
10167 super();
10168 this._converterFactory = _converterFactory;
10169 }
10170 visitPipe(ast, context) {
10171 const args = [ast.exp, ...ast.args].map(ast => ast.visit(this, context));
10172 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createPipeConverter(ast.name, args.length));
10173 }
10174 visitLiteralArray(ast, context) {
10175 const args = ast.expressions.map(ast => ast.visit(this, context));
10176 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralArrayConverter(ast.expressions.length));
10177 }
10178 visitLiteralMap(ast, context) {
10179 const args = ast.values.map(ast => ast.visit(this, context));
10180 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralMapConverter(ast.keys));
10181 }
10182 }
10183 class _AstToIrVisitor {
10184 constructor(_localResolver, _implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses) {
10185 this._localResolver = _localResolver;
10186 this._implicitReceiver = _implicitReceiver;
10187 this.bindingId = bindingId;
10188 this.interpolationFunction = interpolationFunction;
10189 this.baseSourceSpan = baseSourceSpan;
10190 this.implicitReceiverAccesses = implicitReceiverAccesses;
10191 this._nodeMap = new Map();
10192 this._resultMap = new Map();
10193 this._currentTemporary = 0;
10194 this.temporaryCount = 0;
10195 this.usesImplicitReceiver = false;
10196 }
10197 visitUnary(ast, mode) {
10198 let op;
10199 switch (ast.operator) {
10200 case '+':
10201 op = UnaryOperator.Plus;
10202 break;
10203 case '-':
10204 op = UnaryOperator.Minus;
10205 break;
10206 default:
10207 throw new Error(`Unsupported operator ${ast.operator}`);
10208 }
10209 return convertToStatementIfNeeded(mode, new UnaryOperatorExpr(op, this._visit(ast.expr, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
10210 }
10211 visitBinary(ast, mode) {
10212 let op;
10213 switch (ast.operation) {
10214 case '+':
10215 op = BinaryOperator.Plus;
10216 break;
10217 case '-':
10218 op = BinaryOperator.Minus;
10219 break;
10220 case '*':
10221 op = BinaryOperator.Multiply;
10222 break;
10223 case '/':
10224 op = BinaryOperator.Divide;
10225 break;
10226 case '%':
10227 op = BinaryOperator.Modulo;
10228 break;
10229 case '&&':
10230 op = BinaryOperator.And;
10231 break;
10232 case '||':
10233 op = BinaryOperator.Or;
10234 break;
10235 case '==':
10236 op = BinaryOperator.Equals;
10237 break;
10238 case '!=':
10239 op = BinaryOperator.NotEquals;
10240 break;
10241 case '===':
10242 op = BinaryOperator.Identical;
10243 break;
10244 case '!==':
10245 op = BinaryOperator.NotIdentical;
10246 break;
10247 case '<':
10248 op = BinaryOperator.Lower;
10249 break;
10250 case '>':
10251 op = BinaryOperator.Bigger;
10252 break;
10253 case '<=':
10254 op = BinaryOperator.LowerEquals;
10255 break;
10256 case '>=':
10257 op = BinaryOperator.BiggerEquals;
10258 break;
10259 default:
10260 throw new Error(`Unsupported operation ${ast.operation}`);
10261 }
10262 return convertToStatementIfNeeded(mode, new BinaryOperatorExpr(op, this._visit(ast.left, _Mode.Expression), this._visit(ast.right, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
10263 }
10264 visitChain(ast, mode) {
10265 ensureStatementMode(mode, ast);
10266 return this.visitAll(ast.expressions, mode);
10267 }
10268 visitConditional(ast, mode) {
10269 const value = this._visit(ast.condition, _Mode.Expression);
10270 return convertToStatementIfNeeded(mode, value.conditional(this._visit(ast.trueExp, _Mode.Expression), this._visit(ast.falseExp, _Mode.Expression), this.convertSourceSpan(ast.span)));
10271 }
10272 visitPipe(ast, mode) {
10273 throw new Error(`Illegal state: Pipes should have been converted into functions. Pipe: ${ast.name}`);
10274 }
10275 visitFunctionCall(ast, mode) {
10276 const convertedArgs = this.visitAll(ast.args, _Mode.Expression);
10277 let fnResult;
10278 if (ast instanceof BuiltinFunctionCall) {
10279 fnResult = ast.converter(convertedArgs);
10280 }
10281 else {
10282 fnResult = this._visit(ast.target, _Mode.Expression)
10283 .callFn(convertedArgs, this.convertSourceSpan(ast.span));
10284 }
10285 return convertToStatementIfNeeded(mode, fnResult);
10286 }
10287 visitImplicitReceiver(ast, mode) {
10288 ensureExpressionMode(mode, ast);
10289 this.usesImplicitReceiver = true;
10290 return this._implicitReceiver;
10291 }
10292 visitThisReceiver(ast, mode) {
10293 return this.visitImplicitReceiver(ast, mode);
10294 }
10295 visitInterpolation(ast, mode) {
10296 ensureExpressionMode(mode, ast);
10297 const args = [literal(ast.expressions.length)];
10298 for (let i = 0; i < ast.strings.length - 1; i++) {
10299 args.push(literal(ast.strings[i]));
10300 args.push(this._visit(ast.expressions[i], _Mode.Expression));
10301 }
10302 args.push(literal(ast.strings[ast.strings.length - 1]));
10303 if (this.interpolationFunction) {
10304 return this.interpolationFunction(args);
10305 }
10306 return ast.expressions.length <= 9 ?
10307 importExpr(Identifiers.inlineInterpolate).callFn(args) :
10308 importExpr(Identifiers.interpolate).callFn([
10309 args[0], literalArr(args.slice(1), undefined, this.convertSourceSpan(ast.span))
10310 ]);
10311 }
10312 visitKeyedRead(ast, mode) {
10313 const leftMostSafe = this.leftMostSafeNode(ast);
10314 if (leftMostSafe) {
10315 return this.convertSafeAccess(ast, leftMostSafe, mode);
10316 }
10317 else {
10318 return convertToStatementIfNeeded(mode, this._visit(ast.obj, _Mode.Expression).key(this._visit(ast.key, _Mode.Expression)));
10319 }
10320 }
10321 visitKeyedWrite(ast, mode) {
10322 const obj = this._visit(ast.obj, _Mode.Expression);
10323 const key = this._visit(ast.key, _Mode.Expression);
10324 const value = this._visit(ast.value, _Mode.Expression);
10325 return convertToStatementIfNeeded(mode, obj.key(key).set(value));
10326 }
10327 visitLiteralArray(ast, mode) {
10328 throw new Error(`Illegal State: literal arrays should have been converted into functions`);
10329 }
10330 visitLiteralMap(ast, mode) {
10331 throw new Error(`Illegal State: literal maps should have been converted into functions`);
10332 }
10333 visitLiteralPrimitive(ast, mode) {
10334 // For literal values of null, undefined, true, or false allow type interference
10335 // to infer the type.
10336 const type = ast.value === null || ast.value === undefined || ast.value === true || ast.value === true ?
10337 INFERRED_TYPE :
10338 undefined;
10339 return convertToStatementIfNeeded(mode, literal(ast.value, type, this.convertSourceSpan(ast.span)));
10340 }
10341 _getLocal(name, receiver) {
10342 var _a;
10343 if (((_a = this._localResolver.globals) === null || _a === void 0 ? void 0 : _a.has(name)) && receiver instanceof ThisReceiver) {
10344 return null;
10345 }
10346 return this._localResolver.getLocal(name);
10347 }
10348 visitMethodCall(ast, mode) {
10349 if (ast.receiver instanceof ImplicitReceiver &&
10350 !(ast.receiver instanceof ThisReceiver) && ast.name === '$any') {
10351 const args = this.visitAll(ast.args, _Mode.Expression);
10352 if (args.length != 1) {
10353 throw new Error(`Invalid call to $any, expected 1 argument but received ${args.length || 'none'}`);
10354 }
10355 return args[0].cast(DYNAMIC_TYPE, this.convertSourceSpan(ast.span));
10356 }
10357 const leftMostSafe = this.leftMostSafeNode(ast);
10358 if (leftMostSafe) {
10359 return this.convertSafeAccess(ast, leftMostSafe, mode);
10360 }
10361 else {
10362 const args = this.visitAll(ast.args, _Mode.Expression);
10363 const prevUsesImplicitReceiver = this.usesImplicitReceiver;
10364 let result = null;
10365 const receiver = this._visit(ast.receiver, _Mode.Expression);
10366 if (receiver === this._implicitReceiver) {
10367 const varExpr = this._getLocal(ast.name, ast.receiver);
10368 if (varExpr) {
10369 // Restore the previous "usesImplicitReceiver" state since the implicit
10370 // receiver has been replaced with a resolved local expression.
10371 this.usesImplicitReceiver = prevUsesImplicitReceiver;
10372 result = varExpr.callFn(args);
10373 this.addImplicitReceiverAccess(ast.name);
10374 }
10375 }
10376 if (result == null) {
10377 result = receiver.callMethod(ast.name, args, this.convertSourceSpan(ast.span));
10378 }
10379 return convertToStatementIfNeeded(mode, result);
10380 }
10381 }
10382 visitPrefixNot(ast, mode) {
10383 return convertToStatementIfNeeded(mode, not(this._visit(ast.expression, _Mode.Expression)));
10384 }
10385 visitNonNullAssert(ast, mode) {
10386 return convertToStatementIfNeeded(mode, assertNotNull(this._visit(ast.expression, _Mode.Expression)));
10387 }
10388 visitPropertyRead(ast, mode) {
10389 const leftMostSafe = this.leftMostSafeNode(ast);
10390 if (leftMostSafe) {
10391 return this.convertSafeAccess(ast, leftMostSafe, mode);
10392 }
10393 else {
10394 let result = null;
10395 const prevUsesImplicitReceiver = this.usesImplicitReceiver;
10396 const receiver = this._visit(ast.receiver, _Mode.Expression);
10397 if (receiver === this._implicitReceiver) {
10398 result = this._getLocal(ast.name, ast.receiver);
10399 if (result) {
10400 // Restore the previous "usesImplicitReceiver" state since the implicit
10401 // receiver has been replaced with a resolved local expression.
10402 this.usesImplicitReceiver = prevUsesImplicitReceiver;
10403 this.addImplicitReceiverAccess(ast.name);
10404 }
10405 }
10406 if (result == null) {
10407 result = receiver.prop(ast.name);
10408 }
10409 return convertToStatementIfNeeded(mode, result);
10410 }
10411 }
10412 visitPropertyWrite(ast, mode) {
10413 const receiver = this._visit(ast.receiver, _Mode.Expression);
10414 const prevUsesImplicitReceiver = this.usesImplicitReceiver;
10415 let varExpr = null;
10416 if (receiver === this._implicitReceiver) {
10417 const localExpr = this._getLocal(ast.name, ast.receiver);
10418 if (localExpr) {
10419 if (localExpr instanceof ReadPropExpr) {
10420 // If the local variable is a property read expression, it's a reference
10421 // to a 'context.property' value and will be used as the target of the
10422 // write expression.
10423 varExpr = localExpr;
10424 // Restore the previous "usesImplicitReceiver" state since the implicit
10425 // receiver has been replaced with a resolved local expression.
10426 this.usesImplicitReceiver = prevUsesImplicitReceiver;
10427 this.addImplicitReceiverAccess(ast.name);
10428 }
10429 else {
10430 // Otherwise it's an error.
10431 const receiver = ast.name;
10432 const value = (ast.value instanceof PropertyRead) ? ast.value.name : undefined;
10433 throw new Error(`Cannot assign value "${value}" to template variable "${receiver}". Template variables are read-only.`);
10434 }
10435 }
10436 }
10437 // If no local expression could be produced, use the original receiver's
10438 // property as the target.
10439 if (varExpr === null) {
10440 varExpr = receiver.prop(ast.name);
10441 }
10442 return convertToStatementIfNeeded(mode, varExpr.set(this._visit(ast.value, _Mode.Expression)));
10443 }
10444 visitSafePropertyRead(ast, mode) {
10445 return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
10446 }
10447 visitSafeMethodCall(ast, mode) {
10448 return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
10449 }
10450 visitAll(asts, mode) {
10451 return asts.map(ast => this._visit(ast, mode));
10452 }
10453 visitQuote(ast, mode) {
10454 throw new Error(`Quotes are not supported for evaluation!
10455 Statement: ${ast.uninterpretedExpression} located at ${ast.location}`);
10456 }
10457 _visit(ast, mode) {
10458 const result = this._resultMap.get(ast);
10459 if (result)
10460 return result;
10461 return (this._nodeMap.get(ast) || ast).visit(this, mode);
10462 }
10463 convertSafeAccess(ast, leftMostSafe, mode) {
10464 // If the expression contains a safe access node on the left it needs to be converted to
10465 // an expression that guards the access to the member by checking the receiver for blank. As
10466 // execution proceeds from left to right, the left most part of the expression must be guarded
10467 // first but, because member access is left associative, the right side of the expression is at
10468 // the top of the AST. The desired result requires lifting a copy of the left part of the
10469 // expression up to test it for blank before generating the unguarded version.
10470 // Consider, for example the following expression: a?.b.c?.d.e
10471 // This results in the ast:
10472 // .
10473 // / \
10474 // ?. e
10475 // / \
10476 // . d
10477 // / \
10478 // ?. c
10479 // / \
10480 // a b
10481 // The following tree should be generated:
10482 //
10483 // /---- ? ----\
10484 // / | \
10485 // a /--- ? ---\ null
10486 // / | \
10487 // . . null
10488 // / \ / \
10489 // . c . e
10490 // / \ / \
10491 // a b . d
10492 // / \
10493 // . c
10494 // / \
10495 // a b
10496 //
10497 // Notice that the first guard condition is the left hand of the left most safe access node
10498 // which comes in as leftMostSafe to this routine.
10499 let guardedExpression = this._visit(leftMostSafe.receiver, _Mode.Expression);
10500 let temporary = undefined;
10501 if (this.needsTemporary(leftMostSafe.receiver)) {
10502 // If the expression has method calls or pipes then we need to save the result into a
10503 // temporary variable to avoid calling stateful or impure code more than once.
10504 temporary = this.allocateTemporary();
10505 // Preserve the result in the temporary variable
10506 guardedExpression = temporary.set(guardedExpression);
10507 // Ensure all further references to the guarded expression refer to the temporary instead.
10508 this._resultMap.set(leftMostSafe.receiver, temporary);
10509 }
10510 const condition = guardedExpression.isBlank();
10511 // Convert the ast to an unguarded access to the receiver's member. The map will substitute
10512 // leftMostNode with its unguarded version in the call to `this.visit()`.
10513 if (leftMostSafe instanceof SafeMethodCall) {
10514 this._nodeMap.set(leftMostSafe, new MethodCall(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name, leftMostSafe.args));
10515 }
10516 else {
10517 this._nodeMap.set(leftMostSafe, new PropertyRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name));
10518 }
10519 // Recursively convert the node now without the guarded member access.
10520 const access = this._visit(ast, _Mode.Expression);
10521 // Remove the mapping. This is not strictly required as the converter only traverses each node
10522 // once but is safer if the conversion is changed to traverse the nodes more than once.
10523 this._nodeMap.delete(leftMostSafe);
10524 // If we allocated a temporary, release it.
10525 if (temporary) {
10526 this.releaseTemporary(temporary);
10527 }
10528 // Produce the conditional
10529 return convertToStatementIfNeeded(mode, condition.conditional(literal(null), access));
10530 }
10531 // Given an expression of the form a?.b.c?.d.e then the left most safe node is
10532 // the (a?.b). The . and ?. are left associative thus can be rewritten as:
10533 // ((((a?.c).b).c)?.d).e. This returns the most deeply nested safe read or
10534 // safe method call as this needs to be transformed initially to:
10535 // a == null ? null : a.c.b.c?.d.e
10536 // then to:
10537 // a == null ? null : a.b.c == null ? null : a.b.c.d.e
10538 leftMostSafeNode(ast) {
10539 const visit = (visitor, ast) => {
10540 return (this._nodeMap.get(ast) || ast).visit(visitor);
10541 };
10542 return ast.visit({
10543 visitUnary(ast) {
10544 return null;
10545 },
10546 visitBinary(ast) {
10547 return null;
10548 },
10549 visitChain(ast) {
10550 return null;
10551 },
10552 visitConditional(ast) {
10553 return null;
10554 },
10555 visitFunctionCall(ast) {
10556 return null;
10557 },
10558 visitImplicitReceiver(ast) {
10559 return null;
10560 },
10561 visitThisReceiver(ast) {
10562 return null;
10563 },
10564 visitInterpolation(ast) {
10565 return null;
10566 },
10567 visitKeyedRead(ast) {
10568 return visit(this, ast.obj);
10569 },
10570 visitKeyedWrite(ast) {
10571 return null;
10572 },
10573 visitLiteralArray(ast) {
10574 return null;
10575 },
10576 visitLiteralMap(ast) {
10577 return null;
10578 },
10579 visitLiteralPrimitive(ast) {
10580 return null;
10581 },
10582 visitMethodCall(ast) {
10583 return visit(this, ast.receiver);
10584 },
10585 visitPipe(ast) {
10586 return null;
10587 },
10588 visitPrefixNot(ast) {
10589 return null;
10590 },
10591 visitNonNullAssert(ast) {
10592 return null;
10593 },
10594 visitPropertyRead(ast) {
10595 return visit(this, ast.receiver);
10596 },
10597 visitPropertyWrite(ast) {
10598 return null;
10599 },
10600 visitQuote(ast) {
10601 return null;
10602 },
10603 visitSafeMethodCall(ast) {
10604 return visit(this, ast.receiver) || ast;
10605 },
10606 visitSafePropertyRead(ast) {
10607 return visit(this, ast.receiver) || ast;
10608 }
10609 });
10610 }
10611 // Returns true of the AST includes a method or a pipe indicating that, if the
10612 // expression is used as the target of a safe property or method access then
10613 // the expression should be stored into a temporary variable.
10614 needsTemporary(ast) {
10615 const visit = (visitor, ast) => {
10616 return ast && (this._nodeMap.get(ast) || ast).visit(visitor);
10617 };
10618 const visitSome = (visitor, ast) => {
10619 return ast.some(ast => visit(visitor, ast));
10620 };
10621 return ast.visit({
10622 visitUnary(ast) {
10623 return visit(this, ast.expr);
10624 },
10625 visitBinary(ast) {
10626 return visit(this, ast.left) || visit(this, ast.right);
10627 },
10628 visitChain(ast) {
10629 return false;
10630 },
10631 visitConditional(ast) {
10632 return visit(this, ast.condition) || visit(this, ast.trueExp) || visit(this, ast.falseExp);
10633 },
10634 visitFunctionCall(ast) {
10635 return true;
10636 },
10637 visitImplicitReceiver(ast) {
10638 return false;
10639 },
10640 visitThisReceiver(ast) {
10641 return false;
10642 },
10643 visitInterpolation(ast) {
10644 return visitSome(this, ast.expressions);
10645 },
10646 visitKeyedRead(ast) {
10647 return false;
10648 },
10649 visitKeyedWrite(ast) {
10650 return false;
10651 },
10652 visitLiteralArray(ast) {
10653 return true;
10654 },
10655 visitLiteralMap(ast) {
10656 return true;
10657 },
10658 visitLiteralPrimitive(ast) {
10659 return false;
10660 },
10661 visitMethodCall(ast) {
10662 return true;
10663 },
10664 visitPipe(ast) {
10665 return true;
10666 },
10667 visitPrefixNot(ast) {
10668 return visit(this, ast.expression);
10669 },
10670 visitNonNullAssert(ast) {
10671 return visit(this, ast.expression);
10672 },
10673 visitPropertyRead(ast) {
10674 return false;
10675 },
10676 visitPropertyWrite(ast) {
10677 return false;
10678 },
10679 visitQuote(ast) {
10680 return false;
10681 },
10682 visitSafeMethodCall(ast) {
10683 return true;
10684 },
10685 visitSafePropertyRead(ast) {
10686 return false;
10687 }
10688 });
10689 }
10690 allocateTemporary() {
10691 const tempNumber = this._currentTemporary++;
10692 this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount);
10693 return new ReadVarExpr(temporaryName(this.bindingId, tempNumber));
10694 }
10695 releaseTemporary(temporary) {
10696 this._currentTemporary--;
10697 if (temporary.name != temporaryName(this.bindingId, this._currentTemporary)) {
10698 throw new Error(`Temporary ${temporary.name} released out of order`);
10699 }
10700 }
10701 /**
10702 * Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`.
10703 *
10704 * `ParseSpan` objects are relative to the start of the expression.
10705 * This method converts these to full `ParseSourceSpan` objects that
10706 * show where the span is within the overall source file.
10707 *
10708 * @param span the relative span to convert.
10709 * @returns a `ParseSourceSpan` for the given span or null if no
10710 * `baseSourceSpan` was provided to this class.
10711 */
10712 convertSourceSpan(span) {
10713 if (this.baseSourceSpan) {
10714 const start = this.baseSourceSpan.start.moveBy(span.start);
10715 const end = this.baseSourceSpan.start.moveBy(span.end);
10716 const fullStart = this.baseSourceSpan.fullStart.moveBy(span.start);
10717 return new ParseSourceSpan(start, end, fullStart);
10718 }
10719 else {
10720 return null;
10721 }
10722 }
10723 /** Adds the name of an AST to the list of implicit receiver accesses. */
10724 addImplicitReceiverAccess(name) {
10725 if (this.implicitReceiverAccesses) {
10726 this.implicitReceiverAccesses.add(name);
10727 }
10728 }
10729 }
10730 function flattenStatements(arg, output) {
10731 if (Array.isArray(arg)) {
10732 arg.forEach((entry) => flattenStatements(entry, output));
10733 }
10734 else {
10735 output.push(arg);
10736 }
10737 }
10738 class DefaultLocalResolver {
10739 constructor(globals) {
10740 this.globals = globals;
10741 }
10742 notifyImplicitReceiverUse() { }
10743 getLocal(name) {
10744 if (name === EventHandlerVars.event.name) {
10745 return EventHandlerVars.event;
10746 }
10747 return null;
10748 }
10749 }
10750 function createCurrValueExpr(bindingId) {
10751 return variable(`currVal_${bindingId}`); // fix syntax highlighting: `
10752 }
10753 function createPreventDefaultVar(bindingId) {
10754 return variable(`pd_${bindingId}`);
10755 }
10756 function convertStmtIntoExpression(stmt) {
10757 if (stmt instanceof ExpressionStatement) {
10758 return stmt.expr;
10759 }
10760 else if (stmt instanceof ReturnStatement) {
10761 return stmt.value;
10762 }
10763 return null;
10764 }
10765 class BuiltinFunctionCall extends FunctionCall {
10766 constructor(span, sourceSpan, args, converter) {
10767 super(span, sourceSpan, null, args);
10768 this.args = args;
10769 this.converter = converter;
10770 }
10771 }
10772
10773 /**
10774 * @license
10775 * Copyright Google LLC All Rights Reserved.
10776 *
10777 * Use of this source code is governed by an MIT-style license that can be
10778 * found in the LICENSE file at https://angular.io/license
10779 */
10780 /**
10781 * This file is a port of shadowCSS from webcomponents.js to TypeScript.
10782 *
10783 * Please make sure to keep to edits in sync with the source file.
10784 *
10785 * Source:
10786 * https://github.com/webcomponents/webcomponentsjs/blob/4efecd7e0e/src/ShadowCSS/ShadowCSS.js
10787 *
10788 * The original file level comment is reproduced below
10789 */
10790 /*
10791 This is a limited shim for ShadowDOM css styling.
10792 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#styles
10793
10794 The intention here is to support only the styling features which can be
10795 relatively simply implemented. The goal is to allow users to avoid the
10796 most obvious pitfalls and do so without compromising performance significantly.
10797 For ShadowDOM styling that's not covered here, a set of best practices
10798 can be provided that should allow users to accomplish more complex styling.
10799
10800 The following is a list of specific ShadowDOM styling features and a brief
10801 discussion of the approach used to shim.
10802
10803 Shimmed features:
10804
10805 * :host, :host-context: ShadowDOM allows styling of the shadowRoot's host
10806 element using the :host rule. To shim this feature, the :host styles are
10807 reformatted and prefixed with a given scope name and promoted to a
10808 document level stylesheet.
10809 For example, given a scope name of .foo, a rule like this:
10810
10811 :host {
10812 background: red;
10813 }
10814 }
10815
10816 becomes:
10817
10818 .foo {
10819 background: red;
10820 }
10821
10822 * encapsulation: Styles defined within ShadowDOM, apply only to
10823 dom inside the ShadowDOM. Polymer uses one of two techniques to implement
10824 this feature.
10825
10826 By default, rules are prefixed with the host element tag name
10827 as a descendant selector. This ensures styling does not leak out of the 'top'
10828 of the element's ShadowDOM. For example,
10829
10830 div {
10831 font-weight: bold;
10832 }
10833
10834 becomes:
10835
10836 x-foo div {
10837 font-weight: bold;
10838 }
10839
10840 becomes:
10841
10842
10843 Alternatively, if WebComponents.ShadowCSS.strictStyling is set to true then
10844 selectors are scoped by adding an attribute selector suffix to each
10845 simple selector that contains the host element tag name. Each element
10846 in the element's ShadowDOM template is also given the scope attribute.
10847 Thus, these rules match only elements that have the scope attribute.
10848 For example, given a scope name of x-foo, a rule like this:
10849
10850 div {
10851 font-weight: bold;
10852 }
10853
10854 becomes:
10855
10856 div[x-foo] {
10857 font-weight: bold;
10858 }
10859
10860 Note that elements that are dynamically added to a scope must have the scope
10861 selector added to them manually.
10862
10863 * upper/lower bound encapsulation: Styles which are defined outside a
10864 shadowRoot should not cross the ShadowDOM boundary and should not apply
10865 inside a shadowRoot.
10866
10867 This styling behavior is not emulated. Some possible ways to do this that
10868 were rejected due to complexity and/or performance concerns include: (1) reset
10869 every possible property for every possible selector for a given scope name;
10870 (2) re-implement css in javascript.
10871
10872 As an alternative, users should make sure to use selectors
10873 specific to the scope in which they are working.
10874
10875 * ::distributed: This behavior is not emulated. It's often not necessary
10876 to style the contents of a specific insertion point and instead, descendants
10877 of the host element can be styled selectively. Users can also create an
10878 extra node around an insertion point and style that node's contents
10879 via descendent selectors. For example, with a shadowRoot like this:
10880
10881 <style>
10882 ::content(div) {
10883 background: red;
10884 }
10885 </style>
10886 <content></content>
10887
10888 could become:
10889
10890 <style>
10891 / *@polyfill .content-container div * /
10892 ::content(div) {
10893 background: red;
10894 }
10895 </style>
10896 <div class="content-container">
10897 <content></content>
10898 </div>
10899
10900 Note the use of @polyfill in the comment above a ShadowDOM specific style
10901 declaration. This is a directive to the styling shim to use the selector
10902 in comments in lieu of the next selector when running under polyfill.
10903 */
10904 class ShadowCss {
10905 constructor() {
10906 this.strictStyling = true;
10907 }
10908 /*
10909 * Shim some cssText with the given selector. Returns cssText that can
10910 * be included in the document via WebComponents.ShadowCSS.addCssToDocument(css).
10911 *
10912 * When strictStyling is true:
10913 * - selector is the attribute added to all elements inside the host,
10914 * - hostSelector is the attribute added to the host itself.
10915 */
10916 shimCssText(cssText, selector, hostSelector = '') {
10917 const commentsWithHash = extractCommentsWithHash(cssText);
10918 cssText = stripComments(cssText);
10919 cssText = this._insertDirectives(cssText);
10920 const scopedCssText = this._scopeCssText(cssText, selector, hostSelector);
10921 return [scopedCssText, ...commentsWithHash].join('\n');
10922 }
10923 _insertDirectives(cssText) {
10924 cssText = this._insertPolyfillDirectivesInCssText(cssText);
10925 return this._insertPolyfillRulesInCssText(cssText);
10926 }
10927 /*
10928 * Process styles to convert native ShadowDOM rules that will trip
10929 * up the css parser; we rely on decorating the stylesheet with inert rules.
10930 *
10931 * For example, we convert this rule:
10932 *
10933 * polyfill-next-selector { content: ':host menu-item'; }
10934 * ::content menu-item {
10935 *
10936 * to this:
10937 *
10938 * scopeName menu-item {
10939 *
10940 **/
10941 _insertPolyfillDirectivesInCssText(cssText) {
10942 // Difference with webcomponents.js: does not handle comments
10943 return cssText.replace(_cssContentNextSelectorRe, function (...m) {
10944 return m[2] + '{';
10945 });
10946 }
10947 /*
10948 * Process styles to add rules which will only apply under the polyfill
10949 *
10950 * For example, we convert this rule:
10951 *
10952 * polyfill-rule {
10953 * content: ':host menu-item';
10954 * ...
10955 * }
10956 *
10957 * to this:
10958 *
10959 * scopeName menu-item {...}
10960 *
10961 **/
10962 _insertPolyfillRulesInCssText(cssText) {
10963 // Difference with webcomponents.js: does not handle comments
10964 return cssText.replace(_cssContentRuleRe, (...m) => {
10965 const rule = m[0].replace(m[1], '').replace(m[2], '');
10966 return m[4] + rule;
10967 });
10968 }
10969 /* Ensure styles are scoped. Pseudo-scoping takes a rule like:
10970 *
10971 * .foo {... }
10972 *
10973 * and converts this to
10974 *
10975 * scopeName .foo { ... }
10976 */
10977 _scopeCssText(cssText, scopeSelector, hostSelector) {
10978 const unscopedRules = this._extractUnscopedRulesFromCssText(cssText);
10979 // replace :host and :host-context -shadowcsshost and -shadowcsshost respectively
10980 cssText = this._insertPolyfillHostInCssText(cssText);
10981 cssText = this._convertColonHost(cssText);
10982 cssText = this._convertColonHostContext(cssText);
10983 cssText = this._convertShadowDOMSelectors(cssText);
10984 if (scopeSelector) {
10985 cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector);
10986 }
10987 cssText = cssText + '\n' + unscopedRules;
10988 return cssText.trim();
10989 }
10990 /*
10991 * Process styles to add rules which will only apply under the polyfill
10992 * and do not process via CSSOM. (CSSOM is destructive to rules on rare
10993 * occasions, e.g. -webkit-calc on Safari.)
10994 * For example, we convert this rule:
10995 *
10996 * @polyfill-unscoped-rule {
10997 * content: 'menu-item';
10998 * ... }
10999 *
11000 * to this:
11001 *
11002 * menu-item {...}
11003 *
11004 **/
11005 _extractUnscopedRulesFromCssText(cssText) {
11006 // Difference with webcomponents.js: does not handle comments
11007 let r = '';
11008 let m;
11009 _cssContentUnscopedRuleRe.lastIndex = 0;
11010 while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) {
11011 const rule = m[0].replace(m[2], '').replace(m[1], m[4]);
11012 r += rule + '\n\n';
11013 }
11014 return r;
11015 }
11016 /*
11017 * convert a rule like :host(.foo) > .bar { }
11018 *
11019 * to
11020 *
11021 * .foo<scopeName> > .bar
11022 */
11023 _convertColonHost(cssText) {
11024 return cssText.replace(_cssColonHostRe, (_, hostSelectors, otherSelectors) => {
11025 if (hostSelectors) {
11026 const convertedSelectors = [];
11027 const hostSelectorArray = hostSelectors.split(',').map(p => p.trim());
11028 for (const hostSelector of hostSelectorArray) {
11029 if (!hostSelector)
11030 break;
11031 const convertedSelector = _polyfillHostNoCombinator + hostSelector.replace(_polyfillHost, '') + otherSelectors;
11032 convertedSelectors.push(convertedSelector);
11033 }
11034 return convertedSelectors.join(',');
11035 }
11036 else {
11037 return _polyfillHostNoCombinator + otherSelectors;
11038 }
11039 });
11040 }
11041 /*
11042 * convert a rule like :host-context(.foo) > .bar { }
11043 *
11044 * to
11045 *
11046 * .foo<scopeName> > .bar, .foo <scopeName> > .bar { }
11047 *
11048 * and
11049 *
11050 * :host-context(.foo:host) .bar { ... }
11051 *
11052 * to
11053 *
11054 * .foo<scopeName> .bar { ... }
11055 */
11056 _convertColonHostContext(cssText) {
11057 return cssText.replace(_cssColonHostContextReGlobal, selectorText => {
11058 // We have captured a selector that contains a `:host-context` rule.
11059 var _a;
11060 // For backward compatibility `:host-context` may contain a comma separated list of selectors.
11061 // Each context selector group will contain a list of host-context selectors that must match
11062 // an ancestor of the host.
11063 // (Normally `contextSelectorGroups` will only contain a single array of context selectors.)
11064 const contextSelectorGroups = [[]];
11065 // There may be more than `:host-context` in this selector so `selectorText` could look like:
11066 // `:host-context(.one):host-context(.two)`.
11067 // Execute `_cssColonHostContextRe` over and over until we have extracted all the
11068 // `:host-context` selectors from this selector.
11069 let match;
11070 while (match = _cssColonHostContextRe.exec(selectorText)) {
11071 // `match` = [':host-context(<selectors>)<rest>', <selectors>, <rest>]
11072 // The `<selectors>` could actually be a comma separated list: `:host-context(.one, .two)`.
11073 const newContextSelectors = ((_a = match[1]) !== null && _a !== void 0 ? _a : '').trim().split(',').map(m => m.trim()).filter(m => m !== '');
11074 // We must duplicate the current selector group for each of these new selectors.
11075 // For example if the current groups are:
11076 // ```
11077 // [
11078 // ['a', 'b', 'c'],
11079 // ['x', 'y', 'z'],
11080 // ]
11081 // ```
11082 // And we have a new set of comma separated selectors: `:host-context(m,n)` then the new
11083 // groups are:
11084 // ```
11085 // [
11086 // ['a', 'b', 'c', 'm'],
11087 // ['x', 'y', 'z', 'm'],
11088 // ['a', 'b', 'c', 'n'],
11089 // ['x', 'y', 'z', 'n'],
11090 // ]
11091 // ```
11092 const contextSelectorGroupsLength = contextSelectorGroups.length;
11093 repeatGroups(contextSelectorGroups, newContextSelectors.length);
11094 for (let i = 0; i < newContextSelectors.length; i++) {
11095 for (let j = 0; j < contextSelectorGroupsLength; j++) {
11096 contextSelectorGroups[j + (i * contextSelectorGroupsLength)].push(newContextSelectors[i]);
11097 }
11098 }
11099 // Update the `selectorText` and see repeat to see if there are more `:host-context`s.
11100 selectorText = match[2];
11101 }
11102 // The context selectors now must be combined with each other to capture all the possible
11103 // selectors that `:host-context` can match. See `combineHostContextSelectors()` for more
11104 // info about how this is done.
11105 return contextSelectorGroups
11106 .map(contextSelectors => combineHostContextSelectors(contextSelectors, selectorText))
11107 .join(', ');
11108 });
11109 }
11110 /*
11111 * Convert combinators like ::shadow and pseudo-elements like ::content
11112 * by replacing with space.
11113 */
11114 _convertShadowDOMSelectors(cssText) {
11115 return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText);
11116 }
11117 // change a selector like 'div' to 'name div'
11118 _scopeSelectors(cssText, scopeSelector, hostSelector) {
11119 return processRules(cssText, (rule) => {
11120 let selector = rule.selector;
11121 let content = rule.content;
11122 if (rule.selector[0] != '@') {
11123 selector =
11124 this._scopeSelector(rule.selector, scopeSelector, hostSelector, this.strictStyling);
11125 }
11126 else if (rule.selector.startsWith('@media') || rule.selector.startsWith('@supports') ||
11127 rule.selector.startsWith('@page') || rule.selector.startsWith('@document')) {
11128 content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
11129 }
11130 return new CssRule(selector, content);
11131 });
11132 }
11133 _scopeSelector(selector, scopeSelector, hostSelector, strict) {
11134 return selector.split(',')
11135 .map(part => part.trim().split(_shadowDeepSelectors))
11136 .map((deepParts) => {
11137 const [shallowPart, ...otherParts] = deepParts;
11138 const applyScope = (shallowPart) => {
11139 if (this._selectorNeedsScoping(shallowPart, scopeSelector)) {
11140 return strict ?
11141 this._applyStrictSelectorScope(shallowPart, scopeSelector, hostSelector) :
11142 this._applySelectorScope(shallowPart, scopeSelector, hostSelector);
11143 }
11144 else {
11145 return shallowPart;
11146 }
11147 };
11148 return [applyScope(shallowPart), ...otherParts].join(' ');
11149 })
11150 .join(', ');
11151 }
11152 _selectorNeedsScoping(selector, scopeSelector) {
11153 const re = this._makeScopeMatcher(scopeSelector);
11154 return !re.test(selector);
11155 }
11156 _makeScopeMatcher(scopeSelector) {
11157 const lre = /\[/g;
11158 const rre = /\]/g;
11159 scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]');
11160 return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm');
11161 }
11162 _applySelectorScope(selector, scopeSelector, hostSelector) {
11163 // Difference from webcomponents.js: scopeSelector could not be an array
11164 return this._applySimpleSelectorScope(selector, scopeSelector, hostSelector);
11165 }
11166 // scope via name and [is=name]
11167 _applySimpleSelectorScope(selector, scopeSelector, hostSelector) {
11168 // In Android browser, the lastIndex is not reset when the regex is used in String.replace()
11169 _polyfillHostRe.lastIndex = 0;
11170 if (_polyfillHostRe.test(selector)) {
11171 const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
11172 return selector
11173 .replace(_polyfillHostNoCombinatorRe, (hnc, selector) => {
11174 return selector.replace(/([^:]*)(:*)(.*)/, (_, before, colon, after) => {
11175 return before + replaceBy + colon + after;
11176 });
11177 })
11178 .replace(_polyfillHostRe, replaceBy + ' ');
11179 }
11180 return scopeSelector + ' ' + selector;
11181 }
11182 // return a selector with [name] suffix on each simple selector
11183 // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] /** @internal */
11184 _applyStrictSelectorScope(selector, scopeSelector, hostSelector) {
11185 const isRe = /\[is=([^\]]*)\]/g;
11186 scopeSelector = scopeSelector.replace(isRe, (_, ...parts) => parts[0]);
11187 const attrName = '[' + scopeSelector + ']';
11188 const _scopeSelectorPart = (p) => {
11189 let scopedP = p.trim();
11190 if (!scopedP) {
11191 return '';
11192 }
11193 if (p.indexOf(_polyfillHostNoCombinator) > -1) {
11194 scopedP = this._applySimpleSelectorScope(p, scopeSelector, hostSelector);
11195 }
11196 else {
11197 // remove :host since it should be unnecessary
11198 const t = p.replace(_polyfillHostRe, '');
11199 if (t.length > 0) {
11200 const matches = t.match(/([^:]*)(:*)(.*)/);
11201 if (matches) {
11202 scopedP = matches[1] + attrName + matches[2] + matches[3];
11203 }
11204 }
11205 }
11206 return scopedP;
11207 };
11208 const safeContent = new SafeSelector(selector);
11209 selector = safeContent.content();
11210 let scopedSelector = '';
11211 let startIndex = 0;
11212 let res;
11213 const sep = /( |>|\+|~(?!=))\s*/g;
11214 // If a selector appears before :host it should not be shimmed as it
11215 // matches on ancestor elements and not on elements in the host's shadow
11216 // `:host-context(div)` is transformed to
11217 // `-shadowcsshost-no-combinatordiv, div -shadowcsshost-no-combinator`
11218 // the `div` is not part of the component in the 2nd selectors and should not be scoped.
11219 // Historically `component-tag:host` was matching the component so we also want to preserve
11220 // this behavior to avoid breaking legacy apps (it should not match).
11221 // The behavior should be:
11222 // - `tag:host` -> `tag[h]` (this is to avoid breaking legacy apps, should not match anything)
11223 // - `tag :host` -> `tag [h]` (`tag` is not scoped because it's considered part of a
11224 // `:host-context(tag)`)
11225 const hasHost = selector.indexOf(_polyfillHostNoCombinator) > -1;
11226 // Only scope parts after the first `-shadowcsshost-no-combinator` when it is present
11227 let shouldScope = !hasHost;
11228 while ((res = sep.exec(selector)) !== null) {
11229 const separator = res[1];
11230 const part = selector.slice(startIndex, res.index).trim();
11231 shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
11232 const scopedPart = shouldScope ? _scopeSelectorPart(part) : part;
11233 scopedSelector += `${scopedPart} ${separator} `;
11234 startIndex = sep.lastIndex;
11235 }
11236 const part = selector.substring(startIndex);
11237 shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
11238 scopedSelector += shouldScope ? _scopeSelectorPart(part) : part;
11239 // replace the placeholders with their original values
11240 return safeContent.restore(scopedSelector);
11241 }
11242 _insertPolyfillHostInCssText(selector) {
11243 return selector.replace(_colonHostContextRe, _polyfillHostContext)
11244 .replace(_colonHostRe, _polyfillHost);
11245 }
11246 }
11247 class SafeSelector {
11248 constructor(selector) {
11249 this.placeholders = [];
11250 this.index = 0;
11251 // Replaces attribute selectors with placeholders.
11252 // The WS in [attr="va lue"] would otherwise be interpreted as a selector separator.
11253 selector = this._escapeRegexMatches(selector, /(\[[^\]]*\])/g);
11254 // CSS allows for certain special characters to be used in selectors if they're escaped.
11255 // E.g. `.foo:blue` won't match a class called `foo:blue`, because the colon denotes a
11256 // pseudo-class, but writing `.foo\:blue` will match, because the colon was escaped.
11257 // Replace all escape sequences (`\` followed by a character) with a placeholder so
11258 // that our handling of pseudo-selectors doesn't mess with them.
11259 selector = this._escapeRegexMatches(selector, /(\\.)/g);
11260 // Replaces the expression in `:nth-child(2n + 1)` with a placeholder.
11261 // WS and "+" would otherwise be interpreted as selector separators.
11262 this._content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => {
11263 const replaceBy = `__ph-${this.index}__`;
11264 this.placeholders.push(exp);
11265 this.index++;
11266 return pseudo + replaceBy;
11267 });
11268 }
11269 restore(content) {
11270 return content.replace(/__ph-(\d+)__/g, (_ph, index) => this.placeholders[+index]);
11271 }
11272 content() {
11273 return this._content;
11274 }
11275 /**
11276 * Replaces all of the substrings that match a regex within a
11277 * special string (e.g. `__ph-0__`, `__ph-1__`, etc).
11278 */
11279 _escapeRegexMatches(content, pattern) {
11280 return content.replace(pattern, (_, keep) => {
11281 const replaceBy = `__ph-${this.index}__`;
11282 this.placeholders.push(keep);
11283 this.index++;
11284 return replaceBy;
11285 });
11286 }
11287 }
11288 const _cssContentNextSelectorRe = /polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
11289 const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
11290 const _cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
11291 const _polyfillHost = '-shadowcsshost';
11292 // note: :host-context pre-processed to -shadowcsshostcontext.
11293 const _polyfillHostContext = '-shadowcsscontext';
11294 const _parenSuffix = '(?:\\((' +
11295 '(?:\\([^)(]*\\)|[^)(]*)+?' +
11296 ')\\))?([^,{]*)';
11297 const _cssColonHostRe = new RegExp(_polyfillHost + _parenSuffix, 'gim');
11298 const _cssColonHostContextReGlobal = new RegExp(_polyfillHostContext + _parenSuffix, 'gim');
11299 const _cssColonHostContextRe = new RegExp(_polyfillHostContext + _parenSuffix, 'im');
11300 const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';
11301 const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/;
11302 const _shadowDOMSelectorsRe = [
11303 /::shadow/g,
11304 /::content/g,
11305 // Deprecated selectors
11306 /\/shadow-deep\//g,
11307 /\/shadow\//g,
11308 ];
11309 // The deep combinator is deprecated in the CSS spec
11310 // Support for `>>>`, `deep`, `::ng-deep` is then also deprecated and will be removed in the future.
11311 // see https://github.com/angular/angular/pull/17677
11312 const _shadowDeepSelectors = /(?:>>>)|(?:\/deep\/)|(?:::ng-deep)/g;
11313 const _selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$';
11314 const _polyfillHostRe = /-shadowcsshost/gim;
11315 const _colonHostRe = /:host/gim;
11316 const _colonHostContextRe = /:host-context/gim;
11317 const _commentRe = /\/\*\s*[\s\S]*?\*\//g;
11318 function stripComments(input) {
11319 return input.replace(_commentRe, '');
11320 }
11321 const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=[\s\S]+?\*\//g;
11322 function extractCommentsWithHash(input) {
11323 return input.match(_commentWithHashRe) || [];
11324 }
11325 const BLOCK_PLACEHOLDER = '%BLOCK%';
11326 const QUOTE_PLACEHOLDER = '%QUOTED%';
11327 const _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g;
11328 const _quotedRe = /%QUOTED%/g;
11329 const CONTENT_PAIRS = new Map([['{', '}']]);
11330 const QUOTE_PAIRS = new Map([[`"`, `"`], [`'`, `'`]]);
11331 class CssRule {
11332 constructor(selector, content) {
11333 this.selector = selector;
11334 this.content = content;
11335 }
11336 }
11337 function processRules(input, ruleCallback) {
11338 const inputWithEscapedQuotes = escapeBlocks(input, QUOTE_PAIRS, QUOTE_PLACEHOLDER);
11339 const inputWithEscapedBlocks = escapeBlocks(inputWithEscapedQuotes.escapedString, CONTENT_PAIRS, BLOCK_PLACEHOLDER);
11340 let nextBlockIndex = 0;
11341 let nextQuoteIndex = 0;
11342 return inputWithEscapedBlocks.escapedString
11343 .replace(_ruleRe, (...m) => {
11344 const selector = m[2];
11345 let content = '';
11346 let suffix = m[4];
11347 let contentPrefix = '';
11348 if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) {
11349 content = inputWithEscapedBlocks.blocks[nextBlockIndex++];
11350 suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1);
11351 contentPrefix = '{';
11352 }
11353 const rule = ruleCallback(new CssRule(selector, content));
11354 return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`;
11355 })
11356 .replace(_quotedRe, () => inputWithEscapedQuotes.blocks[nextQuoteIndex++]);
11357 }
11358 class StringWithEscapedBlocks {
11359 constructor(escapedString, blocks) {
11360 this.escapedString = escapedString;
11361 this.blocks = blocks;
11362 }
11363 }
11364 function escapeBlocks(input, charPairs, placeholder) {
11365 const resultParts = [];
11366 const escapedBlocks = [];
11367 let openCharCount = 0;
11368 let nonBlockStartIndex = 0;
11369 let blockStartIndex = -1;
11370 let openChar;
11371 let closeChar;
11372 for (let i = 0; i < input.length; i++) {
11373 const char = input[i];
11374 if (char === '\\') {
11375 i++;
11376 }
11377 else if (char === closeChar) {
11378 openCharCount--;
11379 if (openCharCount === 0) {
11380 escapedBlocks.push(input.substring(blockStartIndex, i));
11381 resultParts.push(placeholder);
11382 nonBlockStartIndex = i;
11383 blockStartIndex = -1;
11384 openChar = closeChar = undefined;
11385 }
11386 }
11387 else if (char === openChar) {
11388 openCharCount++;
11389 }
11390 else if (openCharCount === 0 && charPairs.has(char)) {
11391 openChar = char;
11392 closeChar = charPairs.get(char);
11393 openCharCount = 1;
11394 blockStartIndex = i + 1;
11395 resultParts.push(input.substring(nonBlockStartIndex, blockStartIndex));
11396 }
11397 }
11398 if (blockStartIndex !== -1) {
11399 escapedBlocks.push(input.substring(blockStartIndex));
11400 resultParts.push(placeholder);
11401 }
11402 else {
11403 resultParts.push(input.substring(nonBlockStartIndex));
11404 }
11405 return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks);
11406 }
11407 /**
11408 * Combine the `contextSelectors` with the `hostMarker` and the `otherSelectors`
11409 * to create a selector that matches the same as `:host-context()`.
11410 *
11411 * Given a single context selector `A` we need to output selectors that match on the host and as an
11412 * ancestor of the host:
11413 *
11414 * ```
11415 * A <hostMarker>, A<hostMarker> {}
11416 * ```
11417 *
11418 * When there is more than one context selector we also have to create combinations of those
11419 * selectors with each other. For example if there are `A` and `B` selectors the output is:
11420 *
11421 * ```
11422 * AB<hostMarker>, AB <hostMarker>, A B<hostMarker>,
11423 * B A<hostMarker>, A B <hostMarker>, B A <hostMarker> {}
11424 * ```
11425 *
11426 * And so on...
11427 *
11428 * @param hostMarker the string that selects the host element.
11429 * @param contextSelectors an array of context selectors that will be combined.
11430 * @param otherSelectors the rest of the selectors that are not context selectors.
11431 */
11432 function combineHostContextSelectors(contextSelectors, otherSelectors) {
11433 const hostMarker = _polyfillHostNoCombinator;
11434 const otherSelectorsHasHost = _polyfillHostRe.test(otherSelectors);
11435 // If there are no context selectors then just output a host marker
11436 if (contextSelectors.length === 0) {
11437 return hostMarker + otherSelectors;
11438 }
11439 const combined = [contextSelectors.pop() || ''];
11440 while (contextSelectors.length > 0) {
11441 const length = combined.length;
11442 const contextSelector = contextSelectors.pop();
11443 for (let i = 0; i < length; i++) {
11444 const previousSelectors = combined[i];
11445 // Add the new selector as a descendant of the previous selectors
11446 combined[length * 2 + i] = previousSelectors + ' ' + contextSelector;
11447 // Add the new selector as an ancestor of the previous selectors
11448 combined[length + i] = contextSelector + ' ' + previousSelectors;
11449 // Add the new selector to act on the same element as the previous selectors
11450 combined[i] = contextSelector + previousSelectors;
11451 }
11452 }
11453 // Finally connect the selector to the `hostMarker`s: either acting directly on the host
11454 // (A<hostMarker>) or as an ancestor (A <hostMarker>).
11455 return combined
11456 .map(s => otherSelectorsHasHost ?
11457 `${s}${otherSelectors}` :
11458 `${s}${hostMarker}${otherSelectors}, ${s} ${hostMarker}${otherSelectors}`)
11459 .join(',');
11460 }
11461 /**
11462 * Mutate the given `groups` array so that there are `multiples` clones of the original array
11463 * stored.
11464 *
11465 * For example `repeatGroups([a, b], 3)` will result in `[a, b, a, b, a, b]` - but importantly the
11466 * newly added groups will be clones of the original.
11467 *
11468 * @param groups An array of groups of strings that will be repeated. This array is mutated
11469 * in-place.
11470 * @param multiples The number of times the current groups should appear.
11471 */
11472 function repeatGroups(groups, multiples) {
11473 const length = groups.length;
11474 for (let i = 1; i < multiples; i++) {
11475 for (let j = 0; j < length; j++) {
11476 groups[j + (i * length)] = groups[j].slice(0);
11477 }
11478 }
11479 }
11480
11481 /**
11482 * @license
11483 * Copyright Google LLC All Rights Reserved.
11484 *
11485 * Use of this source code is governed by an MIT-style license that can be
11486 * found in the LICENSE file at https://angular.io/license
11487 */
11488 const COMPONENT_VARIABLE = '%COMP%';
11489 const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
11490 const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
11491
11492 /**
11493 * @license
11494 * Copyright Google LLC All Rights Reserved.
11495 *
11496 * Use of this source code is governed by an MIT-style license that can be
11497 * found in the LICENSE file at https://angular.io/license
11498 */
11499 class NodeWithI18n {
11500 constructor(sourceSpan, i18n) {
11501 this.sourceSpan = sourceSpan;
11502 this.i18n = i18n;
11503 }
11504 }
11505 class Text$2 extends NodeWithI18n {
11506 constructor(value, sourceSpan, i18n) {
11507 super(sourceSpan, i18n);
11508 this.value = value;
11509 }
11510 visit(visitor, context) {
11511 return visitor.visitText(this, context);
11512 }
11513 }
11514 class Expansion extends NodeWithI18n {
11515 constructor(switchValue, type, cases, sourceSpan, switchValueSourceSpan, i18n) {
11516 super(sourceSpan, i18n);
11517 this.switchValue = switchValue;
11518 this.type = type;
11519 this.cases = cases;
11520 this.switchValueSourceSpan = switchValueSourceSpan;
11521 }
11522 visit(visitor, context) {
11523 return visitor.visitExpansion(this, context);
11524 }
11525 }
11526 class ExpansionCase {
11527 constructor(value, expression, sourceSpan, valueSourceSpan, expSourceSpan) {
11528 this.value = value;
11529 this.expression = expression;
11530 this.sourceSpan = sourceSpan;
11531 this.valueSourceSpan = valueSourceSpan;
11532 this.expSourceSpan = expSourceSpan;
11533 }
11534 visit(visitor, context) {
11535 return visitor.visitExpansionCase(this, context);
11536 }
11537 }
11538 class Attribute extends NodeWithI18n {
11539 constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) {
11540 super(sourceSpan, i18n);
11541 this.name = name;
11542 this.value = value;
11543 this.keySpan = keySpan;
11544 this.valueSpan = valueSpan;
11545 }
11546 visit(visitor, context) {
11547 return visitor.visitAttribute(this, context);
11548 }
11549 }
11550 class Element$1 extends NodeWithI18n {
11551 constructor(name, attrs, children, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) {
11552 super(sourceSpan, i18n);
11553 this.name = name;
11554 this.attrs = attrs;
11555 this.children = children;
11556 this.startSourceSpan = startSourceSpan;
11557 this.endSourceSpan = endSourceSpan;
11558 }
11559 visit(visitor, context) {
11560 return visitor.visitElement(this, context);
11561 }
11562 }
11563 class Comment {
11564 constructor(value, sourceSpan) {
11565 this.value = value;
11566 this.sourceSpan = sourceSpan;
11567 }
11568 visit(visitor, context) {
11569 return visitor.visitComment(this, context);
11570 }
11571 }
11572 function visitAll$1(visitor, nodes, context = null) {
11573 const result = [];
11574 const visit = visitor.visit ?
11575 (ast) => visitor.visit(ast, context) || ast.visit(visitor, context) :
11576 (ast) => ast.visit(visitor, context);
11577 nodes.forEach(ast => {
11578 const astResult = visit(ast);
11579 if (astResult) {
11580 result.push(astResult);
11581 }
11582 });
11583 return result;
11584 }
11585
11586 /**
11587 * @license
11588 * Copyright Google LLC All Rights Reserved.
11589 *
11590 * Use of this source code is governed by an MIT-style license that can be
11591 * found in the LICENSE file at https://angular.io/license
11592 */
11593 var TokenType;
11594 (function (TokenType) {
11595 TokenType[TokenType["TAG_OPEN_START"] = 0] = "TAG_OPEN_START";
11596 TokenType[TokenType["TAG_OPEN_END"] = 1] = "TAG_OPEN_END";
11597 TokenType[TokenType["TAG_OPEN_END_VOID"] = 2] = "TAG_OPEN_END_VOID";
11598 TokenType[TokenType["TAG_CLOSE"] = 3] = "TAG_CLOSE";
11599 TokenType[TokenType["INCOMPLETE_TAG_OPEN"] = 4] = "INCOMPLETE_TAG_OPEN";
11600 TokenType[TokenType["TEXT"] = 5] = "TEXT";
11601 TokenType[TokenType["ESCAPABLE_RAW_TEXT"] = 6] = "ESCAPABLE_RAW_TEXT";
11602 TokenType[TokenType["RAW_TEXT"] = 7] = "RAW_TEXT";
11603 TokenType[TokenType["COMMENT_START"] = 8] = "COMMENT_START";
11604 TokenType[TokenType["COMMENT_END"] = 9] = "COMMENT_END";
11605 TokenType[TokenType["CDATA_START"] = 10] = "CDATA_START";
11606 TokenType[TokenType["CDATA_END"] = 11] = "CDATA_END";
11607 TokenType[TokenType["ATTR_NAME"] = 12] = "ATTR_NAME";
11608 TokenType[TokenType["ATTR_QUOTE"] = 13] = "ATTR_QUOTE";
11609 TokenType[TokenType["ATTR_VALUE"] = 14] = "ATTR_VALUE";
11610 TokenType[TokenType["DOC_TYPE"] = 15] = "DOC_TYPE";
11611 TokenType[TokenType["EXPANSION_FORM_START"] = 16] = "EXPANSION_FORM_START";
11612 TokenType[TokenType["EXPANSION_CASE_VALUE"] = 17] = "EXPANSION_CASE_VALUE";
11613 TokenType[TokenType["EXPANSION_CASE_EXP_START"] = 18] = "EXPANSION_CASE_EXP_START";
11614 TokenType[TokenType["EXPANSION_CASE_EXP_END"] = 19] = "EXPANSION_CASE_EXP_END";
11615 TokenType[TokenType["EXPANSION_FORM_END"] = 20] = "EXPANSION_FORM_END";
11616 TokenType[TokenType["EOF"] = 21] = "EOF";
11617 })(TokenType || (TokenType = {}));
11618 class Token {
11619 constructor(type, parts, sourceSpan) {
11620 this.type = type;
11621 this.parts = parts;
11622 this.sourceSpan = sourceSpan;
11623 }
11624 }
11625 class TokenError extends ParseError {
11626 constructor(errorMsg, tokenType, span) {
11627 super(span, errorMsg);
11628 this.tokenType = tokenType;
11629 }
11630 }
11631 class TokenizeResult {
11632 constructor(tokens, errors, nonNormalizedIcuExpressions) {
11633 this.tokens = tokens;
11634 this.errors = errors;
11635 this.nonNormalizedIcuExpressions = nonNormalizedIcuExpressions;
11636 }
11637 }
11638 function tokenize(source, url, getTagDefinition, options = {}) {
11639 const tokenizer = new _Tokenizer(new ParseSourceFile(source, url), getTagDefinition, options);
11640 tokenizer.tokenize();
11641 return new TokenizeResult(mergeTextTokens(tokenizer.tokens), tokenizer.errors, tokenizer.nonNormalizedIcuExpressions);
11642 }
11643 const _CR_OR_CRLF_REGEXP = /\r\n?/g;
11644 function _unexpectedCharacterErrorMsg(charCode) {
11645 const char = charCode === $EOF ? 'EOF' : String.fromCharCode(charCode);
11646 return `Unexpected character "${char}"`;
11647 }
11648 function _unknownEntityErrorMsg(entitySrc) {
11649 return `Unknown entity "${entitySrc}" - use the "&#<decimal>;" or "&#x<hex>;" syntax`;
11650 }
11651 function _unparsableEntityErrorMsg(type, entityStr) {
11652 return `Unable to parse entity "${entityStr}" - ${type} character reference entities must end with ";"`;
11653 }
11654 var CharacterReferenceType;
11655 (function (CharacterReferenceType) {
11656 CharacterReferenceType["HEX"] = "hexadecimal";
11657 CharacterReferenceType["DEC"] = "decimal";
11658 })(CharacterReferenceType || (CharacterReferenceType = {}));
11659 class _ControlFlowError {
11660 constructor(error) {
11661 this.error = error;
11662 }
11663 }
11664 // See https://www.w3.org/TR/html51/syntax.html#writing-html-documents
11665 class _Tokenizer {
11666 /**
11667 * @param _file The html source file being tokenized.
11668 * @param _getTagDefinition A function that will retrieve a tag definition for a given tag name.
11669 * @param options Configuration of the tokenization.
11670 */
11671 constructor(_file, _getTagDefinition, options) {
11672 this._getTagDefinition = _getTagDefinition;
11673 this._currentTokenStart = null;
11674 this._currentTokenType = null;
11675 this._expansionCaseStack = [];
11676 this._inInterpolation = false;
11677 this.tokens = [];
11678 this.errors = [];
11679 this.nonNormalizedIcuExpressions = [];
11680 this._tokenizeIcu = options.tokenizeExpansionForms || false;
11681 this._interpolationConfig = options.interpolationConfig || DEFAULT_INTERPOLATION_CONFIG;
11682 this._leadingTriviaCodePoints =
11683 options.leadingTriviaChars && options.leadingTriviaChars.map(c => c.codePointAt(0) || 0);
11684 const range = options.range || { endPos: _file.content.length, startPos: 0, startLine: 0, startCol: 0 };
11685 this._cursor = options.escapedString ? new EscapedCharacterCursor(_file, range) :
11686 new PlainCharacterCursor(_file, range);
11687 this._preserveLineEndings = options.preserveLineEndings || false;
11688 this._escapedString = options.escapedString || false;
11689 this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
11690 try {
11691 this._cursor.init();
11692 }
11693 catch (e) {
11694 this.handleError(e);
11695 }
11696 }
11697 _processCarriageReturns(content) {
11698 if (this._preserveLineEndings) {
11699 return content;
11700 }
11701 // https://www.w3.org/TR/html51/syntax.html#preprocessing-the-input-stream
11702 // In order to keep the original position in the source, we can not
11703 // pre-process it.
11704 // Instead CRs are processed right before instantiating the tokens.
11705 return content.replace(_CR_OR_CRLF_REGEXP, '\n');
11706 }
11707 tokenize() {
11708 while (this._cursor.peek() !== $EOF) {
11709 const start = this._cursor.clone();
11710 try {
11711 if (this._attemptCharCode($LT)) {
11712 if (this._attemptCharCode($BANG)) {
11713 if (this._attemptCharCode($LBRACKET)) {
11714 this._consumeCdata(start);
11715 }
11716 else if (this._attemptCharCode($MINUS)) {
11717 this._consumeComment(start);
11718 }
11719 else {
11720 this._consumeDocType(start);
11721 }
11722 }
11723 else if (this._attemptCharCode($SLASH)) {
11724 this._consumeTagClose(start);
11725 }
11726 else {
11727 this._consumeTagOpen(start);
11728 }
11729 }
11730 else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {
11731 this._consumeText();
11732 }
11733 }
11734 catch (e) {
11735 this.handleError(e);
11736 }
11737 }
11738 this._beginToken(TokenType.EOF);
11739 this._endToken([]);
11740 }
11741 /**
11742 * @returns whether an ICU token has been created
11743 * @internal
11744 */
11745 _tokenizeExpansionForm() {
11746 if (this.isExpansionFormStart()) {
11747 this._consumeExpansionFormStart();
11748 return true;
11749 }
11750 if (isExpansionCaseStart(this._cursor.peek()) && this._isInExpansionForm()) {
11751 this._consumeExpansionCaseStart();
11752 return true;
11753 }
11754 if (this._cursor.peek() === $RBRACE) {
11755 if (this._isInExpansionCase()) {
11756 this._consumeExpansionCaseEnd();
11757 return true;
11758 }
11759 if (this._isInExpansionForm()) {
11760 this._consumeExpansionFormEnd();
11761 return true;
11762 }
11763 }
11764 return false;
11765 }
11766 _beginToken(type, start = this._cursor.clone()) {
11767 this._currentTokenStart = start;
11768 this._currentTokenType = type;
11769 }
11770 _endToken(parts, end) {
11771 if (this._currentTokenStart === null) {
11772 throw new TokenError('Programming error - attempted to end a token when there was no start to the token', this._currentTokenType, this._cursor.getSpan(end));
11773 }
11774 if (this._currentTokenType === null) {
11775 throw new TokenError('Programming error - attempted to end a token which has no token type', null, this._cursor.getSpan(this._currentTokenStart));
11776 }
11777 const token = new Token(this._currentTokenType, parts, this._cursor.getSpan(this._currentTokenStart, this._leadingTriviaCodePoints));
11778 this.tokens.push(token);
11779 this._currentTokenStart = null;
11780 this._currentTokenType = null;
11781 return token;
11782 }
11783 _createError(msg, span) {
11784 if (this._isInExpansionForm()) {
11785 msg += ` (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)`;
11786 }
11787 const error = new TokenError(msg, this._currentTokenType, span);
11788 this._currentTokenStart = null;
11789 this._currentTokenType = null;
11790 return new _ControlFlowError(error);
11791 }
11792 handleError(e) {
11793 if (e instanceof CursorError) {
11794 e = this._createError(e.msg, this._cursor.getSpan(e.cursor));
11795 }
11796 if (e instanceof _ControlFlowError) {
11797 this.errors.push(e.error);
11798 }
11799 else {
11800 throw e;
11801 }
11802 }
11803 _attemptCharCode(charCode) {
11804 if (this._cursor.peek() === charCode) {
11805 this._cursor.advance();
11806 return true;
11807 }
11808 return false;
11809 }
11810 _attemptCharCodeCaseInsensitive(charCode) {
11811 if (compareCharCodeCaseInsensitive(this._cursor.peek(), charCode)) {
11812 this._cursor.advance();
11813 return true;
11814 }
11815 return false;
11816 }
11817 _requireCharCode(charCode) {
11818 const location = this._cursor.clone();
11819 if (!this._attemptCharCode(charCode)) {
11820 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
11821 }
11822 }
11823 _attemptStr(chars) {
11824 const len = chars.length;
11825 if (this._cursor.charsLeft() < len) {
11826 return false;
11827 }
11828 const initialPosition = this._cursor.clone();
11829 for (let i = 0; i < len; i++) {
11830 if (!this._attemptCharCode(chars.charCodeAt(i))) {
11831 // If attempting to parse the string fails, we want to reset the parser
11832 // to where it was before the attempt
11833 this._cursor = initialPosition;
11834 return false;
11835 }
11836 }
11837 return true;
11838 }
11839 _attemptStrCaseInsensitive(chars) {
11840 for (let i = 0; i < chars.length; i++) {
11841 if (!this._attemptCharCodeCaseInsensitive(chars.charCodeAt(i))) {
11842 return false;
11843 }
11844 }
11845 return true;
11846 }
11847 _requireStr(chars) {
11848 const location = this._cursor.clone();
11849 if (!this._attemptStr(chars)) {
11850 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
11851 }
11852 }
11853 _attemptCharCodeUntilFn(predicate) {
11854 while (!predicate(this._cursor.peek())) {
11855 this._cursor.advance();
11856 }
11857 }
11858 _requireCharCodeUntilFn(predicate, len) {
11859 const start = this._cursor.clone();
11860 this._attemptCharCodeUntilFn(predicate);
11861 if (this._cursor.diff(start) < len) {
11862 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
11863 }
11864 }
11865 _attemptUntilChar(char) {
11866 while (this._cursor.peek() !== char) {
11867 this._cursor.advance();
11868 }
11869 }
11870 _readChar(decodeEntities) {
11871 if (decodeEntities && this._cursor.peek() === $AMPERSAND) {
11872 return this._decodeEntity();
11873 }
11874 else {
11875 // Don't rely upon reading directly from `_input` as the actual char value
11876 // may have been generated from an escape sequence.
11877 const char = String.fromCodePoint(this._cursor.peek());
11878 this._cursor.advance();
11879 return char;
11880 }
11881 }
11882 _decodeEntity() {
11883 const start = this._cursor.clone();
11884 this._cursor.advance();
11885 if (this._attemptCharCode($HASH)) {
11886 const isHex = this._attemptCharCode($x) || this._attemptCharCode($X);
11887 const codeStart = this._cursor.clone();
11888 this._attemptCharCodeUntilFn(isDigitEntityEnd);
11889 if (this._cursor.peek() != $SEMICOLON) {
11890 // Advance cursor to include the peeked character in the string provided to the error
11891 // message.
11892 this._cursor.advance();
11893 const entityType = isHex ? CharacterReferenceType.HEX : CharacterReferenceType.DEC;
11894 throw this._createError(_unparsableEntityErrorMsg(entityType, this._cursor.getChars(start)), this._cursor.getSpan());
11895 }
11896 const strNum = this._cursor.getChars(codeStart);
11897 this._cursor.advance();
11898 try {
11899 const charCode = parseInt(strNum, isHex ? 16 : 10);
11900 return String.fromCharCode(charCode);
11901 }
11902 catch (_a) {
11903 throw this._createError(_unknownEntityErrorMsg(this._cursor.getChars(start)), this._cursor.getSpan());
11904 }
11905 }
11906 else {
11907 const nameStart = this._cursor.clone();
11908 this._attemptCharCodeUntilFn(isNamedEntityEnd);
11909 if (this._cursor.peek() != $SEMICOLON) {
11910 this._cursor = nameStart;
11911 return '&';
11912 }
11913 const name = this._cursor.getChars(nameStart);
11914 this._cursor.advance();
11915 const char = NAMED_ENTITIES[name];
11916 if (!char) {
11917 throw this._createError(_unknownEntityErrorMsg(name), this._cursor.getSpan(start));
11918 }
11919 return char;
11920 }
11921 }
11922 _consumeRawText(decodeEntities, endMarkerPredicate) {
11923 this._beginToken(decodeEntities ? TokenType.ESCAPABLE_RAW_TEXT : TokenType.RAW_TEXT);
11924 const parts = [];
11925 while (true) {
11926 const tagCloseStart = this._cursor.clone();
11927 const foundEndMarker = endMarkerPredicate();
11928 this._cursor = tagCloseStart;
11929 if (foundEndMarker) {
11930 break;
11931 }
11932 parts.push(this._readChar(decodeEntities));
11933 }
11934 return this._endToken([this._processCarriageReturns(parts.join(''))]);
11935 }
11936 _consumeComment(start) {
11937 this._beginToken(TokenType.COMMENT_START, start);
11938 this._requireCharCode($MINUS);
11939 this._endToken([]);
11940 this._consumeRawText(false, () => this._attemptStr('-->'));
11941 this._beginToken(TokenType.COMMENT_END);
11942 this._requireStr('-->');
11943 this._endToken([]);
11944 }
11945 _consumeCdata(start) {
11946 this._beginToken(TokenType.CDATA_START, start);
11947 this._requireStr('CDATA[');
11948 this._endToken([]);
11949 this._consumeRawText(false, () => this._attemptStr(']]>'));
11950 this._beginToken(TokenType.CDATA_END);
11951 this._requireStr(']]>');
11952 this._endToken([]);
11953 }
11954 _consumeDocType(start) {
11955 this._beginToken(TokenType.DOC_TYPE, start);
11956 const contentStart = this._cursor.clone();
11957 this._attemptUntilChar($GT);
11958 const content = this._cursor.getChars(contentStart);
11959 this._cursor.advance();
11960 this._endToken([content]);
11961 }
11962 _consumePrefixAndName() {
11963 const nameOrPrefixStart = this._cursor.clone();
11964 let prefix = '';
11965 while (this._cursor.peek() !== $COLON && !isPrefixEnd(this._cursor.peek())) {
11966 this._cursor.advance();
11967 }
11968 let nameStart;
11969 if (this._cursor.peek() === $COLON) {
11970 prefix = this._cursor.getChars(nameOrPrefixStart);
11971 this._cursor.advance();
11972 nameStart = this._cursor.clone();
11973 }
11974 else {
11975 nameStart = nameOrPrefixStart;
11976 }
11977 this._requireCharCodeUntilFn(isNameEnd, prefix === '' ? 0 : 1);
11978 const name = this._cursor.getChars(nameStart);
11979 return [prefix, name];
11980 }
11981 _consumeTagOpen(start) {
11982 let tagName;
11983 let prefix;
11984 let openTagToken;
11985 try {
11986 if (!isAsciiLetter(this._cursor.peek())) {
11987 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
11988 }
11989 openTagToken = this._consumeTagOpenStart(start);
11990 prefix = openTagToken.parts[0];
11991 tagName = openTagToken.parts[1];
11992 this._attemptCharCodeUntilFn(isNotWhitespace);
11993 while (this._cursor.peek() !== $SLASH && this._cursor.peek() !== $GT &&
11994 this._cursor.peek() !== $LT && this._cursor.peek() !== $EOF) {
11995 this._consumeAttributeName();
11996 this._attemptCharCodeUntilFn(isNotWhitespace);
11997 if (this._attemptCharCode($EQ)) {
11998 this._attemptCharCodeUntilFn(isNotWhitespace);
11999 this._consumeAttributeValue();
12000 }
12001 this._attemptCharCodeUntilFn(isNotWhitespace);
12002 }
12003 this._consumeTagOpenEnd();
12004 }
12005 catch (e) {
12006 if (e instanceof _ControlFlowError) {
12007 if (openTagToken) {
12008 // We errored before we could close the opening tag, so it is incomplete.
12009 openTagToken.type = TokenType.INCOMPLETE_TAG_OPEN;
12010 }
12011 else {
12012 // When the start tag is invalid, assume we want a "<" as text.
12013 // Back to back text tokens are merged at the end.
12014 this._beginToken(TokenType.TEXT, start);
12015 this._endToken(['<']);
12016 }
12017 return;
12018 }
12019 throw e;
12020 }
12021 const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix);
12022 if (contentTokenType === TagContentType.RAW_TEXT) {
12023 this._consumeRawTextWithTagClose(prefix, tagName, false);
12024 }
12025 else if (contentTokenType === TagContentType.ESCAPABLE_RAW_TEXT) {
12026 this._consumeRawTextWithTagClose(prefix, tagName, true);
12027 }
12028 }
12029 _consumeRawTextWithTagClose(prefix, tagName, decodeEntities) {
12030 this._consumeRawText(decodeEntities, () => {
12031 if (!this._attemptCharCode($LT))
12032 return false;
12033 if (!this._attemptCharCode($SLASH))
12034 return false;
12035 this._attemptCharCodeUntilFn(isNotWhitespace);
12036 if (!this._attemptStrCaseInsensitive(tagName))
12037 return false;
12038 this._attemptCharCodeUntilFn(isNotWhitespace);
12039 return this._attemptCharCode($GT);
12040 });
12041 this._beginToken(TokenType.TAG_CLOSE);
12042 this._requireCharCodeUntilFn(code => code === $GT, 3);
12043 this._cursor.advance(); // Consume the `>`
12044 this._endToken([prefix, tagName]);
12045 }
12046 _consumeTagOpenStart(start) {
12047 this._beginToken(TokenType.TAG_OPEN_START, start);
12048 const parts = this._consumePrefixAndName();
12049 return this._endToken(parts);
12050 }
12051 _consumeAttributeName() {
12052 const attrNameStart = this._cursor.peek();
12053 if (attrNameStart === $SQ || attrNameStart === $DQ) {
12054 throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());
12055 }
12056 this._beginToken(TokenType.ATTR_NAME);
12057 const prefixAndName = this._consumePrefixAndName();
12058 this._endToken(prefixAndName);
12059 }
12060 _consumeAttributeValue() {
12061 let value;
12062 if (this._cursor.peek() === $SQ || this._cursor.peek() === $DQ) {
12063 this._beginToken(TokenType.ATTR_QUOTE);
12064 const quoteChar = this._cursor.peek();
12065 this._cursor.advance();
12066 this._endToken([String.fromCodePoint(quoteChar)]);
12067 this._beginToken(TokenType.ATTR_VALUE);
12068 const parts = [];
12069 while (this._cursor.peek() !== quoteChar) {
12070 parts.push(this._readChar(true));
12071 }
12072 value = parts.join('');
12073 this._endToken([this._processCarriageReturns(value)]);
12074 this._beginToken(TokenType.ATTR_QUOTE);
12075 this._cursor.advance();
12076 this._endToken([String.fromCodePoint(quoteChar)]);
12077 }
12078 else {
12079 this._beginToken(TokenType.ATTR_VALUE);
12080 const valueStart = this._cursor.clone();
12081 this._requireCharCodeUntilFn(isNameEnd, 1);
12082 value = this._cursor.getChars(valueStart);
12083 this._endToken([this._processCarriageReturns(value)]);
12084 }
12085 }
12086 _consumeTagOpenEnd() {
12087 const tokenType = this._attemptCharCode($SLASH) ? TokenType.TAG_OPEN_END_VOID : TokenType.TAG_OPEN_END;
12088 this._beginToken(tokenType);
12089 this._requireCharCode($GT);
12090 this._endToken([]);
12091 }
12092 _consumeTagClose(start) {
12093 this._beginToken(TokenType.TAG_CLOSE, start);
12094 this._attemptCharCodeUntilFn(isNotWhitespace);
12095 const prefixAndName = this._consumePrefixAndName();
12096 this._attemptCharCodeUntilFn(isNotWhitespace);
12097 this._requireCharCode($GT);
12098 this._endToken(prefixAndName);
12099 }
12100 _consumeExpansionFormStart() {
12101 this._beginToken(TokenType.EXPANSION_FORM_START);
12102 this._requireCharCode($LBRACE);
12103 this._endToken([]);
12104 this._expansionCaseStack.push(TokenType.EXPANSION_FORM_START);
12105 this._beginToken(TokenType.RAW_TEXT);
12106 const condition = this._readUntil($COMMA);
12107 const normalizedCondition = this._processCarriageReturns(condition);
12108 if (this._i18nNormalizeLineEndingsInICUs) {
12109 // We explicitly want to normalize line endings for this text.
12110 this._endToken([normalizedCondition]);
12111 }
12112 else {
12113 // We are not normalizing line endings.
12114 const conditionToken = this._endToken([condition]);
12115 if (normalizedCondition !== condition) {
12116 this.nonNormalizedIcuExpressions.push(conditionToken);
12117 }
12118 }
12119 this._requireCharCode($COMMA);
12120 this._attemptCharCodeUntilFn(isNotWhitespace);
12121 this._beginToken(TokenType.RAW_TEXT);
12122 const type = this._readUntil($COMMA);
12123 this._endToken([type]);
12124 this._requireCharCode($COMMA);
12125 this._attemptCharCodeUntilFn(isNotWhitespace);
12126 }
12127 _consumeExpansionCaseStart() {
12128 this._beginToken(TokenType.EXPANSION_CASE_VALUE);
12129 const value = this._readUntil($LBRACE).trim();
12130 this._endToken([value]);
12131 this._attemptCharCodeUntilFn(isNotWhitespace);
12132 this._beginToken(TokenType.EXPANSION_CASE_EXP_START);
12133 this._requireCharCode($LBRACE);
12134 this._endToken([]);
12135 this._attemptCharCodeUntilFn(isNotWhitespace);
12136 this._expansionCaseStack.push(TokenType.EXPANSION_CASE_EXP_START);
12137 }
12138 _consumeExpansionCaseEnd() {
12139 this._beginToken(TokenType.EXPANSION_CASE_EXP_END);
12140 this._requireCharCode($RBRACE);
12141 this._endToken([]);
12142 this._attemptCharCodeUntilFn(isNotWhitespace);
12143 this._expansionCaseStack.pop();
12144 }
12145 _consumeExpansionFormEnd() {
12146 this._beginToken(TokenType.EXPANSION_FORM_END);
12147 this._requireCharCode($RBRACE);
12148 this._endToken([]);
12149 this._expansionCaseStack.pop();
12150 }
12151 _consumeText() {
12152 const start = this._cursor.clone();
12153 this._beginToken(TokenType.TEXT, start);
12154 const parts = [];
12155 do {
12156 if (this._interpolationConfig && this._attemptStr(this._interpolationConfig.start)) {
12157 parts.push(this._interpolationConfig.start);
12158 this._inInterpolation = true;
12159 }
12160 else if (this._interpolationConfig && this._inInterpolation &&
12161 this._attemptStr(this._interpolationConfig.end)) {
12162 parts.push(this._interpolationConfig.end);
12163 this._inInterpolation = false;
12164 }
12165 else {
12166 parts.push(this._readChar(true));
12167 }
12168 } while (!this._isTextEnd());
12169 this._endToken([this._processCarriageReturns(parts.join(''))]);
12170 }
12171 _isTextEnd() {
12172 if (this._cursor.peek() === $LT || this._cursor.peek() === $EOF) {
12173 return true;
12174 }
12175 if (this._tokenizeIcu && !this._inInterpolation) {
12176 if (this.isExpansionFormStart()) {
12177 // start of an expansion form
12178 return true;
12179 }
12180 if (this._cursor.peek() === $RBRACE && this._isInExpansionCase()) {
12181 // end of and expansion case
12182 return true;
12183 }
12184 }
12185 return false;
12186 }
12187 _readUntil(char) {
12188 const start = this._cursor.clone();
12189 this._attemptUntilChar(char);
12190 return this._cursor.getChars(start);
12191 }
12192 _isInExpansionCase() {
12193 return this._expansionCaseStack.length > 0 &&
12194 this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
12195 TokenType.EXPANSION_CASE_EXP_START;
12196 }
12197 _isInExpansionForm() {
12198 return this._expansionCaseStack.length > 0 &&
12199 this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
12200 TokenType.EXPANSION_FORM_START;
12201 }
12202 isExpansionFormStart() {
12203 if (this._cursor.peek() !== $LBRACE) {
12204 return false;
12205 }
12206 if (this._interpolationConfig) {
12207 const start = this._cursor.clone();
12208 const isInterpolation = this._attemptStr(this._interpolationConfig.start);
12209 this._cursor = start;
12210 return !isInterpolation;
12211 }
12212 return true;
12213 }
12214 }
12215 function isNotWhitespace(code) {
12216 return !isWhitespace(code) || code === $EOF;
12217 }
12218 function isNameEnd(code) {
12219 return isWhitespace(code) || code === $GT || code === $LT ||
12220 code === $SLASH || code === $SQ || code === $DQ || code === $EQ ||
12221 code === $EOF;
12222 }
12223 function isPrefixEnd(code) {
12224 return (code < $a || $z < code) && (code < $A || $Z < code) &&
12225 (code < $0 || code > $9);
12226 }
12227 function isDigitEntityEnd(code) {
12228 return code == $SEMICOLON || code == $EOF || !isAsciiHexDigit(code);
12229 }
12230 function isNamedEntityEnd(code) {
12231 return code == $SEMICOLON || code == $EOF || !isAsciiLetter(code);
12232 }
12233 function isExpansionCaseStart(peek) {
12234 return peek !== $RBRACE;
12235 }
12236 function compareCharCodeCaseInsensitive(code1, code2) {
12237 return toUpperCaseCharCode(code1) == toUpperCaseCharCode(code2);
12238 }
12239 function toUpperCaseCharCode(code) {
12240 return code >= $a && code <= $z ? code - $a + $A : code;
12241 }
12242 function mergeTextTokens(srcTokens) {
12243 const dstTokens = [];
12244 let lastDstToken = undefined;
12245 for (let i = 0; i < srcTokens.length; i++) {
12246 const token = srcTokens[i];
12247 if (lastDstToken && lastDstToken.type == TokenType.TEXT && token.type == TokenType.TEXT) {
12248 lastDstToken.parts[0] += token.parts[0];
12249 lastDstToken.sourceSpan.end = token.sourceSpan.end;
12250 }
12251 else {
12252 lastDstToken = token;
12253 dstTokens.push(lastDstToken);
12254 }
12255 }
12256 return dstTokens;
12257 }
12258 class PlainCharacterCursor {
12259 constructor(fileOrCursor, range) {
12260 if (fileOrCursor instanceof PlainCharacterCursor) {
12261 this.file = fileOrCursor.file;
12262 this.input = fileOrCursor.input;
12263 this.end = fileOrCursor.end;
12264 const state = fileOrCursor.state;
12265 // Note: avoid using `{...fileOrCursor.state}` here as that has a severe performance penalty.
12266 // In ES5 bundles the object spread operator is translated into the `__assign` helper, which
12267 // is not optimized by VMs as efficiently as a raw object literal. Since this constructor is
12268 // called in tight loops, this difference matters.
12269 this.state = {
12270 peek: state.peek,
12271 offset: state.offset,
12272 line: state.line,
12273 column: state.column,
12274 };
12275 }
12276 else {
12277 if (!range) {
12278 throw new Error('Programming error: the range argument must be provided with a file argument.');
12279 }
12280 this.file = fileOrCursor;
12281 this.input = fileOrCursor.content;
12282 this.end = range.endPos;
12283 this.state = {
12284 peek: -1,
12285 offset: range.startPos,
12286 line: range.startLine,
12287 column: range.startCol,
12288 };
12289 }
12290 }
12291 clone() {
12292 return new PlainCharacterCursor(this);
12293 }
12294 peek() {
12295 return this.state.peek;
12296 }
12297 charsLeft() {
12298 return this.end - this.state.offset;
12299 }
12300 diff(other) {
12301 return this.state.offset - other.state.offset;
12302 }
12303 advance() {
12304 this.advanceState(this.state);
12305 }
12306 init() {
12307 this.updatePeek(this.state);
12308 }
12309 getSpan(start, leadingTriviaCodePoints) {
12310 start = start || this;
12311 let fullStart = start;
12312 if (leadingTriviaCodePoints) {
12313 while (this.diff(start) > 0 && leadingTriviaCodePoints.indexOf(start.peek()) !== -1) {
12314 if (fullStart === start) {
12315 start = start.clone();
12316 }
12317 start.advance();
12318 }
12319 }
12320 const startLocation = this.locationFromCursor(start);
12321 const endLocation = this.locationFromCursor(this);
12322 const fullStartLocation = fullStart !== start ? this.locationFromCursor(fullStart) : startLocation;
12323 return new ParseSourceSpan(startLocation, endLocation, fullStartLocation);
12324 }
12325 getChars(start) {
12326 return this.input.substring(start.state.offset, this.state.offset);
12327 }
12328 charAt(pos) {
12329 return this.input.charCodeAt(pos);
12330 }
12331 advanceState(state) {
12332 if (state.offset >= this.end) {
12333 this.state = state;
12334 throw new CursorError('Unexpected character "EOF"', this);
12335 }
12336 const currentChar = this.charAt(state.offset);
12337 if (currentChar === $LF) {
12338 state.line++;
12339 state.column = 0;
12340 }
12341 else if (!isNewLine(currentChar)) {
12342 state.column++;
12343 }
12344 state.offset++;
12345 this.updatePeek(state);
12346 }
12347 updatePeek(state) {
12348 state.peek = state.offset >= this.end ? $EOF : this.charAt(state.offset);
12349 }
12350 locationFromCursor(cursor) {
12351 return new ParseLocation(cursor.file, cursor.state.offset, cursor.state.line, cursor.state.column);
12352 }
12353 }
12354 class EscapedCharacterCursor extends PlainCharacterCursor {
12355 constructor(fileOrCursor, range) {
12356 if (fileOrCursor instanceof EscapedCharacterCursor) {
12357 super(fileOrCursor);
12358 this.internalState = Object.assign({}, fileOrCursor.internalState);
12359 }
12360 else {
12361 super(fileOrCursor, range);
12362 this.internalState = this.state;
12363 }
12364 }
12365 advance() {
12366 this.state = this.internalState;
12367 super.advance();
12368 this.processEscapeSequence();
12369 }
12370 init() {
12371 super.init();
12372 this.processEscapeSequence();
12373 }
12374 clone() {
12375 return new EscapedCharacterCursor(this);
12376 }
12377 getChars(start) {
12378 const cursor = start.clone();
12379 let chars = '';
12380 while (cursor.internalState.offset < this.internalState.offset) {
12381 chars += String.fromCodePoint(cursor.peek());
12382 cursor.advance();
12383 }
12384 return chars;
12385 }
12386 /**
12387 * Process the escape sequence that starts at the current position in the text.
12388 *
12389 * This method is called to ensure that `peek` has the unescaped value of escape sequences.
12390 */
12391 processEscapeSequence() {
12392 const peek = () => this.internalState.peek;
12393 if (peek() === $BACKSLASH) {
12394 // We have hit an escape sequence so we need the internal state to become independent
12395 // of the external state.
12396 this.internalState = Object.assign({}, this.state);
12397 // Move past the backslash
12398 this.advanceState(this.internalState);
12399 // First check for standard control char sequences
12400 if (peek() === $n) {
12401 this.state.peek = $LF;
12402 }
12403 else if (peek() === $r) {
12404 this.state.peek = $CR;
12405 }
12406 else if (peek() === $v) {
12407 this.state.peek = $VTAB;
12408 }
12409 else if (peek() === $t) {
12410 this.state.peek = $TAB;
12411 }
12412 else if (peek() === $b) {
12413 this.state.peek = $BSPACE;
12414 }
12415 else if (peek() === $f) {
12416 this.state.peek = $FF;
12417 }
12418 // Now consider more complex sequences
12419 else if (peek() === $u) {
12420 // Unicode code-point sequence
12421 this.advanceState(this.internalState); // advance past the `u` char
12422 if (peek() === $LBRACE) {
12423 // Variable length Unicode, e.g. `\x{123}`
12424 this.advanceState(this.internalState); // advance past the `{` char
12425 // Advance past the variable number of hex digits until we hit a `}` char
12426 const digitStart = this.clone();
12427 let length = 0;
12428 while (peek() !== $RBRACE) {
12429 this.advanceState(this.internalState);
12430 length++;
12431 }
12432 this.state.peek = this.decodeHexDigits(digitStart, length);
12433 }
12434 else {
12435 // Fixed length Unicode, e.g. `\u1234`
12436 const digitStart = this.clone();
12437 this.advanceState(this.internalState);
12438 this.advanceState(this.internalState);
12439 this.advanceState(this.internalState);
12440 this.state.peek = this.decodeHexDigits(digitStart, 4);
12441 }
12442 }
12443 else if (peek() === $x) {
12444 // Hex char code, e.g. `\x2F`
12445 this.advanceState(this.internalState); // advance past the `x` char
12446 const digitStart = this.clone();
12447 this.advanceState(this.internalState);
12448 this.state.peek = this.decodeHexDigits(digitStart, 2);
12449 }
12450 else if (isOctalDigit(peek())) {
12451 // Octal char code, e.g. `\012`,
12452 let octal = '';
12453 let length = 0;
12454 let previous = this.clone();
12455 while (isOctalDigit(peek()) && length < 3) {
12456 previous = this.clone();
12457 octal += String.fromCodePoint(peek());
12458 this.advanceState(this.internalState);
12459 length++;
12460 }
12461 this.state.peek = parseInt(octal, 8);
12462 // Backup one char
12463 this.internalState = previous.internalState;
12464 }
12465 else if (isNewLine(this.internalState.peek)) {
12466 // Line continuation `\` followed by a new line
12467 this.advanceState(this.internalState); // advance over the newline
12468 this.state = this.internalState;
12469 }
12470 else {
12471 // If none of the `if` blocks were executed then we just have an escaped normal character.
12472 // In that case we just, effectively, skip the backslash from the character.
12473 this.state.peek = this.internalState.peek;
12474 }
12475 }
12476 }
12477 decodeHexDigits(start, length) {
12478 const hex = this.input.substr(start.internalState.offset, length);
12479 const charCode = parseInt(hex, 16);
12480 if (!isNaN(charCode)) {
12481 return charCode;
12482 }
12483 else {
12484 start.state = start.internalState;
12485 throw new CursorError('Invalid hexadecimal escape sequence', start);
12486 }
12487 }
12488 }
12489 class CursorError {
12490 constructor(msg, cursor) {
12491 this.msg = msg;
12492 this.cursor = cursor;
12493 }
12494 }
12495
12496 /**
12497 * @license
12498 * Copyright Google LLC All Rights Reserved.
12499 *
12500 * Use of this source code is governed by an MIT-style license that can be
12501 * found in the LICENSE file at https://angular.io/license
12502 */
12503 class TreeError extends ParseError {
12504 constructor(elementName, span, msg) {
12505 super(span, msg);
12506 this.elementName = elementName;
12507 }
12508 static create(elementName, span, msg) {
12509 return new TreeError(elementName, span, msg);
12510 }
12511 }
12512 class ParseTreeResult {
12513 constructor(rootNodes, errors) {
12514 this.rootNodes = rootNodes;
12515 this.errors = errors;
12516 }
12517 }
12518 class Parser {
12519 constructor(getTagDefinition) {
12520 this.getTagDefinition = getTagDefinition;
12521 }
12522 parse(source, url, options) {
12523 const tokenizeResult = tokenize(source, url, this.getTagDefinition, options);
12524 const parser = new _TreeBuilder(tokenizeResult.tokens, this.getTagDefinition);
12525 parser.build();
12526 return new ParseTreeResult(parser.rootNodes, tokenizeResult.errors.concat(parser.errors));
12527 }
12528 }
12529 class _TreeBuilder {
12530 constructor(tokens, getTagDefinition) {
12531 this.tokens = tokens;
12532 this.getTagDefinition = getTagDefinition;
12533 this._index = -1;
12534 this._elementStack = [];
12535 this.rootNodes = [];
12536 this.errors = [];
12537 this._advance();
12538 }
12539 build() {
12540 while (this._peek.type !== TokenType.EOF) {
12541 if (this._peek.type === TokenType.TAG_OPEN_START ||
12542 this._peek.type === TokenType.INCOMPLETE_TAG_OPEN) {
12543 this._consumeStartTag(this._advance());
12544 }
12545 else if (this._peek.type === TokenType.TAG_CLOSE) {
12546 this._consumeEndTag(this._advance());
12547 }
12548 else if (this._peek.type === TokenType.CDATA_START) {
12549 this._closeVoidElement();
12550 this._consumeCdata(this._advance());
12551 }
12552 else if (this._peek.type === TokenType.COMMENT_START) {
12553 this._closeVoidElement();
12554 this._consumeComment(this._advance());
12555 }
12556 else if (this._peek.type === TokenType.TEXT || this._peek.type === TokenType.RAW_TEXT ||
12557 this._peek.type === TokenType.ESCAPABLE_RAW_TEXT) {
12558 this._closeVoidElement();
12559 this._consumeText(this._advance());
12560 }
12561 else if (this._peek.type === TokenType.EXPANSION_FORM_START) {
12562 this._consumeExpansion(this._advance());
12563 }
12564 else {
12565 // Skip all other tokens...
12566 this._advance();
12567 }
12568 }
12569 }
12570 _advance() {
12571 const prev = this._peek;
12572 if (this._index < this.tokens.length - 1) {
12573 // Note: there is always an EOF token at the end
12574 this._index++;
12575 }
12576 this._peek = this.tokens[this._index];
12577 return prev;
12578 }
12579 _advanceIf(type) {
12580 if (this._peek.type === type) {
12581 return this._advance();
12582 }
12583 return null;
12584 }
12585 _consumeCdata(_startToken) {
12586 this._consumeText(this._advance());
12587 this._advanceIf(TokenType.CDATA_END);
12588 }
12589 _consumeComment(token) {
12590 const text = this._advanceIf(TokenType.RAW_TEXT);
12591 this._advanceIf(TokenType.COMMENT_END);
12592 const value = text != null ? text.parts[0].trim() : null;
12593 this._addToParent(new Comment(value, token.sourceSpan));
12594 }
12595 _consumeExpansion(token) {
12596 const switchValue = this._advance();
12597 const type = this._advance();
12598 const cases = [];
12599 // read =
12600 while (this._peek.type === TokenType.EXPANSION_CASE_VALUE) {
12601 const expCase = this._parseExpansionCase();
12602 if (!expCase)
12603 return; // error
12604 cases.push(expCase);
12605 }
12606 // read the final }
12607 if (this._peek.type !== TokenType.EXPANSION_FORM_END) {
12608 this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '}'.`));
12609 return;
12610 }
12611 const sourceSpan = new ParseSourceSpan(token.sourceSpan.start, this._peek.sourceSpan.end, token.sourceSpan.fullStart);
12612 this._addToParent(new Expansion(switchValue.parts[0], type.parts[0], cases, sourceSpan, switchValue.sourceSpan));
12613 this._advance();
12614 }
12615 _parseExpansionCase() {
12616 const value = this._advance();
12617 // read {
12618 if (this._peek.type !== TokenType.EXPANSION_CASE_EXP_START) {
12619 this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '{'.`));
12620 return null;
12621 }
12622 // read until }
12623 const start = this._advance();
12624 const exp = this._collectExpansionExpTokens(start);
12625 if (!exp)
12626 return null;
12627 const end = this._advance();
12628 exp.push(new Token(TokenType.EOF, [], end.sourceSpan));
12629 // parse everything in between { and }
12630 const expansionCaseParser = new _TreeBuilder(exp, this.getTagDefinition);
12631 expansionCaseParser.build();
12632 if (expansionCaseParser.errors.length > 0) {
12633 this.errors = this.errors.concat(expansionCaseParser.errors);
12634 return null;
12635 }
12636 const sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end, value.sourceSpan.fullStart);
12637 const expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end, start.sourceSpan.fullStart);
12638 return new ExpansionCase(value.parts[0], expansionCaseParser.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan);
12639 }
12640 _collectExpansionExpTokens(start) {
12641 const exp = [];
12642 const expansionFormStack = [TokenType.EXPANSION_CASE_EXP_START];
12643 while (true) {
12644 if (this._peek.type === TokenType.EXPANSION_FORM_START ||
12645 this._peek.type === TokenType.EXPANSION_CASE_EXP_START) {
12646 expansionFormStack.push(this._peek.type);
12647 }
12648 if (this._peek.type === TokenType.EXPANSION_CASE_EXP_END) {
12649 if (lastOnStack(expansionFormStack, TokenType.EXPANSION_CASE_EXP_START)) {
12650 expansionFormStack.pop();
12651 if (expansionFormStack.length == 0)
12652 return exp;
12653 }
12654 else {
12655 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
12656 return null;
12657 }
12658 }
12659 if (this._peek.type === TokenType.EXPANSION_FORM_END) {
12660 if (lastOnStack(expansionFormStack, TokenType.EXPANSION_FORM_START)) {
12661 expansionFormStack.pop();
12662 }
12663 else {
12664 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
12665 return null;
12666 }
12667 }
12668 if (this._peek.type === TokenType.EOF) {
12669 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
12670 return null;
12671 }
12672 exp.push(this._advance());
12673 }
12674 }
12675 _consumeText(token) {
12676 let text = token.parts[0];
12677 if (text.length > 0 && text[0] == '\n') {
12678 const parent = this._getParentElement();
12679 if (parent != null && parent.children.length == 0 &&
12680 this.getTagDefinition(parent.name).ignoreFirstLf) {
12681 text = text.substring(1);
12682 }
12683 }
12684 if (text.length > 0) {
12685 this._addToParent(new Text$2(text, token.sourceSpan));
12686 }
12687 }
12688 _closeVoidElement() {
12689 const el = this._getParentElement();
12690 if (el && this.getTagDefinition(el.name).isVoid) {
12691 this._elementStack.pop();
12692 }
12693 }
12694 _consumeStartTag(startTagToken) {
12695 const [prefix, name] = startTagToken.parts;
12696 const attrs = [];
12697 while (this._peek.type === TokenType.ATTR_NAME) {
12698 attrs.push(this._consumeAttr(this._advance()));
12699 }
12700 const fullName = this._getElementFullName(prefix, name, this._getParentElement());
12701 let selfClosing = false;
12702 // Note: There could have been a tokenizer error
12703 // so that we don't get a token for the end tag...
12704 if (this._peek.type === TokenType.TAG_OPEN_END_VOID) {
12705 this._advance();
12706 selfClosing = true;
12707 const tagDef = this.getTagDefinition(fullName);
12708 if (!(tagDef.canSelfClose || getNsPrefix(fullName) !== null || tagDef.isVoid)) {
12709 this.errors.push(TreeError.create(fullName, startTagToken.sourceSpan, `Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`));
12710 }
12711 }
12712 else if (this._peek.type === TokenType.TAG_OPEN_END) {
12713 this._advance();
12714 selfClosing = false;
12715 }
12716 const end = this._peek.sourceSpan.fullStart;
12717 const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
12718 // Create a separate `startSpan` because `span` will be modified when there is an `end` span.
12719 const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
12720 const el = new Element$1(fullName, attrs, [], span, startSpan, undefined);
12721 this._pushElement(el);
12722 if (selfClosing) {
12723 // Elements that are self-closed have their `endSourceSpan` set to the full span, as the
12724 // element start tag also represents the end tag.
12725 this._popElement(fullName, span);
12726 }
12727 else if (startTagToken.type === TokenType.INCOMPLETE_TAG_OPEN) {
12728 // We already know the opening tag is not complete, so it is unlikely it has a corresponding
12729 // close tag. Let's optimistically parse it as a full element and emit an error.
12730 this._popElement(fullName, null);
12731 this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
12732 }
12733 }
12734 _pushElement(el) {
12735 const parentEl = this._getParentElement();
12736 if (parentEl && this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) {
12737 this._elementStack.pop();
12738 }
12739 this._addToParent(el);
12740 this._elementStack.push(el);
12741 }
12742 _consumeEndTag(endTagToken) {
12743 const fullName = this._getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
12744 if (this.getTagDefinition(fullName).isVoid) {
12745 this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`));
12746 }
12747 else if (!this._popElement(fullName, endTagToken.sourceSpan)) {
12748 const errMsg = `Unexpected closing tag "${fullName}". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags`;
12749 this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg));
12750 }
12751 }
12752 /**
12753 * Closes the nearest element with the tag name `fullName` in the parse tree.
12754 * `endSourceSpan` is the span of the closing tag, or null if the element does
12755 * not have a closing tag (for example, this happens when an incomplete
12756 * opening tag is recovered).
12757 */
12758 _popElement(fullName, endSourceSpan) {
12759 for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) {
12760 const el = this._elementStack[stackIndex];
12761 if (el.name == fullName) {
12762 // Record the parse span with the element that is being closed. Any elements that are
12763 // removed from the element stack at this point are closed implicitly, so they won't get
12764 // an end source span (as there is no explicit closing element).
12765 el.endSourceSpan = endSourceSpan;
12766 el.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : el.sourceSpan.end;
12767 this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex);
12768 return true;
12769 }
12770 if (!this.getTagDefinition(el.name).closedByParent) {
12771 return false;
12772 }
12773 }
12774 return false;
12775 }
12776 _consumeAttr(attrName) {
12777 const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
12778 let end = attrName.sourceSpan.end;
12779 let value = '';
12780 let valueSpan = undefined;
12781 if (this._peek.type === TokenType.ATTR_QUOTE) {
12782 this._advance();
12783 }
12784 if (this._peek.type === TokenType.ATTR_VALUE) {
12785 const valueToken = this._advance();
12786 value = valueToken.parts[0];
12787 end = valueToken.sourceSpan.end;
12788 valueSpan = valueToken.sourceSpan;
12789 }
12790 if (this._peek.type === TokenType.ATTR_QUOTE) {
12791 const quoteToken = this._advance();
12792 end = quoteToken.sourceSpan.end;
12793 }
12794 const keySpan = new ParseSourceSpan(attrName.sourceSpan.start, attrName.sourceSpan.end);
12795 return new Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, end, attrName.sourceSpan.fullStart), keySpan, valueSpan);
12796 }
12797 _getParentElement() {
12798 return this._elementStack.length > 0 ? this._elementStack[this._elementStack.length - 1] : null;
12799 }
12800 _addToParent(node) {
12801 const parent = this._getParentElement();
12802 if (parent != null) {
12803 parent.children.push(node);
12804 }
12805 else {
12806 this.rootNodes.push(node);
12807 }
12808 }
12809 _getElementFullName(prefix, localName, parentElement) {
12810 if (prefix === '') {
12811 prefix = this.getTagDefinition(localName).implicitNamespacePrefix || '';
12812 if (prefix === '' && parentElement != null) {
12813 const parentTagName = splitNsName(parentElement.name)[1];
12814 const parentTagDefinition = this.getTagDefinition(parentTagName);
12815 if (!parentTagDefinition.preventNamespaceInheritance) {
12816 prefix = getNsPrefix(parentElement.name);
12817 }
12818 }
12819 }
12820 return mergeNsAndName(prefix, localName);
12821 }
12822 }
12823 function lastOnStack(stack, element) {
12824 return stack.length > 0 && stack[stack.length - 1] === element;
12825 }
12826
12827 /**
12828 * @license
12829 * Copyright Google LLC All Rights Reserved.
12830 *
12831 * Use of this source code is governed by an MIT-style license that can be
12832 * found in the LICENSE file at https://angular.io/license
12833 */
12834 class HtmlParser extends Parser {
12835 constructor() {
12836 super(getHtmlTagDefinition);
12837 }
12838 parse(source, url, options) {
12839 return super.parse(source, url, options);
12840 }
12841 }
12842
12843 /**
12844 * @license
12845 * Copyright Google LLC All Rights Reserved.
12846 *
12847 * Use of this source code is governed by an MIT-style license that can be
12848 * found in the LICENSE file at https://angular.io/license
12849 */
12850 const PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces';
12851 const SKIP_WS_TRIM_TAGS = new Set(['pre', 'template', 'textarea', 'script', 'style']);
12852 // Equivalent to \s with \u00a0 (non-breaking space) excluded.
12853 // Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
12854 const WS_CHARS = ' \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff';
12855 const NO_WS_REGEXP = new RegExp(`[^${WS_CHARS}]`);
12856 const WS_REPLACE_REGEXP = new RegExp(`[${WS_CHARS}]{2,}`, 'g');
12857 function hasPreserveWhitespacesAttr(attrs) {
12858 return attrs.some((attr) => attr.name === PRESERVE_WS_ATTR_NAME);
12859 }
12860 /**
12861 * Angular Dart introduced &ngsp; as a placeholder for non-removable space, see:
12862 * https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32
12863 * In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character
12864 * and later on replaced by a space. We are re-implementing the same idea here.
12865 */
12866 function replaceNgsp(value) {
12867 // lexer is replacing the &ngsp; pseudo-entity with NGSP_UNICODE
12868 return value.replace(new RegExp(NGSP_UNICODE, 'g'), ' ');
12869 }
12870 /**
12871 * This visitor can walk HTML parse tree and remove / trim text nodes using the following rules:
12872 * - consider spaces, tabs and new lines as whitespace characters;
12873 * - drop text nodes consisting of whitespace characters only;
12874 * - for all other text nodes replace consecutive whitespace characters with one space;
12875 * - convert &ngsp; pseudo-entity to a single space;
12876 *
12877 * Removal and trimming of whitespaces have positive performance impact (less code to generate
12878 * while compiling templates, faster view creation). At the same time it can be "destructive"
12879 * in some cases (whitespaces can influence layout). Because of the potential of breaking layout
12880 * this visitor is not activated by default in Angular 5 and people need to explicitly opt-in for
12881 * whitespace removal. The default option for whitespace removal will be revisited in Angular 6
12882 * and might be changed to "on" by default.
12883 */
12884 class WhitespaceVisitor {
12885 visitElement(element, context) {
12886 if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
12887 // don't descent into elements where we need to preserve whitespaces
12888 // but still visit all attributes to eliminate one used as a market to preserve WS
12889 return new Element$1(element.name, visitAll$1(this, element.attrs), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
12890 }
12891 return new Element$1(element.name, element.attrs, visitAllWithSiblings(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
12892 }
12893 visitAttribute(attribute, context) {
12894 return attribute.name !== PRESERVE_WS_ATTR_NAME ? attribute : null;
12895 }
12896 visitText(text, context) {
12897 const isNotBlank = text.value.match(NO_WS_REGEXP);
12898 const hasExpansionSibling = context &&
12899 (context.prev instanceof Expansion || context.next instanceof Expansion);
12900 if (isNotBlank || hasExpansionSibling) {
12901 return new Text$2(replaceNgsp(text.value).replace(WS_REPLACE_REGEXP, ' '), text.sourceSpan, text.i18n);
12902 }
12903 return null;
12904 }
12905 visitComment(comment, context) {
12906 return comment;
12907 }
12908 visitExpansion(expansion, context) {
12909 return expansion;
12910 }
12911 visitExpansionCase(expansionCase, context) {
12912 return expansionCase;
12913 }
12914 }
12915 function visitAllWithSiblings(visitor, nodes) {
12916 const result = [];
12917 nodes.forEach((ast, i) => {
12918 const context = { prev: nodes[i - 1], next: nodes[i + 1] };
12919 const astResult = ast.visit(visitor, context);
12920 if (astResult) {
12921 result.push(astResult);
12922 }
12923 });
12924 return result;
12925 }
12926
12927 /**
12928 * @license
12929 * Copyright Google LLC All Rights Reserved.
12930 *
12931 * Use of this source code is governed by an MIT-style license that can be
12932 * found in the LICENSE file at https://angular.io/license
12933 */
12934 var ProviderAstType;
12935 (function (ProviderAstType) {
12936 ProviderAstType[ProviderAstType["PublicService"] = 0] = "PublicService";
12937 ProviderAstType[ProviderAstType["PrivateService"] = 1] = "PrivateService";
12938 ProviderAstType[ProviderAstType["Component"] = 2] = "Component";
12939 ProviderAstType[ProviderAstType["Directive"] = 3] = "Directive";
12940 ProviderAstType[ProviderAstType["Builtin"] = 4] = "Builtin";
12941 })(ProviderAstType || (ProviderAstType = {}));
12942
12943 /**
12944 * @license
12945 * Copyright Google LLC All Rights Reserved.
12946 *
12947 * Use of this source code is governed by an MIT-style license that can be
12948 * found in the LICENSE file at https://angular.io/license
12949 */
12950 function isStyleUrlResolvable(url) {
12951 if (url == null || url.length === 0 || url[0] == '/')
12952 return false;
12953 const schemeMatch = url.match(URL_WITH_SCHEMA_REGEXP);
12954 return schemeMatch === null || schemeMatch[1] == 'package' || schemeMatch[1] == 'asset';
12955 }
12956 const URL_WITH_SCHEMA_REGEXP = /^([^:/?#]+):/;
12957
12958 /**
12959 * @license
12960 * Copyright Google LLC All Rights Reserved.
12961 *
12962 * Use of this source code is governed by an MIT-style license that can be
12963 * found in the LICENSE file at https://angular.io/license
12964 */
12965 const PROPERTY_PARTS_SEPARATOR = '.';
12966 const ATTRIBUTE_PREFIX = 'attr';
12967 const CLASS_PREFIX = 'class';
12968 const STYLE_PREFIX = 'style';
12969 const TEMPLATE_ATTR_PREFIX = '*';
12970 const ANIMATE_PROP_PREFIX = 'animate-';
12971 /**
12972 * Parses bindings in templates and in the directive host area.
12973 */
12974 class BindingParser {
12975 constructor(_exprParser, _interpolationConfig, _schemaRegistry, pipes, errors) {
12976 this._exprParser = _exprParser;
12977 this._interpolationConfig = _interpolationConfig;
12978 this._schemaRegistry = _schemaRegistry;
12979 this.errors = errors;
12980 this.pipesByName = null;
12981 this._usedPipes = new Map();
12982 // When the `pipes` parameter is `null`, do not check for used pipes
12983 // This is used in IVY when we might not know the available pipes at compile time
12984 if (pipes) {
12985 const pipesByName = new Map();
12986 pipes.forEach(pipe => pipesByName.set(pipe.name, pipe));
12987 this.pipesByName = pipesByName;
12988 }
12989 }
12990 get interpolationConfig() {
12991 return this._interpolationConfig;
12992 }
12993 getUsedPipes() {
12994 return Array.from(this._usedPipes.values());
12995 }
12996 createBoundHostProperties(dirMeta, sourceSpan) {
12997 if (dirMeta.hostProperties) {
12998 const boundProps = [];
12999 Object.keys(dirMeta.hostProperties).forEach(propName => {
13000 const expression = dirMeta.hostProperties[propName];
13001 if (typeof expression === 'string') {
13002 this.parsePropertyBinding(propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [],
13003 // Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the
13004 // sourceSpan, as it represents the sourceSpan of the host itself rather than the
13005 // source of the host binding (which doesn't exist in the template). Regardless,
13006 // neither of these values are used in Ivy but are only here to satisfy the function
13007 // signature. This should likely be refactored in the future so that `sourceSpan`
13008 // isn't being used inaccurately.
13009 boundProps, sourceSpan);
13010 }
13011 else {
13012 this._reportError(`Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
13013 }
13014 });
13015 return boundProps;
13016 }
13017 return null;
13018 }
13019 createDirectiveHostPropertyAsts(dirMeta, elementSelector, sourceSpan) {
13020 const boundProps = this.createBoundHostProperties(dirMeta, sourceSpan);
13021 return boundProps &&
13022 boundProps.map((prop) => this.createBoundElementProperty(elementSelector, prop));
13023 }
13024 createDirectiveHostEventAsts(dirMeta, sourceSpan) {
13025 if (dirMeta.hostListeners) {
13026 const targetEvents = [];
13027 Object.keys(dirMeta.hostListeners).forEach(propName => {
13028 const expression = dirMeta.hostListeners[propName];
13029 if (typeof expression === 'string') {
13030 // Use the `sourceSpan` for `keySpan` and `handlerSpan`. This isn't really accurate, but
13031 // neither is the `sourceSpan`, as it represents the `sourceSpan` of the host itself
13032 // rather than the source of the host binding (which doesn't exist in the template).
13033 // Regardless, neither of these values are used in Ivy but are only here to satisfy the
13034 // function signature. This should likely be refactored in the future so that `sourceSpan`
13035 // isn't being used inaccurately.
13036 this.parseEvent(propName, expression, sourceSpan, sourceSpan, [], targetEvents, sourceSpan);
13037 }
13038 else {
13039 this._reportError(`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
13040 }
13041 });
13042 return targetEvents;
13043 }
13044 return null;
13045 }
13046 parseInterpolation(value, sourceSpan) {
13047 const sourceInfo = sourceSpan.start.toString();
13048 const absoluteOffset = sourceSpan.fullStart.offset;
13049 try {
13050 const ast = this._exprParser.parseInterpolation(value, sourceInfo, absoluteOffset, this._interpolationConfig);
13051 if (ast)
13052 this._reportExpressionParserErrors(ast.errors, sourceSpan);
13053 this._checkPipes(ast, sourceSpan);
13054 return ast;
13055 }
13056 catch (e) {
13057 this._reportError(`${e}`, sourceSpan);
13058 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
13059 }
13060 }
13061 /**
13062 * Similar to `parseInterpolation`, but treats the provided string as a single expression
13063 * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).
13064 * This is used for parsing the switch expression in ICUs.
13065 */
13066 parseInterpolationExpression(expression, sourceSpan) {
13067 const sourceInfo = sourceSpan.start.toString();
13068 const absoluteOffset = sourceSpan.start.offset;
13069 try {
13070 const ast = this._exprParser.parseInterpolationExpression(expression, sourceInfo, absoluteOffset);
13071 if (ast)
13072 this._reportExpressionParserErrors(ast.errors, sourceSpan);
13073 this._checkPipes(ast, sourceSpan);
13074 return ast;
13075 }
13076 catch (e) {
13077 this._reportError(`${e}`, sourceSpan);
13078 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
13079 }
13080 }
13081 /**
13082 * Parses the bindings in a microsyntax expression, and converts them to
13083 * `ParsedProperty` or `ParsedVariable`.
13084 *
13085 * @param tplKey template binding name
13086 * @param tplValue template binding value
13087 * @param sourceSpan span of template binding relative to entire the template
13088 * @param absoluteValueOffset start of the tplValue relative to the entire template
13089 * @param targetMatchableAttrs potential attributes to match in the template
13090 * @param targetProps target property bindings in the template
13091 * @param targetVars target variables in the template
13092 */
13093 parseInlineTemplateBinding(tplKey, tplValue, sourceSpan, absoluteValueOffset, targetMatchableAttrs, targetProps, targetVars, isIvyAst) {
13094 const absoluteKeyOffset = sourceSpan.start.offset + TEMPLATE_ATTR_PREFIX.length;
13095 const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset);
13096 for (const binding of bindings) {
13097 // sourceSpan is for the entire HTML attribute. bindingSpan is for a particular
13098 // binding within the microsyntax expression so it's more narrow than sourceSpan.
13099 const bindingSpan = moveParseSourceSpan(sourceSpan, binding.sourceSpan);
13100 const key = binding.key.source;
13101 const keySpan = moveParseSourceSpan(sourceSpan, binding.key.span);
13102 if (binding instanceof VariableBinding) {
13103 const value = binding.value ? binding.value.source : '$implicit';
13104 const valueSpan = binding.value ? moveParseSourceSpan(sourceSpan, binding.value.span) : undefined;
13105 targetVars.push(new ParsedVariable(key, value, bindingSpan, keySpan, valueSpan));
13106 }
13107 else if (binding.value) {
13108 const srcSpan = isIvyAst ? bindingSpan : sourceSpan;
13109 const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);
13110 this._parsePropertyAst(key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
13111 }
13112 else {
13113 targetMatchableAttrs.push([key, '' /* value */]);
13114 // Since this is a literal attribute with no RHS, source span should be
13115 // just the key span.
13116 this.parseLiteralAttr(key, null /* value */, keySpan, absoluteValueOffset, undefined /* valueSpan */, targetMatchableAttrs, targetProps, keySpan);
13117 }
13118 }
13119 }
13120 /**
13121 * Parses the bindings in a microsyntax expression, e.g.
13122 * ```
13123 * <tag *tplKey="let value1 = prop; let value2 = localVar">
13124 * ```
13125 *
13126 * @param tplKey template binding name
13127 * @param tplValue template binding value
13128 * @param sourceSpan span of template binding relative to entire the template
13129 * @param absoluteKeyOffset start of the `tplKey`
13130 * @param absoluteValueOffset start of the `tplValue`
13131 */
13132 _parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset) {
13133 const sourceInfo = sourceSpan.start.toString();
13134 try {
13135 const bindingsResult = this._exprParser.parseTemplateBindings(tplKey, tplValue, sourceInfo, absoluteKeyOffset, absoluteValueOffset);
13136 this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
13137 bindingsResult.templateBindings.forEach((binding) => {
13138 if (binding.value instanceof ASTWithSource) {
13139 this._checkPipes(binding.value, sourceSpan);
13140 }
13141 });
13142 bindingsResult.warnings.forEach((warning) => {
13143 this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING);
13144 });
13145 return bindingsResult.templateBindings;
13146 }
13147 catch (e) {
13148 this._reportError(`${e}`, sourceSpan);
13149 return [];
13150 }
13151 }
13152 parseLiteralAttr(name, value, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs,
13153 // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
13154 // have to change This should be required when VE is removed.
13155 targetProps, keySpan) {
13156 if (isAnimationLabel(name)) {
13157 name = name.substring(1);
13158 if (keySpan !== undefined) {
13159 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
13160 }
13161 if (value) {
13162 this._reportError(`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
13163 ` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`, sourceSpan, ParseErrorLevel.ERROR);
13164 }
13165 this._parseAnimation(name, value, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
13166 }
13167 else {
13168 targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));
13169 }
13170 }
13171 parsePropertyBinding(name, expression, isHost, sourceSpan, absoluteOffset, valueSpan,
13172 // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
13173 // have to change This should be required when VE is removed.
13174 targetMatchableAttrs, targetProps, keySpan) {
13175 if (name.length === 0) {
13176 this._reportError(`Property name is missing in binding`, sourceSpan);
13177 }
13178 let isAnimationProp = false;
13179 if (name.startsWith(ANIMATE_PROP_PREFIX)) {
13180 isAnimationProp = true;
13181 name = name.substring(ANIMATE_PROP_PREFIX.length);
13182 if (keySpan !== undefined) {
13183 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + ANIMATE_PROP_PREFIX.length, keySpan.end.offset));
13184 }
13185 }
13186 else if (isAnimationLabel(name)) {
13187 isAnimationProp = true;
13188 name = name.substring(1);
13189 if (keySpan !== undefined) {
13190 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
13191 }
13192 }
13193 if (isAnimationProp) {
13194 this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
13195 }
13196 else {
13197 this._parsePropertyAst(name, this._parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
13198 }
13199 }
13200 parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs,
13201 // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
13202 // have to change This should be required when VE is removed.
13203 targetProps, keySpan) {
13204 const expr = this.parseInterpolation(value, valueSpan || sourceSpan);
13205 if (expr) {
13206 this._parsePropertyAst(name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
13207 return true;
13208 }
13209 return false;
13210 }
13211 _parsePropertyAst(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
13212 targetMatchableAttrs.push([name, ast.source]);
13213 targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
13214 }
13215 _parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
13216 if (name.length === 0) {
13217 this._reportError('Animation trigger is missing', sourceSpan);
13218 }
13219 // This will occur when a @trigger is not paired with an expression.
13220 // For animations it is valid to not have an expression since */void
13221 // states will be applied by angular when the element is attached/detached
13222 const ast = this._parseBinding(expression || 'undefined', false, valueSpan || sourceSpan, absoluteOffset);
13223 targetMatchableAttrs.push([name, ast.source]);
13224 targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan, keySpan, valueSpan));
13225 }
13226 _parseBinding(value, isHostBinding, sourceSpan, absoluteOffset) {
13227 const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown)').toString();
13228 try {
13229 const ast = isHostBinding ?
13230 this._exprParser.parseSimpleBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig) :
13231 this._exprParser.parseBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig);
13232 if (ast)
13233 this._reportExpressionParserErrors(ast.errors, sourceSpan);
13234 this._checkPipes(ast, sourceSpan);
13235 return ast;
13236 }
13237 catch (e) {
13238 this._reportError(`${e}`, sourceSpan);
13239 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
13240 }
13241 }
13242 createBoundElementProperty(elementSelector, boundProp, skipValidation = false, mapPropertyName = true) {
13243 if (boundProp.isAnimation) {
13244 return new BoundElementProperty(boundProp.name, 4 /* Animation */, SecurityContext.NONE, boundProp.expression, null, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
13245 }
13246 let unit = null;
13247 let bindingType = undefined;
13248 let boundPropertyName = null;
13249 const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR);
13250 let securityContexts = undefined;
13251 // Check for special cases (prefix style, attr, class)
13252 if (parts.length > 1) {
13253 if (parts[0] == ATTRIBUTE_PREFIX) {
13254 boundPropertyName = parts.slice(1).join(PROPERTY_PARTS_SEPARATOR);
13255 if (!skipValidation) {
13256 this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, true);
13257 }
13258 securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, boundPropertyName, true);
13259 const nsSeparatorIdx = boundPropertyName.indexOf(':');
13260 if (nsSeparatorIdx > -1) {
13261 const ns = boundPropertyName.substring(0, nsSeparatorIdx);
13262 const name = boundPropertyName.substring(nsSeparatorIdx + 1);
13263 boundPropertyName = mergeNsAndName(ns, name);
13264 }
13265 bindingType = 1 /* Attribute */;
13266 }
13267 else if (parts[0] == CLASS_PREFIX) {
13268 boundPropertyName = parts[1];
13269 bindingType = 2 /* Class */;
13270 securityContexts = [SecurityContext.NONE];
13271 }
13272 else if (parts[0] == STYLE_PREFIX) {
13273 unit = parts.length > 2 ? parts[2] : null;
13274 boundPropertyName = parts[1];
13275 bindingType = 3 /* Style */;
13276 securityContexts = [SecurityContext.STYLE];
13277 }
13278 }
13279 // If not a special case, use the full property name
13280 if (boundPropertyName === null) {
13281 const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);
13282 boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;
13283 securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);
13284 bindingType = 0 /* Property */;
13285 if (!skipValidation) {
13286 this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);
13287 }
13288 }
13289 return new BoundElementProperty(boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
13290 }
13291 // TODO: keySpan should be required but was made optional to avoid changing VE parser.
13292 parseEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
13293 if (name.length === 0) {
13294 this._reportError(`Event name is missing in binding`, sourceSpan);
13295 }
13296 if (isAnimationLabel(name)) {
13297 name = name.substr(1);
13298 if (keySpan !== undefined) {
13299 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
13300 }
13301 this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
13302 }
13303 else {
13304 this._parseRegularEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);
13305 }
13306 }
13307 calcPossibleSecurityContexts(selector, propName, isAttribute) {
13308 const prop = this._schemaRegistry.getMappedPropName(propName);
13309 return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);
13310 }
13311 _parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) {
13312 const matches = splitAtPeriod(name, [name, '']);
13313 const eventName = matches[0];
13314 const phase = matches[1].toLowerCase();
13315 const ast = this._parseAction(expression, handlerSpan);
13316 targetEvents.push(new ParsedEvent(eventName, phase, 1 /* Animation */, ast, sourceSpan, handlerSpan, keySpan));
13317 if (eventName.length === 0) {
13318 this._reportError(`Animation event name is missing in binding`, sourceSpan);
13319 }
13320 if (phase) {
13321 if (phase !== 'start' && phase !== 'done') {
13322 this._reportError(`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, sourceSpan);
13323 }
13324 }
13325 else {
13326 this._reportError(`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, sourceSpan);
13327 }
13328 }
13329 _parseRegularEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
13330 // long format: 'target: eventName'
13331 const [target, eventName] = splitAtColon(name, [null, name]);
13332 const ast = this._parseAction(expression, handlerSpan);
13333 targetMatchableAttrs.push([name, ast.source]);
13334 targetEvents.push(new ParsedEvent(eventName, target, 0 /* Regular */, ast, sourceSpan, handlerSpan, keySpan));
13335 // Don't detect directives for event names for now,
13336 // so don't add the event name to the matchableAttrs
13337 }
13338 _parseAction(value, sourceSpan) {
13339 const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString();
13340 const absoluteOffset = (sourceSpan && sourceSpan.start) ? sourceSpan.start.offset : 0;
13341 try {
13342 const ast = this._exprParser.parseAction(value, sourceInfo, absoluteOffset, this._interpolationConfig);
13343 if (ast) {
13344 this._reportExpressionParserErrors(ast.errors, sourceSpan);
13345 }
13346 if (!ast || ast.ast instanceof EmptyExpr) {
13347 this._reportError(`Empty expressions are not allowed`, sourceSpan);
13348 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
13349 }
13350 this._checkPipes(ast, sourceSpan);
13351 return ast;
13352 }
13353 catch (e) {
13354 this._reportError(`${e}`, sourceSpan);
13355 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
13356 }
13357 }
13358 _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
13359 this.errors.push(new ParseError(sourceSpan, message, level));
13360 }
13361 _reportExpressionParserErrors(errors, sourceSpan) {
13362 for (const error of errors) {
13363 this._reportError(error.message, sourceSpan);
13364 }
13365 }
13366 // Make sure all the used pipes are known in `this.pipesByName`
13367 _checkPipes(ast, sourceSpan) {
13368 if (ast && this.pipesByName) {
13369 const collector = new PipeCollector();
13370 ast.visit(collector);
13371 collector.pipes.forEach((ast, pipeName) => {
13372 const pipeMeta = this.pipesByName.get(pipeName);
13373 if (!pipeMeta) {
13374 this._reportError(`The pipe '${pipeName}' could not be found`, new ParseSourceSpan(sourceSpan.start.moveBy(ast.span.start), sourceSpan.start.moveBy(ast.span.end)));
13375 }
13376 else {
13377 this._usedPipes.set(pipeName, pipeMeta);
13378 }
13379 });
13380 }
13381 }
13382 /**
13383 * @param propName the name of the property / attribute
13384 * @param sourceSpan
13385 * @param isAttr true when binding to an attribute
13386 */
13387 _validatePropertyOrAttributeName(propName, sourceSpan, isAttr) {
13388 const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
13389 this._schemaRegistry.validateProperty(propName);
13390 if (report.error) {
13391 this._reportError(report.msg, sourceSpan, ParseErrorLevel.ERROR);
13392 }
13393 }
13394 }
13395 class PipeCollector extends RecursiveAstVisitor {
13396 constructor() {
13397 super(...arguments);
13398 this.pipes = new Map();
13399 }
13400 visitPipe(ast, context) {
13401 this.pipes.set(ast.name, ast);
13402 ast.exp.visit(this);
13403 this.visitAll(ast.args, context);
13404 return null;
13405 }
13406 }
13407 function isAnimationLabel(name) {
13408 return name[0] == '@';
13409 }
13410 function calcPossibleSecurityContexts(registry, selector, propName, isAttribute) {
13411 const ctxs = [];
13412 CssSelector.parse(selector).forEach((selector) => {
13413 const elementNames = selector.element ? [selector.element] : registry.allKnownElementNames();
13414 const notElementNames = new Set(selector.notSelectors.filter(selector => selector.isElementSelector())
13415 .map((selector) => selector.element));
13416 const possibleElementNames = elementNames.filter(elementName => !notElementNames.has(elementName));
13417 ctxs.push(...possibleElementNames.map(elementName => registry.securityContext(elementName, propName, isAttribute)));
13418 });
13419 return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();
13420 }
13421 /**
13422 * Compute a new ParseSourceSpan based off an original `sourceSpan` by using
13423 * absolute offsets from the specified `absoluteSpan`.
13424 *
13425 * @param sourceSpan original source span
13426 * @param absoluteSpan absolute source span to move to
13427 */
13428 function moveParseSourceSpan(sourceSpan, absoluteSpan) {
13429 // The difference of two absolute offsets provide the relative offset
13430 const startDiff = absoluteSpan.start - sourceSpan.start.offset;
13431 const endDiff = absoluteSpan.end - sourceSpan.end.offset;
13432 return new ParseSourceSpan(sourceSpan.start.moveBy(startDiff), sourceSpan.end.moveBy(endDiff), sourceSpan.fullStart.moveBy(startDiff), sourceSpan.details);
13433 }
13434
13435 /**
13436 * @license
13437 * Copyright Google LLC All Rights Reserved.
13438 *
13439 * Use of this source code is governed by an MIT-style license that can be
13440 * found in the LICENSE file at https://angular.io/license
13441 */
13442 const NG_CONTENT_SELECT_ATTR = 'select';
13443 const LINK_ELEMENT = 'link';
13444 const LINK_STYLE_REL_ATTR = 'rel';
13445 const LINK_STYLE_HREF_ATTR = 'href';
13446 const LINK_STYLE_REL_VALUE = 'stylesheet';
13447 const STYLE_ELEMENT = 'style';
13448 const SCRIPT_ELEMENT = 'script';
13449 const NG_NON_BINDABLE_ATTR = 'ngNonBindable';
13450 const NG_PROJECT_AS = 'ngProjectAs';
13451 function preparseElement(ast) {
13452 let selectAttr = null;
13453 let hrefAttr = null;
13454 let relAttr = null;
13455 let nonBindable = false;
13456 let projectAs = '';
13457 ast.attrs.forEach(attr => {
13458 const lcAttrName = attr.name.toLowerCase();
13459 if (lcAttrName == NG_CONTENT_SELECT_ATTR) {
13460 selectAttr = attr.value;
13461 }
13462 else if (lcAttrName == LINK_STYLE_HREF_ATTR) {
13463 hrefAttr = attr.value;
13464 }
13465 else if (lcAttrName == LINK_STYLE_REL_ATTR) {
13466 relAttr = attr.value;
13467 }
13468 else if (attr.name == NG_NON_BINDABLE_ATTR) {
13469 nonBindable = true;
13470 }
13471 else if (attr.name == NG_PROJECT_AS) {
13472 if (attr.value.length > 0) {
13473 projectAs = attr.value;
13474 }
13475 }
13476 });
13477 selectAttr = normalizeNgContentSelect(selectAttr);
13478 const nodeName = ast.name.toLowerCase();
13479 let type = PreparsedElementType.OTHER;
13480 if (isNgContent(nodeName)) {
13481 type = PreparsedElementType.NG_CONTENT;
13482 }
13483 else if (nodeName == STYLE_ELEMENT) {
13484 type = PreparsedElementType.STYLE;
13485 }
13486 else if (nodeName == SCRIPT_ELEMENT) {
13487 type = PreparsedElementType.SCRIPT;
13488 }
13489 else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
13490 type = PreparsedElementType.STYLESHEET;
13491 }
13492 return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable, projectAs);
13493 }
13494 var PreparsedElementType;
13495 (function (PreparsedElementType) {
13496 PreparsedElementType[PreparsedElementType["NG_CONTENT"] = 0] = "NG_CONTENT";
13497 PreparsedElementType[PreparsedElementType["STYLE"] = 1] = "STYLE";
13498 PreparsedElementType[PreparsedElementType["STYLESHEET"] = 2] = "STYLESHEET";
13499 PreparsedElementType[PreparsedElementType["SCRIPT"] = 3] = "SCRIPT";
13500 PreparsedElementType[PreparsedElementType["OTHER"] = 4] = "OTHER";
13501 })(PreparsedElementType || (PreparsedElementType = {}));
13502 class PreparsedElement {
13503 constructor(type, selectAttr, hrefAttr, nonBindable, projectAs) {
13504 this.type = type;
13505 this.selectAttr = selectAttr;
13506 this.hrefAttr = hrefAttr;
13507 this.nonBindable = nonBindable;
13508 this.projectAs = projectAs;
13509 }
13510 }
13511 function normalizeNgContentSelect(selectAttr) {
13512 if (selectAttr === null || selectAttr.length === 0) {
13513 return '*';
13514 }
13515 return selectAttr;
13516 }
13517
13518 /**
13519 * @license
13520 * Copyright Google LLC All Rights Reserved.
13521 *
13522 * Use of this source code is governed by an MIT-style license that can be
13523 * found in the LICENSE file at https://angular.io/license
13524 */
13525 function isEmptyExpression(ast) {
13526 if (ast instanceof ASTWithSource) {
13527 ast = ast.ast;
13528 }
13529 return ast instanceof EmptyExpr;
13530 }
13531
13532 /**
13533 * @license
13534 * Copyright Google LLC All Rights Reserved.
13535 *
13536 * Use of this source code is governed by an MIT-style license that can be
13537 * found in the LICENSE file at https://angular.io/license
13538 */
13539 /**
13540 * Parses string representation of a style and converts it into object literal.
13541 *
13542 * @param value string representation of style as used in the `style` attribute in HTML.
13543 * Example: `color: red; height: auto`.
13544 * @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
13545 * 'auto']`
13546 */
13547 function parse(value) {
13548 // we use a string array here instead of a string map
13549 // because a string-map is not guaranteed to retain the
13550 // order of the entries whereas a string array can be
13551 // constructed in a [key, value, key, value] format.
13552 const styles = [];
13553 let i = 0;
13554 let parenDepth = 0;
13555 let quote = 0 /* QuoteNone */;
13556 let valueStart = 0;
13557 let propStart = 0;
13558 let currentProp = null;
13559 let valueHasQuotes = false;
13560 while (i < value.length) {
13561 const token = value.charCodeAt(i++);
13562 switch (token) {
13563 case 40 /* OpenParen */:
13564 parenDepth++;
13565 break;
13566 case 41 /* CloseParen */:
13567 parenDepth--;
13568 break;
13569 case 39 /* QuoteSingle */:
13570 // valueStart needs to be there since prop values don't
13571 // have quotes in CSS
13572 valueHasQuotes = valueHasQuotes || valueStart > 0;
13573 if (quote === 0 /* QuoteNone */) {
13574 quote = 39 /* QuoteSingle */;
13575 }
13576 else if (quote === 39 /* QuoteSingle */ && value.charCodeAt(i - 1) !== 92 /* BackSlash */) {
13577 quote = 0 /* QuoteNone */;
13578 }
13579 break;
13580 case 34 /* QuoteDouble */:
13581 // same logic as above
13582 valueHasQuotes = valueHasQuotes || valueStart > 0;
13583 if (quote === 0 /* QuoteNone */) {
13584 quote = 34 /* QuoteDouble */;
13585 }
13586 else if (quote === 34 /* QuoteDouble */ && value.charCodeAt(i - 1) !== 92 /* BackSlash */) {
13587 quote = 0 /* QuoteNone */;
13588 }
13589 break;
13590 case 58 /* Colon */:
13591 if (!currentProp && parenDepth === 0 && quote === 0 /* QuoteNone */) {
13592 currentProp = hyphenate(value.substring(propStart, i - 1).trim());
13593 valueStart = i;
13594 }
13595 break;
13596 case 59 /* Semicolon */:
13597 if (currentProp && valueStart > 0 && parenDepth === 0 && quote === 0 /* QuoteNone */) {
13598 const styleVal = value.substring(valueStart, i - 1).trim();
13599 styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
13600 propStart = i;
13601 valueStart = 0;
13602 currentProp = null;
13603 valueHasQuotes = false;
13604 }
13605 break;
13606 }
13607 }
13608 if (currentProp && valueStart) {
13609 const styleVal = value.substr(valueStart).trim();
13610 styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
13611 }
13612 return styles;
13613 }
13614 function stripUnnecessaryQuotes(value) {
13615 const qS = value.charCodeAt(0);
13616 const qE = value.charCodeAt(value.length - 1);
13617 if (qS == qE && (qS == 39 /* QuoteSingle */ || qS == 34 /* QuoteDouble */)) {
13618 const tempValue = value.substring(1, value.length - 1);
13619 // special case to avoid using a multi-quoted string that was just chomped
13620 // (e.g. `font-family: "Verdana", "sans-serif"`)
13621 if (tempValue.indexOf('\'') == -1 && tempValue.indexOf('"') == -1) {
13622 value = tempValue;
13623 }
13624 }
13625 return value;
13626 }
13627 function hyphenate(value) {
13628 return value
13629 .replace(/[a-z][A-Z]/g, v => {
13630 return v.charAt(0) + '-' + v.charAt(1);
13631 })
13632 .toLowerCase();
13633 }
13634
13635 const IMPORTANT_FLAG = '!important';
13636 /**
13637 * Minimum amount of binding slots required in the runtime for style/class bindings.
13638 *
13639 * Styling in Angular uses up two slots in the runtime LView/TData data structures to
13640 * record binding data, property information and metadata.
13641 *
13642 * When a binding is registered it will place the following information in the `LView`:
13643 *
13644 * slot 1) binding value
13645 * slot 2) cached value (all other values collected before it in string form)
13646 *
13647 * When a binding is registered it will place the following information in the `TData`:
13648 *
13649 * slot 1) prop name
13650 * slot 2) binding index that points to the previous style/class binding (and some extra config
13651 * values)
13652 *
13653 * Let's imagine we have a binding that looks like so:
13654 *
13655 * ```
13656 * <div [style.width]="x" [style.height]="y">
13657 * ```
13658 *
13659 * Our `LView` and `TData` data-structures look like so:
13660 *
13661 * ```typescript
13662 * LView = [
13663 * // ...
13664 * x, // value of x
13665 * "width: x",
13666 *
13667 * y, // value of y
13668 * "width: x; height: y",
13669 * // ...
13670 * ];
13671 *
13672 * TData = [
13673 * // ...
13674 * "width", // binding slot 20
13675 * 0,
13676 *
13677 * "height",
13678 * 20,
13679 * // ...
13680 * ];
13681 * ```
13682 *
13683 * */
13684 const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2;
13685 /**
13686 * Produces creation/update instructions for all styling bindings (class and style)
13687 *
13688 * It also produces the creation instruction to register all initial styling values
13689 * (which are all the static class="..." and style="..." attribute values that exist
13690 * on an element within a template).
13691 *
13692 * The builder class below handles producing instructions for the following cases:
13693 *
13694 * - Static style/class attributes (style="..." and class="...")
13695 * - Dynamic style/class map bindings ([style]="map" and [class]="map|string")
13696 * - Dynamic style/class property bindings ([style.prop]="exp" and [class.name]="exp")
13697 *
13698 * Due to the complex relationship of all of these cases, the instructions generated
13699 * for these attributes/properties/bindings must be done so in the correct order. The
13700 * order which these must be generated is as follows:
13701 *
13702 * if (createMode) {
13703 * styling(...)
13704 * }
13705 * if (updateMode) {
13706 * styleMap(...)
13707 * classMap(...)
13708 * styleProp(...)
13709 * classProp(...)
13710 * }
13711 *
13712 * The creation/update methods within the builder class produce these instructions.
13713 */
13714 class StylingBuilder {
13715 constructor(_directiveExpr) {
13716 this._directiveExpr = _directiveExpr;
13717 /** Whether or not there are any static styling values present */
13718 this._hasInitialValues = false;
13719 /**
13720 * Whether or not there are any styling bindings present
13721 * (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
13722 */
13723 this.hasBindings = false;
13724 this.hasBindingsWithPipes = false;
13725 /** the input for [class] (if it exists) */
13726 this._classMapInput = null;
13727 /** the input for [style] (if it exists) */
13728 this._styleMapInput = null;
13729 /** an array of each [style.prop] input */
13730 this._singleStyleInputs = null;
13731 /** an array of each [class.name] input */
13732 this._singleClassInputs = null;
13733 this._lastStylingInput = null;
13734 this._firstStylingInput = null;
13735 // maps are used instead of hash maps because a Map will
13736 // retain the ordering of the keys
13737 /**
13738 * Represents the location of each style binding in the template
13739 * (e.g. `<div [style.width]="w" [style.height]="h">` implies
13740 * that `width=0` and `height=1`)
13741 */
13742 this._stylesIndex = new Map();
13743 /**
13744 * Represents the location of each class binding in the template
13745 * (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
13746 * that `big=0` and `hidden=1`)
13747 */
13748 this._classesIndex = new Map();
13749 this._initialStyleValues = [];
13750 this._initialClassValues = [];
13751 }
13752 /**
13753 * Registers a given input to the styling builder to be later used when producing AOT code.
13754 *
13755 * The code below will only accept the input if it is somehow tied to styling (whether it be
13756 * style/class bindings or static style/class attributes).
13757 */
13758 registerBoundInput(input) {
13759 // [attr.style] or [attr.class] are skipped in the code below,
13760 // they should not be treated as styling-based bindings since
13761 // they are intended to be written directly to the attr and
13762 // will therefore skip all style/class resolution that is present
13763 // with style="", [style]="" and [style.prop]="", class="",
13764 // [class.prop]="". [class]="" assignments
13765 let binding = null;
13766 let name = input.name;
13767 switch (input.type) {
13768 case 0 /* Property */:
13769 binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan);
13770 break;
13771 case 3 /* Style */:
13772 binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit);
13773 break;
13774 case 2 /* Class */:
13775 binding = this.registerClassInput(name, false, input.value, input.sourceSpan);
13776 break;
13777 }
13778 return binding ? true : false;
13779 }
13780 registerInputBasedOnName(name, expression, sourceSpan) {
13781 let binding = null;
13782 const prefix = name.substring(0, 6);
13783 const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!';
13784 const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');
13785 if (isStyle || isClass) {
13786 const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no
13787 const property = name.substr(isMapBased ? 5 : 6); // the dot explains why there's a +1
13788 if (isStyle) {
13789 binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan);
13790 }
13791 else {
13792 binding = this.registerClassInput(property, isMapBased, expression, sourceSpan);
13793 }
13794 }
13795 return binding;
13796 }
13797 registerStyleInput(name, isMapBased, value, sourceSpan, suffix) {
13798 if (isEmptyExpression(value)) {
13799 return null;
13800 }
13801 name = normalizePropName(name);
13802 const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
13803 suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
13804 const entry = { name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag };
13805 if (isMapBased) {
13806 this._styleMapInput = entry;
13807 }
13808 else {
13809 (this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
13810 registerIntoMap(this._stylesIndex, property);
13811 }
13812 this._lastStylingInput = entry;
13813 this._firstStylingInput = this._firstStylingInput || entry;
13814 this._checkForPipes(value);
13815 this.hasBindings = true;
13816 return entry;
13817 }
13818 registerClassInput(name, isMapBased, value, sourceSpan) {
13819 if (isEmptyExpression(value)) {
13820 return null;
13821 }
13822 const { property, hasOverrideFlag } = parseProperty(name);
13823 const entry = { name: property, value, sourceSpan, hasOverrideFlag, suffix: null };
13824 if (isMapBased) {
13825 if (this._classMapInput) {
13826 throw new Error('[class] and [className] bindings cannot be used on the same element simultaneously');
13827 }
13828 this._classMapInput = entry;
13829 }
13830 else {
13831 (this._singleClassInputs = this._singleClassInputs || []).push(entry);
13832 registerIntoMap(this._classesIndex, property);
13833 }
13834 this._lastStylingInput = entry;
13835 this._firstStylingInput = this._firstStylingInput || entry;
13836 this._checkForPipes(value);
13837 this.hasBindings = true;
13838 return entry;
13839 }
13840 _checkForPipes(value) {
13841 if ((value instanceof ASTWithSource) && (value.ast instanceof BindingPipe)) {
13842 this.hasBindingsWithPipes = true;
13843 }
13844 }
13845 /**
13846 * Registers the element's static style string value to the builder.
13847 *
13848 * @param value the style string (e.g. `width:100px; height:200px;`)
13849 */
13850 registerStyleAttr(value) {
13851 this._initialStyleValues = parse(value);
13852 this._hasInitialValues = true;
13853 }
13854 /**
13855 * Registers the element's static class string value to the builder.
13856 *
13857 * @param value the className string (e.g. `disabled gold zoom`)
13858 */
13859 registerClassAttr(value) {
13860 this._initialClassValues = value.trim().split(/\s+/g);
13861 this._hasInitialValues = true;
13862 }
13863 /**
13864 * Appends all styling-related expressions to the provided attrs array.
13865 *
13866 * @param attrs an existing array where each of the styling expressions
13867 * will be inserted into.
13868 */
13869 populateInitialStylingAttrs(attrs) {
13870 // [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
13871 if (this._initialClassValues.length) {
13872 attrs.push(literal(1 /* Classes */));
13873 for (let i = 0; i < this._initialClassValues.length; i++) {
13874 attrs.push(literal(this._initialClassValues[i]));
13875 }
13876 }
13877 // [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]
13878 if (this._initialStyleValues.length) {
13879 attrs.push(literal(2 /* Styles */));
13880 for (let i = 0; i < this._initialStyleValues.length; i += 2) {
13881 attrs.push(literal(this._initialStyleValues[i]), literal(this._initialStyleValues[i + 1]));
13882 }
13883 }
13884 }
13885 /**
13886 * Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
13887 *
13888 * The instruction generation code below is used for producing the AOT statement code which is
13889 * responsible for registering initial styles (within a directive hostBindings' creation block),
13890 * as well as any of the provided attribute values, to the directive host element.
13891 */
13892 assignHostAttrs(attrs, definitionMap) {
13893 if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
13894 this.populateInitialStylingAttrs(attrs);
13895 definitionMap.set('hostAttrs', literalArr(attrs));
13896 }
13897 }
13898 /**
13899 * Builds an instruction with all the expressions and parameters for `classMap`.
13900 *
13901 * The instruction data will contain all expressions for `classMap` to function
13902 * which includes the `[class]` expression params.
13903 */
13904 buildClassMapInstruction(valueConverter) {
13905 if (this._classMapInput) {
13906 return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
13907 }
13908 return null;
13909 }
13910 /**
13911 * Builds an instruction with all the expressions and parameters for `styleMap`.
13912 *
13913 * The instruction data will contain all expressions for `styleMap` to function
13914 * which includes the `[style]` expression params.
13915 */
13916 buildStyleMapInstruction(valueConverter) {
13917 if (this._styleMapInput) {
13918 return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
13919 }
13920 return null;
13921 }
13922 _buildMapBasedInstruction(valueConverter, isClassBased, stylingInput) {
13923 // each styling binding value is stored in the LView
13924 // map-based bindings allocate two slots: one for the
13925 // previous binding value and another for the previous
13926 // className or style attribute value.
13927 let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
13928 // these values must be outside of the update block so that they can
13929 // be evaluated (the AST visit call) during creation time so that any
13930 // pipes can be picked up in time before the template is built
13931 const mapValue = stylingInput.value.visit(valueConverter);
13932 let reference;
13933 if (mapValue instanceof Interpolation) {
13934 totalBindingSlotsRequired += mapValue.expressions.length;
13935 reference = isClassBased ? getClassMapInterpolationExpression(mapValue) :
13936 getStyleMapInterpolationExpression(mapValue);
13937 }
13938 else {
13939 reference = isClassBased ? Identifiers$1.classMap : Identifiers$1.styleMap;
13940 }
13941 return {
13942 reference,
13943 calls: [{
13944 supportsInterpolation: true,
13945 sourceSpan: stylingInput.sourceSpan,
13946 allocateBindingSlots: totalBindingSlotsRequired,
13947 params: (convertFn) => {
13948 const convertResult = convertFn(mapValue);
13949 const params = Array.isArray(convertResult) ? convertResult : [convertResult];
13950 return params;
13951 }
13952 }]
13953 };
13954 }
13955 _buildSingleInputs(reference, inputs, valueConverter, getInterpolationExpressionFn, isClassBased) {
13956 const instructions = [];
13957 inputs.forEach(input => {
13958 const previousInstruction = instructions[instructions.length - 1];
13959 const value = input.value.visit(valueConverter);
13960 let referenceForCall = reference;
13961 // each styling binding value is stored in the LView
13962 // but there are two values stored for each binding:
13963 // 1) the value itself
13964 // 2) an intermediate value (concatenation of style up to this point).
13965 // We need to store the intermediate value so that we don't allocate
13966 // the strings on each CD.
13967 let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
13968 if (value instanceof Interpolation) {
13969 totalBindingSlotsRequired += value.expressions.length;
13970 if (getInterpolationExpressionFn) {
13971 referenceForCall = getInterpolationExpressionFn(value);
13972 }
13973 }
13974 const call = {
13975 sourceSpan: input.sourceSpan,
13976 allocateBindingSlots: totalBindingSlotsRequired,
13977 supportsInterpolation: !!getInterpolationExpressionFn,
13978 params: (convertFn) => {
13979 // params => stylingProp(propName, value, suffix)
13980 const params = [];
13981 params.push(literal(input.name));
13982 const convertResult = convertFn(value);
13983 if (Array.isArray(convertResult)) {
13984 params.push(...convertResult);
13985 }
13986 else {
13987 params.push(convertResult);
13988 }
13989 // [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore,
13990 // if that is detected then we need to pass that in as an optional param.
13991 if (!isClassBased && input.suffix !== null) {
13992 params.push(literal(input.suffix));
13993 }
13994 return params;
13995 }
13996 };
13997 // If we ended up generating a call to the same instruction as the previous styling property
13998 // we can chain the calls together safely to save some bytes, otherwise we have to generate
13999 // a separate instruction call. This is primarily a concern with interpolation instructions
14000 // where we may start off with one `reference`, but end up using another based on the
14001 // number of interpolations.
14002 if (previousInstruction && previousInstruction.reference === referenceForCall) {
14003 previousInstruction.calls.push(call);
14004 }
14005 else {
14006 instructions.push({ reference: referenceForCall, calls: [call] });
14007 }
14008 });
14009 return instructions;
14010 }
14011 _buildClassInputs(valueConverter) {
14012 if (this._singleClassInputs) {
14013 return this._buildSingleInputs(Identifiers$1.classProp, this._singleClassInputs, valueConverter, null, true);
14014 }
14015 return [];
14016 }
14017 _buildStyleInputs(valueConverter) {
14018 if (this._singleStyleInputs) {
14019 return this._buildSingleInputs(Identifiers$1.styleProp, this._singleStyleInputs, valueConverter, getStylePropInterpolationExpression, false);
14020 }
14021 return [];
14022 }
14023 /**
14024 * Constructs all instructions which contain the expressions that will be placed
14025 * into the update block of a template function or a directive hostBindings function.
14026 */
14027 buildUpdateLevelInstructions(valueConverter) {
14028 const instructions = [];
14029 if (this.hasBindings) {
14030 const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
14031 if (styleMapInstruction) {
14032 instructions.push(styleMapInstruction);
14033 }
14034 const classMapInstruction = this.buildClassMapInstruction(valueConverter);
14035 if (classMapInstruction) {
14036 instructions.push(classMapInstruction);
14037 }
14038 instructions.push(...this._buildStyleInputs(valueConverter));
14039 instructions.push(...this._buildClassInputs(valueConverter));
14040 }
14041 return instructions;
14042 }
14043 }
14044 function registerIntoMap(map, key) {
14045 if (!map.has(key)) {
14046 map.set(key, map.size);
14047 }
14048 }
14049 function parseProperty(name) {
14050 let hasOverrideFlag = false;
14051 const overrideIndex = name.indexOf(IMPORTANT_FLAG);
14052 if (overrideIndex !== -1) {
14053 name = overrideIndex > 0 ? name.substring(0, overrideIndex) : '';
14054 hasOverrideFlag = true;
14055 }
14056 let suffix = null;
14057 let property = name;
14058 const unitIndex = name.lastIndexOf('.');
14059 if (unitIndex > 0) {
14060 suffix = name.substr(unitIndex + 1);
14061 property = name.substring(0, unitIndex);
14062 }
14063 return { property, suffix, hasOverrideFlag };
14064 }
14065 /**
14066 * Gets the instruction to generate for an interpolated class map.
14067 * @param interpolation An Interpolation AST
14068 */
14069 function getClassMapInterpolationExpression(interpolation) {
14070 switch (getInterpolationArgsLength(interpolation)) {
14071 case 1:
14072 return Identifiers$1.classMap;
14073 case 3:
14074 return Identifiers$1.classMapInterpolate1;
14075 case 5:
14076 return Identifiers$1.classMapInterpolate2;
14077 case 7:
14078 return Identifiers$1.classMapInterpolate3;
14079 case 9:
14080 return Identifiers$1.classMapInterpolate4;
14081 case 11:
14082 return Identifiers$1.classMapInterpolate5;
14083 case 13:
14084 return Identifiers$1.classMapInterpolate6;
14085 case 15:
14086 return Identifiers$1.classMapInterpolate7;
14087 case 17:
14088 return Identifiers$1.classMapInterpolate8;
14089 default:
14090 return Identifiers$1.classMapInterpolateV;
14091 }
14092 }
14093 /**
14094 * Gets the instruction to generate for an interpolated style map.
14095 * @param interpolation An Interpolation AST
14096 */
14097 function getStyleMapInterpolationExpression(interpolation) {
14098 switch (getInterpolationArgsLength(interpolation)) {
14099 case 1:
14100 return Identifiers$1.styleMap;
14101 case 3:
14102 return Identifiers$1.styleMapInterpolate1;
14103 case 5:
14104 return Identifiers$1.styleMapInterpolate2;
14105 case 7:
14106 return Identifiers$1.styleMapInterpolate3;
14107 case 9:
14108 return Identifiers$1.styleMapInterpolate4;
14109 case 11:
14110 return Identifiers$1.styleMapInterpolate5;
14111 case 13:
14112 return Identifiers$1.styleMapInterpolate6;
14113 case 15:
14114 return Identifiers$1.styleMapInterpolate7;
14115 case 17:
14116 return Identifiers$1.styleMapInterpolate8;
14117 default:
14118 return Identifiers$1.styleMapInterpolateV;
14119 }
14120 }
14121 /**
14122 * Gets the instruction to generate for an interpolated style prop.
14123 * @param interpolation An Interpolation AST
14124 */
14125 function getStylePropInterpolationExpression(interpolation) {
14126 switch (getInterpolationArgsLength(interpolation)) {
14127 case 1:
14128 return Identifiers$1.styleProp;
14129 case 3:
14130 return Identifiers$1.stylePropInterpolate1;
14131 case 5:
14132 return Identifiers$1.stylePropInterpolate2;
14133 case 7:
14134 return Identifiers$1.stylePropInterpolate3;
14135 case 9:
14136 return Identifiers$1.stylePropInterpolate4;
14137 case 11:
14138 return Identifiers$1.stylePropInterpolate5;
14139 case 13:
14140 return Identifiers$1.stylePropInterpolate6;
14141 case 15:
14142 return Identifiers$1.stylePropInterpolate7;
14143 case 17:
14144 return Identifiers$1.stylePropInterpolate8;
14145 default:
14146 return Identifiers$1.stylePropInterpolateV;
14147 }
14148 }
14149 function normalizePropName(prop) {
14150 return hyphenate(prop);
14151 }
14152
14153 /**
14154 * @license
14155 * Copyright Google LLC All Rights Reserved.
14156 *
14157 * Use of this source code is governed by an MIT-style license that can be
14158 * found in the LICENSE file at https://angular.io/license
14159 */
14160 var TokenType$1;
14161 (function (TokenType) {
14162 TokenType[TokenType["Character"] = 0] = "Character";
14163 TokenType[TokenType["Identifier"] = 1] = "Identifier";
14164 TokenType[TokenType["Keyword"] = 2] = "Keyword";
14165 TokenType[TokenType["String"] = 3] = "String";
14166 TokenType[TokenType["Operator"] = 4] = "Operator";
14167 TokenType[TokenType["Number"] = 5] = "Number";
14168 TokenType[TokenType["Error"] = 6] = "Error";
14169 })(TokenType$1 || (TokenType$1 = {}));
14170 const KEYWORDS = ['var', 'let', 'as', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
14171 class Lexer {
14172 tokenize(text) {
14173 const scanner = new _Scanner(text);
14174 const tokens = [];
14175 let token = scanner.scanToken();
14176 while (token != null) {
14177 tokens.push(token);
14178 token = scanner.scanToken();
14179 }
14180 return tokens;
14181 }
14182 }
14183 class Token$1 {
14184 constructor(index, end, type, numValue, strValue) {
14185 this.index = index;
14186 this.end = end;
14187 this.type = type;
14188 this.numValue = numValue;
14189 this.strValue = strValue;
14190 }
14191 isCharacter(code) {
14192 return this.type == TokenType$1.Character && this.numValue == code;
14193 }
14194 isNumber() {
14195 return this.type == TokenType$1.Number;
14196 }
14197 isString() {
14198 return this.type == TokenType$1.String;
14199 }
14200 isOperator(operator) {
14201 return this.type == TokenType$1.Operator && this.strValue == operator;
14202 }
14203 isIdentifier() {
14204 return this.type == TokenType$1.Identifier;
14205 }
14206 isKeyword() {
14207 return this.type == TokenType$1.Keyword;
14208 }
14209 isKeywordLet() {
14210 return this.type == TokenType$1.Keyword && this.strValue == 'let';
14211 }
14212 isKeywordAs() {
14213 return this.type == TokenType$1.Keyword && this.strValue == 'as';
14214 }
14215 isKeywordNull() {
14216 return this.type == TokenType$1.Keyword && this.strValue == 'null';
14217 }
14218 isKeywordUndefined() {
14219 return this.type == TokenType$1.Keyword && this.strValue == 'undefined';
14220 }
14221 isKeywordTrue() {
14222 return this.type == TokenType$1.Keyword && this.strValue == 'true';
14223 }
14224 isKeywordFalse() {
14225 return this.type == TokenType$1.Keyword && this.strValue == 'false';
14226 }
14227 isKeywordThis() {
14228 return this.type == TokenType$1.Keyword && this.strValue == 'this';
14229 }
14230 isError() {
14231 return this.type == TokenType$1.Error;
14232 }
14233 toNumber() {
14234 return this.type == TokenType$1.Number ? this.numValue : -1;
14235 }
14236 toString() {
14237 switch (this.type) {
14238 case TokenType$1.Character:
14239 case TokenType$1.Identifier:
14240 case TokenType$1.Keyword:
14241 case TokenType$1.Operator:
14242 case TokenType$1.String:
14243 case TokenType$1.Error:
14244 return this.strValue;
14245 case TokenType$1.Number:
14246 return this.numValue.toString();
14247 default:
14248 return null;
14249 }
14250 }
14251 }
14252 function newCharacterToken(index, end, code) {
14253 return new Token$1(index, end, TokenType$1.Character, code, String.fromCharCode(code));
14254 }
14255 function newIdentifierToken(index, end, text) {
14256 return new Token$1(index, end, TokenType$1.Identifier, 0, text);
14257 }
14258 function newKeywordToken(index, end, text) {
14259 return new Token$1(index, end, TokenType$1.Keyword, 0, text);
14260 }
14261 function newOperatorToken(index, end, text) {
14262 return new Token$1(index, end, TokenType$1.Operator, 0, text);
14263 }
14264 function newStringToken(index, end, text) {
14265 return new Token$1(index, end, TokenType$1.String, 0, text);
14266 }
14267 function newNumberToken(index, end, n) {
14268 return new Token$1(index, end, TokenType$1.Number, n, '');
14269 }
14270 function newErrorToken(index, end, message) {
14271 return new Token$1(index, end, TokenType$1.Error, 0, message);
14272 }
14273 const EOF = new Token$1(-1, -1, TokenType$1.Character, 0, '');
14274 class _Scanner {
14275 constructor(input) {
14276 this.input = input;
14277 this.peek = 0;
14278 this.index = -1;
14279 this.length = input.length;
14280 this.advance();
14281 }
14282 advance() {
14283 this.peek = ++this.index >= this.length ? $EOF : this.input.charCodeAt(this.index);
14284 }
14285 scanToken() {
14286 const input = this.input, length = this.length;
14287 let peek = this.peek, index = this.index;
14288 // Skip whitespace.
14289 while (peek <= $SPACE) {
14290 if (++index >= length) {
14291 peek = $EOF;
14292 break;
14293 }
14294 else {
14295 peek = input.charCodeAt(index);
14296 }
14297 }
14298 this.peek = peek;
14299 this.index = index;
14300 if (index >= length) {
14301 return null;
14302 }
14303 // Handle identifiers and numbers.
14304 if (isIdentifierStart(peek))
14305 return this.scanIdentifier();
14306 if (isDigit(peek))
14307 return this.scanNumber(index);
14308 const start = index;
14309 switch (peek) {
14310 case $PERIOD:
14311 this.advance();
14312 return isDigit(this.peek) ? this.scanNumber(start) :
14313 newCharacterToken(start, this.index, $PERIOD);
14314 case $LPAREN:
14315 case $RPAREN:
14316 case $LBRACE:
14317 case $RBRACE:
14318 case $LBRACKET:
14319 case $RBRACKET:
14320 case $COMMA:
14321 case $COLON:
14322 case $SEMICOLON:
14323 return this.scanCharacter(start, peek);
14324 case $SQ:
14325 case $DQ:
14326 return this.scanString();
14327 case $HASH:
14328 case $PLUS:
14329 case $MINUS:
14330 case $STAR:
14331 case $SLASH:
14332 case $PERCENT:
14333 case $CARET:
14334 return this.scanOperator(start, String.fromCharCode(peek));
14335 case $QUESTION:
14336 return this.scanComplexOperator(start, '?', $PERIOD, '.');
14337 case $LT:
14338 case $GT:
14339 return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=');
14340 case $BANG:
14341 case $EQ:
14342 return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=', $EQ, '=');
14343 case $AMPERSAND:
14344 return this.scanComplexOperator(start, '&', $AMPERSAND, '&');
14345 case $BAR:
14346 return this.scanComplexOperator(start, '|', $BAR, '|');
14347 case $NBSP:
14348 while (isWhitespace(this.peek))
14349 this.advance();
14350 return this.scanToken();
14351 }
14352 this.advance();
14353 return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);
14354 }
14355 scanCharacter(start, code) {
14356 this.advance();
14357 return newCharacterToken(start, this.index, code);
14358 }
14359 scanOperator(start, str) {
14360 this.advance();
14361 return newOperatorToken(start, this.index, str);
14362 }
14363 /**
14364 * Tokenize a 2/3 char long operator
14365 *
14366 * @param start start index in the expression
14367 * @param one first symbol (always part of the operator)
14368 * @param twoCode code point for the second symbol
14369 * @param two second symbol (part of the operator when the second code point matches)
14370 * @param threeCode code point for the third symbol
14371 * @param three third symbol (part of the operator when provided and matches source expression)
14372 */
14373 scanComplexOperator(start, one, twoCode, two, threeCode, three) {
14374 this.advance();
14375 let str = one;
14376 if (this.peek == twoCode) {
14377 this.advance();
14378 str += two;
14379 }
14380 if (threeCode != null && this.peek == threeCode) {
14381 this.advance();
14382 str += three;
14383 }
14384 return newOperatorToken(start, this.index, str);
14385 }
14386 scanIdentifier() {
14387 const start = this.index;
14388 this.advance();
14389 while (isIdentifierPart(this.peek))
14390 this.advance();
14391 const str = this.input.substring(start, this.index);
14392 return KEYWORDS.indexOf(str) > -1 ? newKeywordToken(start, this.index, str) :
14393 newIdentifierToken(start, this.index, str);
14394 }
14395 scanNumber(start) {
14396 let simple = (this.index === start);
14397 this.advance(); // Skip initial digit.
14398 while (true) {
14399 if (isDigit(this.peek)) ;
14400 else if (this.peek == $PERIOD) {
14401 simple = false;
14402 }
14403 else if (isExponentStart(this.peek)) {
14404 this.advance();
14405 if (isExponentSign(this.peek))
14406 this.advance();
14407 if (!isDigit(this.peek))
14408 return this.error('Invalid exponent', -1);
14409 simple = false;
14410 }
14411 else {
14412 break;
14413 }
14414 this.advance();
14415 }
14416 const str = this.input.substring(start, this.index);
14417 const value = simple ? parseIntAutoRadix(str) : parseFloat(str);
14418 return newNumberToken(start, this.index, value);
14419 }
14420 scanString() {
14421 const start = this.index;
14422 const quote = this.peek;
14423 this.advance(); // Skip initial quote.
14424 let buffer = '';
14425 let marker = this.index;
14426 const input = this.input;
14427 while (this.peek != quote) {
14428 if (this.peek == $BACKSLASH) {
14429 buffer += input.substring(marker, this.index);
14430 this.advance();
14431 let unescapedCode;
14432 // Workaround for TS2.1-introduced type strictness
14433 this.peek = this.peek;
14434 if (this.peek == $u) {
14435 // 4 character hex code for unicode character.
14436 const hex = input.substring(this.index + 1, this.index + 5);
14437 if (/^[0-9a-f]+$/i.test(hex)) {
14438 unescapedCode = parseInt(hex, 16);
14439 }
14440 else {
14441 return this.error(`Invalid unicode escape [\\u${hex}]`, 0);
14442 }
14443 for (let i = 0; i < 5; i++) {
14444 this.advance();
14445 }
14446 }
14447 else {
14448 unescapedCode = unescape(this.peek);
14449 this.advance();
14450 }
14451 buffer += String.fromCharCode(unescapedCode);
14452 marker = this.index;
14453 }
14454 else if (this.peek == $EOF) {
14455 return this.error('Unterminated quote', 0);
14456 }
14457 else {
14458 this.advance();
14459 }
14460 }
14461 const last = input.substring(marker, this.index);
14462 this.advance(); // Skip terminating quote.
14463 return newStringToken(start, this.index, buffer + last);
14464 }
14465 error(message, offset) {
14466 const position = this.index + offset;
14467 return newErrorToken(position, this.index, `Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
14468 }
14469 }
14470 function isIdentifierStart(code) {
14471 return ($a <= code && code <= $z) || ($A <= code && code <= $Z) ||
14472 (code == $_) || (code == $$);
14473 }
14474 function isIdentifier(input) {
14475 if (input.length == 0)
14476 return false;
14477 const scanner = new _Scanner(input);
14478 if (!isIdentifierStart(scanner.peek))
14479 return false;
14480 scanner.advance();
14481 while (scanner.peek !== $EOF) {
14482 if (!isIdentifierPart(scanner.peek))
14483 return false;
14484 scanner.advance();
14485 }
14486 return true;
14487 }
14488 function isIdentifierPart(code) {
14489 return isAsciiLetter(code) || isDigit(code) || (code == $_) ||
14490 (code == $$);
14491 }
14492 function isExponentStart(code) {
14493 return code == $e || code == $E;
14494 }
14495 function isExponentSign(code) {
14496 return code == $MINUS || code == $PLUS;
14497 }
14498 function isQuote(code) {
14499 return code === $SQ || code === $DQ || code === $BT;
14500 }
14501 function unescape(code) {
14502 switch (code) {
14503 case $n:
14504 return $LF;
14505 case $f:
14506 return $FF;
14507 case $r:
14508 return $CR;
14509 case $t:
14510 return $TAB;
14511 case $v:
14512 return $VTAB;
14513 default:
14514 return code;
14515 }
14516 }
14517 function parseIntAutoRadix(text) {
14518 const result = parseInt(text);
14519 if (isNaN(result)) {
14520 throw new Error('Invalid integer literal when parsing ' + text);
14521 }
14522 return result;
14523 }
14524
14525 /**
14526 * @license
14527 * Copyright Google LLC All Rights Reserved.
14528 *
14529 * Use of this source code is governed by an MIT-style license that can be
14530 * found in the LICENSE file at https://angular.io/license
14531 */
14532 class SplitInterpolation {
14533 constructor(strings, expressions, offsets) {
14534 this.strings = strings;
14535 this.expressions = expressions;
14536 this.offsets = offsets;
14537 }
14538 }
14539 class TemplateBindingParseResult {
14540 constructor(templateBindings, warnings, errors) {
14541 this.templateBindings = templateBindings;
14542 this.warnings = warnings;
14543 this.errors = errors;
14544 }
14545 }
14546 class Parser$1 {
14547 constructor(_lexer) {
14548 this._lexer = _lexer;
14549 this.errors = [];
14550 this.simpleExpressionChecker = SimpleExpressionChecker;
14551 }
14552 parseAction(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
14553 this._checkNoInterpolation(input, location, interpolationConfig);
14554 const sourceToLex = this._stripComments(input);
14555 const tokens = this._lexer.tokenize(this._stripComments(input));
14556 const ast = new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, true, this.errors, input.length - sourceToLex.length)
14557 .parseChain();
14558 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
14559 }
14560 parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
14561 const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);
14562 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
14563 }
14564 checkSimpleExpression(ast) {
14565 const checker = new this.simpleExpressionChecker();
14566 ast.visit(checker);
14567 return checker.errors;
14568 }
14569 parseSimpleBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
14570 const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);
14571 const errors = this.checkSimpleExpression(ast);
14572 if (errors.length > 0) {
14573 this._reportError(`Host binding expression cannot contain ${errors.join(' ')}`, input, location);
14574 }
14575 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
14576 }
14577 _reportError(message, input, errLocation, ctxLocation) {
14578 this.errors.push(new ParserError(message, input, errLocation, ctxLocation));
14579 }
14580 _parseBindingAst(input, location, absoluteOffset, interpolationConfig) {
14581 // Quotes expressions use 3rd-party expression language. We don't want to use
14582 // our lexer or parser for that, so we check for that ahead of time.
14583 const quote = this._parseQuote(input, location, absoluteOffset);
14584 if (quote != null) {
14585 return quote;
14586 }
14587 this._checkNoInterpolation(input, location, interpolationConfig);
14588 const sourceToLex = this._stripComments(input);
14589 const tokens = this._lexer.tokenize(sourceToLex);
14590 return new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, input.length - sourceToLex.length)
14591 .parseChain();
14592 }
14593 _parseQuote(input, location, absoluteOffset) {
14594 if (input == null)
14595 return null;
14596 const prefixSeparatorIndex = input.indexOf(':');
14597 if (prefixSeparatorIndex == -1)
14598 return null;
14599 const prefix = input.substring(0, prefixSeparatorIndex).trim();
14600 if (!isIdentifier(prefix))
14601 return null;
14602 const uninterpretedExpression = input.substring(prefixSeparatorIndex + 1);
14603 const span = new ParseSpan(0, input.length);
14604 return new Quote(span, span.toAbsolute(absoluteOffset), prefix, uninterpretedExpression, location);
14605 }
14606 /**
14607 * Parse microsyntax template expression and return a list of bindings or
14608 * parsing errors in case the given expression is invalid.
14609 *
14610 * For example,
14611 * ```
14612 * <div *ngFor="let item of items">
14613 * ^ ^ absoluteValueOffset for `templateValue`
14614 * absoluteKeyOffset for `templateKey`
14615 * ```
14616 * contains three bindings:
14617 * 1. ngFor -> null
14618 * 2. item -> NgForOfContext.$implicit
14619 * 3. ngForOf -> items
14620 *
14621 * This is apparent from the de-sugared template:
14622 * ```
14623 * <ng-template ngFor let-item [ngForOf]="items">
14624 * ```
14625 *
14626 * @param templateKey name of directive, without the * prefix. For example: ngIf, ngFor
14627 * @param templateValue RHS of the microsyntax attribute
14628 * @param templateUrl template filename if it's external, component filename if it's inline
14629 * @param absoluteKeyOffset start of the `templateKey`
14630 * @param absoluteValueOffset start of the `templateValue`
14631 */
14632 parseTemplateBindings(templateKey, templateValue, templateUrl, absoluteKeyOffset, absoluteValueOffset) {
14633 const tokens = this._lexer.tokenize(templateValue);
14634 const parser = new _ParseAST(templateValue, templateUrl, absoluteValueOffset, tokens, templateValue.length, false /* parseAction */, this.errors, 0 /* relative offset */);
14635 return parser.parseTemplateBindings({
14636 source: templateKey,
14637 span: new AbsoluteSourceSpan(absoluteKeyOffset, absoluteKeyOffset + templateKey.length),
14638 });
14639 }
14640 parseInterpolation(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
14641 const { strings, expressions, offsets } = this.splitInterpolation(input, location, interpolationConfig);
14642 if (expressions.length === 0)
14643 return null;
14644 const expressionNodes = [];
14645 for (let i = 0; i < expressions.length; ++i) {
14646 const expressionText = expressions[i].text;
14647 const sourceToLex = this._stripComments(expressionText);
14648 const tokens = this._lexer.tokenize(sourceToLex);
14649 const ast = new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, offsets[i] + (expressionText.length - sourceToLex.length))
14650 .parseChain();
14651 expressionNodes.push(ast);
14652 }
14653 return this.createInterpolationAst(strings.map(s => s.text), expressionNodes, input, location, absoluteOffset);
14654 }
14655 /**
14656 * Similar to `parseInterpolation`, but treats the provided string as a single expression
14657 * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).
14658 * This is used for parsing the switch expression in ICUs.
14659 */
14660 parseInterpolationExpression(expression, location, absoluteOffset) {
14661 const sourceToLex = this._stripComments(expression);
14662 const tokens = this._lexer.tokenize(sourceToLex);
14663 const ast = new _ParseAST(expression, location, absoluteOffset, tokens, sourceToLex.length,
14664 /* parseAction */ false, this.errors, 0)
14665 .parseChain();
14666 const strings = ['', '']; // The prefix and suffix strings are both empty
14667 return this.createInterpolationAst(strings, [ast], expression, location, absoluteOffset);
14668 }
14669 createInterpolationAst(strings, expressions, input, location, absoluteOffset) {
14670 const span = new ParseSpan(0, input.length);
14671 const interpolation = new Interpolation(span, span.toAbsolute(absoluteOffset), strings, expressions);
14672 return new ASTWithSource(interpolation, input, location, absoluteOffset, this.errors);
14673 }
14674 /**
14675 * Splits a string of text into "raw" text segments and expressions present in interpolations in
14676 * the string.
14677 * Returns `null` if there are no interpolations, otherwise a
14678 * `SplitInterpolation` with splits that look like
14679 * <raw text> <expression> <raw text> ... <raw text> <expression> <raw text>
14680 */
14681 splitInterpolation(input, location, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
14682 const strings = [];
14683 const expressions = [];
14684 const offsets = [];
14685 let i = 0;
14686 let atInterpolation = false;
14687 let extendLastString = false;
14688 let { start: interpStart, end: interpEnd } = interpolationConfig;
14689 while (i < input.length) {
14690 if (!atInterpolation) {
14691 // parse until starting {{
14692 const start = i;
14693 i = input.indexOf(interpStart, i);
14694 if (i === -1) {
14695 i = input.length;
14696 }
14697 const text = input.substring(start, i);
14698 strings.push({ text, start, end: i });
14699 atInterpolation = true;
14700 }
14701 else {
14702 // parse from starting {{ to ending }} while ignoring content inside quotes.
14703 const fullStart = i;
14704 const exprStart = fullStart + interpStart.length;
14705 const exprEnd = this._getInterpolationEndIndex(input, interpEnd, exprStart);
14706 if (exprEnd === -1) {
14707 // Could not find the end of the interpolation; do not parse an expression.
14708 // Instead we should extend the content on the last raw string.
14709 atInterpolation = false;
14710 extendLastString = true;
14711 break;
14712 }
14713 const fullEnd = exprEnd + interpEnd.length;
14714 const text = input.substring(exprStart, exprEnd);
14715 if (text.trim().length === 0) {
14716 this._reportError('Blank expressions are not allowed in interpolated strings', input, `at column ${i} in`, location);
14717 }
14718 expressions.push({ text, start: fullStart, end: fullEnd });
14719 offsets.push(exprStart);
14720 i = fullEnd;
14721 atInterpolation = false;
14722 }
14723 }
14724 if (!atInterpolation) {
14725 // If we are now at a text section, add the remaining content as a raw string.
14726 if (extendLastString) {
14727 const piece = strings[strings.length - 1];
14728 piece.text += input.substring(i);
14729 piece.end = input.length;
14730 }
14731 else {
14732 strings.push({ text: input.substring(i), start: i, end: input.length });
14733 }
14734 }
14735 return new SplitInterpolation(strings, expressions, offsets);
14736 }
14737 wrapLiteralPrimitive(input, location, absoluteOffset) {
14738 const span = new ParseSpan(0, input == null ? 0 : input.length);
14739 return new ASTWithSource(new LiteralPrimitive(span, span.toAbsolute(absoluteOffset), input), input, location, absoluteOffset, this.errors);
14740 }
14741 _stripComments(input) {
14742 const i = this._commentStart(input);
14743 return i != null ? input.substring(0, i).trim() : input;
14744 }
14745 _commentStart(input) {
14746 let outerQuote = null;
14747 for (let i = 0; i < input.length - 1; i++) {
14748 const char = input.charCodeAt(i);
14749 const nextChar = input.charCodeAt(i + 1);
14750 if (char === $SLASH && nextChar == $SLASH && outerQuote == null)
14751 return i;
14752 if (outerQuote === char) {
14753 outerQuote = null;
14754 }
14755 else if (outerQuote == null && isQuote(char)) {
14756 outerQuote = char;
14757 }
14758 }
14759 return null;
14760 }
14761 _checkNoInterpolation(input, location, { start, end }) {
14762 let startIndex = -1;
14763 let endIndex = -1;
14764 for (const charIndex of this._forEachUnquotedChar(input, 0)) {
14765 if (startIndex === -1) {
14766 if (input.startsWith(start)) {
14767 startIndex = charIndex;
14768 }
14769 }
14770 else {
14771 endIndex = this._getInterpolationEndIndex(input, end, charIndex);
14772 if (endIndex > -1) {
14773 break;
14774 }
14775 }
14776 }
14777 if (startIndex > -1 && endIndex > -1) {
14778 this._reportError(`Got interpolation (${start}${end}) where expression was expected`, input, `at column ${startIndex} in`, location);
14779 }
14780 }
14781 /**
14782 * Finds the index of the end of an interpolation expression
14783 * while ignoring comments and quoted content.
14784 */
14785 _getInterpolationEndIndex(input, expressionEnd, start) {
14786 for (const charIndex of this._forEachUnquotedChar(input, start)) {
14787 if (input.startsWith(expressionEnd, charIndex)) {
14788 return charIndex;
14789 }
14790 // Nothing else in the expression matters after we've
14791 // hit a comment so look directly for the end token.
14792 if (input.startsWith('//', charIndex)) {
14793 return input.indexOf(expressionEnd, charIndex);
14794 }
14795 }
14796 return -1;
14797 }
14798 /**
14799 * Generator used to iterate over the character indexes of a string that are outside of quotes.
14800 * @param input String to loop through.
14801 * @param start Index within the string at which to start.
14802 */
14803 *_forEachUnquotedChar(input, start) {
14804 let currentQuote = null;
14805 let escapeCount = 0;
14806 for (let i = start; i < input.length; i++) {
14807 const char = input[i];
14808 // Skip the characters inside quotes. Note that we only care about the outer-most
14809 // quotes matching up and we need to account for escape characters.
14810 if (isQuote(input.charCodeAt(i)) && (currentQuote === null || currentQuote === char) &&
14811 escapeCount % 2 === 0) {
14812 currentQuote = currentQuote === null ? char : null;
14813 }
14814 else if (currentQuote === null) {
14815 yield i;
14816 }
14817 escapeCount = char === '\\' ? escapeCount + 1 : 0;
14818 }
14819 }
14820 }
14821 class IvyParser extends Parser$1 {
14822 constructor() {
14823 super(...arguments);
14824 this.simpleExpressionChecker = IvySimpleExpressionChecker;
14825 }
14826 }
14827 /** Describes a stateful context an expression parser is in. */
14828 var ParseContextFlags;
14829 (function (ParseContextFlags) {
14830 ParseContextFlags[ParseContextFlags["None"] = 0] = "None";
14831 /**
14832 * A Writable context is one in which a value may be written to an lvalue.
14833 * For example, after we see a property access, we may expect a write to the
14834 * property via the "=" operator.
14835 * prop
14836 * ^ possible "=" after
14837 */
14838 ParseContextFlags[ParseContextFlags["Writable"] = 1] = "Writable";
14839 })(ParseContextFlags || (ParseContextFlags = {}));
14840 class _ParseAST {
14841 constructor(input, location, absoluteOffset, tokens, inputLength, parseAction, errors, offset) {
14842 this.input = input;
14843 this.location = location;
14844 this.absoluteOffset = absoluteOffset;
14845 this.tokens = tokens;
14846 this.inputLength = inputLength;
14847 this.parseAction = parseAction;
14848 this.errors = errors;
14849 this.offset = offset;
14850 this.rparensExpected = 0;
14851 this.rbracketsExpected = 0;
14852 this.rbracesExpected = 0;
14853 this.context = ParseContextFlags.None;
14854 // Cache of expression start and input indeces to the absolute source span they map to, used to
14855 // prevent creating superfluous source spans in `sourceSpan`.
14856 // A serial of the expression start and input index is used for mapping because both are stateful
14857 // and may change for subsequent expressions visited by the parser.
14858 this.sourceSpanCache = new Map();
14859 this.index = 0;
14860 }
14861 peek(offset) {
14862 const i = this.index + offset;
14863 return i < this.tokens.length ? this.tokens[i] : EOF;
14864 }
14865 get next() {
14866 return this.peek(0);
14867 }
14868 /** Whether all the parser input has been processed. */
14869 get atEOF() {
14870 return this.index >= this.tokens.length;
14871 }
14872 /**
14873 * Index of the next token to be processed, or the end of the last token if all have been
14874 * processed.
14875 */
14876 get inputIndex() {
14877 return this.atEOF ? this.currentEndIndex : this.next.index + this.offset;
14878 }
14879 /**
14880 * End index of the last processed token, or the start of the first token if none have been
14881 * processed.
14882 */
14883 get currentEndIndex() {
14884 if (this.index > 0) {
14885 const curToken = this.peek(-1);
14886 return curToken.end + this.offset;
14887 }
14888 // No tokens have been processed yet; return the next token's start or the length of the input
14889 // if there is no token.
14890 if (this.tokens.length === 0) {
14891 return this.inputLength + this.offset;
14892 }
14893 return this.next.index + this.offset;
14894 }
14895 /**
14896 * Returns the absolute offset of the start of the current token.
14897 */
14898 get currentAbsoluteOffset() {
14899 return this.absoluteOffset + this.inputIndex;
14900 }
14901 /**
14902 * Retrieve a `ParseSpan` from `start` to the current position (or to `artificialEndIndex` if
14903 * provided).
14904 *
14905 * @param start Position from which the `ParseSpan` will start.
14906 * @param artificialEndIndex Optional ending index to be used if provided (and if greater than the
14907 * natural ending index)
14908 */
14909 span(start, artificialEndIndex) {
14910 let endIndex = this.currentEndIndex;
14911 if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) {
14912 endIndex = artificialEndIndex;
14913 }
14914 return new ParseSpan(start, endIndex);
14915 }
14916 sourceSpan(start, artificialEndIndex) {
14917 const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`;
14918 if (!this.sourceSpanCache.has(serial)) {
14919 this.sourceSpanCache.set(serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset));
14920 }
14921 return this.sourceSpanCache.get(serial);
14922 }
14923 advance() {
14924 this.index++;
14925 }
14926 /**
14927 * Executes a callback in the provided context.
14928 */
14929 withContext(context, cb) {
14930 this.context |= context;
14931 const ret = cb();
14932 this.context ^= context;
14933 return ret;
14934 }
14935 consumeOptionalCharacter(code) {
14936 if (this.next.isCharacter(code)) {
14937 this.advance();
14938 return true;
14939 }
14940 else {
14941 return false;
14942 }
14943 }
14944 peekKeywordLet() {
14945 return this.next.isKeywordLet();
14946 }
14947 peekKeywordAs() {
14948 return this.next.isKeywordAs();
14949 }
14950 /**
14951 * Consumes an expected character, otherwise emits an error about the missing expected character
14952 * and skips over the token stream until reaching a recoverable point.
14953 *
14954 * See `this.error` and `this.skip` for more details.
14955 */
14956 expectCharacter(code) {
14957 if (this.consumeOptionalCharacter(code))
14958 return;
14959 this.error(`Missing expected ${String.fromCharCode(code)}`);
14960 }
14961 consumeOptionalOperator(op) {
14962 if (this.next.isOperator(op)) {
14963 this.advance();
14964 return true;
14965 }
14966 else {
14967 return false;
14968 }
14969 }
14970 expectOperator(operator) {
14971 if (this.consumeOptionalOperator(operator))
14972 return;
14973 this.error(`Missing expected operator ${operator}`);
14974 }
14975 prettyPrintToken(tok) {
14976 return tok === EOF ? 'end of input' : `token ${tok}`;
14977 }
14978 expectIdentifierOrKeyword() {
14979 const n = this.next;
14980 if (!n.isIdentifier() && !n.isKeyword()) {
14981 this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`);
14982 return null;
14983 }
14984 this.advance();
14985 return n.toString();
14986 }
14987 expectIdentifierOrKeywordOrString() {
14988 const n = this.next;
14989 if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
14990 this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier, keyword, or string`);
14991 return '';
14992 }
14993 this.advance();
14994 return n.toString();
14995 }
14996 parseChain() {
14997 const exprs = [];
14998 const start = this.inputIndex;
14999 while (this.index < this.tokens.length) {
15000 const expr = this.parsePipe();
15001 exprs.push(expr);
15002 if (this.consumeOptionalCharacter($SEMICOLON)) {
15003 if (!this.parseAction) {
15004 this.error('Binding expression cannot contain chained expression');
15005 }
15006 while (this.consumeOptionalCharacter($SEMICOLON)) {
15007 } // read all semicolons
15008 }
15009 else if (this.index < this.tokens.length) {
15010 this.error(`Unexpected token '${this.next}'`);
15011 }
15012 }
15013 if (exprs.length == 0) {
15014 // We have no expressions so create an empty expression that spans the entire input length
15015 const artificialStart = this.offset;
15016 const artificialEnd = this.offset + this.inputLength;
15017 return new EmptyExpr(this.span(artificialStart, artificialEnd), this.sourceSpan(artificialStart, artificialEnd));
15018 }
15019 if (exprs.length == 1)
15020 return exprs[0];
15021 return new Chain(this.span(start), this.sourceSpan(start), exprs);
15022 }
15023 parsePipe() {
15024 const start = this.inputIndex;
15025 let result = this.parseExpression();
15026 if (this.consumeOptionalOperator('|')) {
15027 if (this.parseAction) {
15028 this.error('Cannot have a pipe in an action expression');
15029 }
15030 do {
15031 const nameStart = this.inputIndex;
15032 let nameId = this.expectIdentifierOrKeyword();
15033 let nameSpan;
15034 let fullSpanEnd = undefined;
15035 if (nameId !== null) {
15036 nameSpan = this.sourceSpan(nameStart);
15037 }
15038 else {
15039 // No valid identifier was found, so we'll assume an empty pipe name ('').
15040 nameId = '';
15041 // However, there may have been whitespace present between the pipe character and the next
15042 // token in the sequence (or the end of input). We want to track this whitespace so that
15043 // the `BindingPipe` we produce covers not just the pipe character, but any trailing
15044 // whitespace beyond it. Another way of thinking about this is that the zero-length name
15045 // is assumed to be at the end of any whitespace beyond the pipe character.
15046 //
15047 // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the
15048 // beginning of the next token, or until the end of input if the next token is EOF.
15049 fullSpanEnd = this.next.index !== -1 ? this.next.index : this.inputLength + this.offset;
15050 // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace
15051 // beyond the pipe character.
15052 nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset);
15053 }
15054 const args = [];
15055 while (this.consumeOptionalCharacter($COLON)) {
15056 args.push(this.parseExpression());
15057 // If there are additional expressions beyond the name, then the artificial end for the
15058 // name is no longer relevant.
15059 }
15060 result = new BindingPipe(this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, nameSpan);
15061 } while (this.consumeOptionalOperator('|'));
15062 }
15063 return result;
15064 }
15065 parseExpression() {
15066 return this.parseConditional();
15067 }
15068 parseConditional() {
15069 const start = this.inputIndex;
15070 const result = this.parseLogicalOr();
15071 if (this.consumeOptionalOperator('?')) {
15072 const yes = this.parsePipe();
15073 let no;
15074 if (!this.consumeOptionalCharacter($COLON)) {
15075 const end = this.inputIndex;
15076 const expression = this.input.substring(start, end);
15077 this.error(`Conditional expression ${expression} requires all 3 expressions`);
15078 no = new EmptyExpr(this.span(start), this.sourceSpan(start));
15079 }
15080 else {
15081 no = this.parsePipe();
15082 }
15083 return new Conditional(this.span(start), this.sourceSpan(start), result, yes, no);
15084 }
15085 else {
15086 return result;
15087 }
15088 }
15089 parseLogicalOr() {
15090 // '||'
15091 const start = this.inputIndex;
15092 let result = this.parseLogicalAnd();
15093 while (this.consumeOptionalOperator('||')) {
15094 const right = this.parseLogicalAnd();
15095 result = new Binary(this.span(start), this.sourceSpan(start), '||', result, right);
15096 }
15097 return result;
15098 }
15099 parseLogicalAnd() {
15100 // '&&'
15101 const start = this.inputIndex;
15102 let result = this.parseEquality();
15103 while (this.consumeOptionalOperator('&&')) {
15104 const right = this.parseEquality();
15105 result = new Binary(this.span(start), this.sourceSpan(start), '&&', result, right);
15106 }
15107 return result;
15108 }
15109 parseEquality() {
15110 // '==','!=','===','!=='
15111 const start = this.inputIndex;
15112 let result = this.parseRelational();
15113 while (this.next.type == TokenType$1.Operator) {
15114 const operator = this.next.strValue;
15115 switch (operator) {
15116 case '==':
15117 case '===':
15118 case '!=':
15119 case '!==':
15120 this.advance();
15121 const right = this.parseRelational();
15122 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
15123 continue;
15124 }
15125 break;
15126 }
15127 return result;
15128 }
15129 parseRelational() {
15130 // '<', '>', '<=', '>='
15131 const start = this.inputIndex;
15132 let result = this.parseAdditive();
15133 while (this.next.type == TokenType$1.Operator) {
15134 const operator = this.next.strValue;
15135 switch (operator) {
15136 case '<':
15137 case '>':
15138 case '<=':
15139 case '>=':
15140 this.advance();
15141 const right = this.parseAdditive();
15142 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
15143 continue;
15144 }
15145 break;
15146 }
15147 return result;
15148 }
15149 parseAdditive() {
15150 // '+', '-'
15151 const start = this.inputIndex;
15152 let result = this.parseMultiplicative();
15153 while (this.next.type == TokenType$1.Operator) {
15154 const operator = this.next.strValue;
15155 switch (operator) {
15156 case '+':
15157 case '-':
15158 this.advance();
15159 let right = this.parseMultiplicative();
15160 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
15161 continue;
15162 }
15163 break;
15164 }
15165 return result;
15166 }
15167 parseMultiplicative() {
15168 // '*', '%', '/'
15169 const start = this.inputIndex;
15170 let result = this.parsePrefix();
15171 while (this.next.type == TokenType$1.Operator) {
15172 const operator = this.next.strValue;
15173 switch (operator) {
15174 case '*':
15175 case '%':
15176 case '/':
15177 this.advance();
15178 let right = this.parsePrefix();
15179 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
15180 continue;
15181 }
15182 break;
15183 }
15184 return result;
15185 }
15186 parsePrefix() {
15187 if (this.next.type == TokenType$1.Operator) {
15188 const start = this.inputIndex;
15189 const operator = this.next.strValue;
15190 let result;
15191 switch (operator) {
15192 case '+':
15193 this.advance();
15194 result = this.parsePrefix();
15195 return Unary.createPlus(this.span(start), this.sourceSpan(start), result);
15196 case '-':
15197 this.advance();
15198 result = this.parsePrefix();
15199 return Unary.createMinus(this.span(start), this.sourceSpan(start), result);
15200 case '!':
15201 this.advance();
15202 result = this.parsePrefix();
15203 return new PrefixNot(this.span(start), this.sourceSpan(start), result);
15204 }
15205 }
15206 return this.parseCallChain();
15207 }
15208 parseCallChain() {
15209 const start = this.inputIndex;
15210 let result = this.parsePrimary();
15211 while (true) {
15212 if (this.consumeOptionalCharacter($PERIOD)) {
15213 result = this.parseAccessMemberOrMethodCall(result, start, false);
15214 }
15215 else if (this.consumeOptionalOperator('?.')) {
15216 result = this.parseAccessMemberOrMethodCall(result, start, true);
15217 }
15218 else if (this.consumeOptionalCharacter($LBRACKET)) {
15219 this.withContext(ParseContextFlags.Writable, () => {
15220 this.rbracketsExpected++;
15221 const key = this.parsePipe();
15222 if (key instanceof EmptyExpr) {
15223 this.error(`Key access cannot be empty`);
15224 }
15225 this.rbracketsExpected--;
15226 this.expectCharacter($RBRACKET);
15227 if (this.consumeOptionalOperator('=')) {
15228 const value = this.parseConditional();
15229 result = new KeyedWrite(this.span(start), this.sourceSpan(start), result, key, value);
15230 }
15231 else {
15232 result = new KeyedRead(this.span(start), this.sourceSpan(start), result, key);
15233 }
15234 });
15235 }
15236 else if (this.consumeOptionalCharacter($LPAREN)) {
15237 this.rparensExpected++;
15238 const args = this.parseCallArguments();
15239 this.rparensExpected--;
15240 this.expectCharacter($RPAREN);
15241 result = new FunctionCall(this.span(start), this.sourceSpan(start), result, args);
15242 }
15243 else if (this.consumeOptionalOperator('!')) {
15244 result = new NonNullAssert(this.span(start), this.sourceSpan(start), result);
15245 }
15246 else {
15247 return result;
15248 }
15249 }
15250 }
15251 parsePrimary() {
15252 const start = this.inputIndex;
15253 if (this.consumeOptionalCharacter($LPAREN)) {
15254 this.rparensExpected++;
15255 const result = this.parsePipe();
15256 this.rparensExpected--;
15257 this.expectCharacter($RPAREN);
15258 return result;
15259 }
15260 else if (this.next.isKeywordNull()) {
15261 this.advance();
15262 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), null);
15263 }
15264 else if (this.next.isKeywordUndefined()) {
15265 this.advance();
15266 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), void 0);
15267 }
15268 else if (this.next.isKeywordTrue()) {
15269 this.advance();
15270 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), true);
15271 }
15272 else if (this.next.isKeywordFalse()) {
15273 this.advance();
15274 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), false);
15275 }
15276 else if (this.next.isKeywordThis()) {
15277 this.advance();
15278 return new ThisReceiver(this.span(start), this.sourceSpan(start));
15279 }
15280 else if (this.consumeOptionalCharacter($LBRACKET)) {
15281 this.rbracketsExpected++;
15282 const elements = this.parseExpressionList($RBRACKET);
15283 this.rbracketsExpected--;
15284 this.expectCharacter($RBRACKET);
15285 return new LiteralArray(this.span(start), this.sourceSpan(start), elements);
15286 }
15287 else if (this.next.isCharacter($LBRACE)) {
15288 return this.parseLiteralMap();
15289 }
15290 else if (this.next.isIdentifier()) {
15291 return this.parseAccessMemberOrMethodCall(new ImplicitReceiver(this.span(start), this.sourceSpan(start)), start, false);
15292 }
15293 else if (this.next.isNumber()) {
15294 const value = this.next.toNumber();
15295 this.advance();
15296 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), value);
15297 }
15298 else if (this.next.isString()) {
15299 const literalValue = this.next.toString();
15300 this.advance();
15301 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), literalValue);
15302 }
15303 else if (this.index >= this.tokens.length) {
15304 this.error(`Unexpected end of expression: ${this.input}`);
15305 return new EmptyExpr(this.span(start), this.sourceSpan(start));
15306 }
15307 else {
15308 this.error(`Unexpected token ${this.next}`);
15309 return new EmptyExpr(this.span(start), this.sourceSpan(start));
15310 }
15311 }
15312 parseExpressionList(terminator) {
15313 const result = [];
15314 do {
15315 if (!this.next.isCharacter(terminator)) {
15316 result.push(this.parsePipe());
15317 }
15318 else {
15319 break;
15320 }
15321 } while (this.consumeOptionalCharacter($COMMA));
15322 return result;
15323 }
15324 parseLiteralMap() {
15325 const keys = [];
15326 const values = [];
15327 const start = this.inputIndex;
15328 this.expectCharacter($LBRACE);
15329 if (!this.consumeOptionalCharacter($RBRACE)) {
15330 this.rbracesExpected++;
15331 do {
15332 const quoted = this.next.isString();
15333 const key = this.expectIdentifierOrKeywordOrString();
15334 keys.push({ key, quoted });
15335 this.expectCharacter($COLON);
15336 values.push(this.parsePipe());
15337 } while (this.consumeOptionalCharacter($COMMA));
15338 this.rbracesExpected--;
15339 this.expectCharacter($RBRACE);
15340 }
15341 return new LiteralMap(this.span(start), this.sourceSpan(start), keys, values);
15342 }
15343 parseAccessMemberOrMethodCall(receiver, start, isSafe = false) {
15344 const nameStart = this.inputIndex;
15345 const id = this.withContext(ParseContextFlags.Writable, () => {
15346 var _a;
15347 const id = (_a = this.expectIdentifierOrKeyword()) !== null && _a !== void 0 ? _a : '';
15348 if (id.length === 0) {
15349 this.error(`Expected identifier for property access`, receiver.span.end);
15350 }
15351 return id;
15352 });
15353 const nameSpan = this.sourceSpan(nameStart);
15354 if (this.consumeOptionalCharacter($LPAREN)) {
15355 this.rparensExpected++;
15356 const args = this.parseCallArguments();
15357 this.expectCharacter($RPAREN);
15358 this.rparensExpected--;
15359 const span = this.span(start);
15360 const sourceSpan = this.sourceSpan(start);
15361 return isSafe ? new SafeMethodCall(span, sourceSpan, nameSpan, receiver, id, args) :
15362 new MethodCall(span, sourceSpan, nameSpan, receiver, id, args);
15363 }
15364 else {
15365 if (isSafe) {
15366 if (this.consumeOptionalOperator('=')) {
15367 this.error('The \'?.\' operator cannot be used in the assignment');
15368 return new EmptyExpr(this.span(start), this.sourceSpan(start));
15369 }
15370 else {
15371 return new SafePropertyRead(this.span(start), this.sourceSpan(start), nameSpan, receiver, id);
15372 }
15373 }
15374 else {
15375 if (this.consumeOptionalOperator('=')) {
15376 if (!this.parseAction) {
15377 this.error('Bindings cannot contain assignments');
15378 return new EmptyExpr(this.span(start), this.sourceSpan(start));
15379 }
15380 const value = this.parseConditional();
15381 return new PropertyWrite(this.span(start), this.sourceSpan(start), nameSpan, receiver, id, value);
15382 }
15383 else {
15384 return new PropertyRead(this.span(start), this.sourceSpan(start), nameSpan, receiver, id);
15385 }
15386 }
15387 }
15388 }
15389 parseCallArguments() {
15390 if (this.next.isCharacter($RPAREN))
15391 return [];
15392 const positionals = [];
15393 do {
15394 positionals.push(this.parsePipe());
15395 } while (this.consumeOptionalCharacter($COMMA));
15396 return positionals;
15397 }
15398 /**
15399 * Parses an identifier, a keyword, a string with an optional `-` in between,
15400 * and returns the string along with its absolute source span.
15401 */
15402 expectTemplateBindingKey() {
15403 let result = '';
15404 let operatorFound = false;
15405 const start = this.currentAbsoluteOffset;
15406 do {
15407 result += this.expectIdentifierOrKeywordOrString();
15408 operatorFound = this.consumeOptionalOperator('-');
15409 if (operatorFound) {
15410 result += '-';
15411 }
15412 } while (operatorFound);
15413 return {
15414 source: result,
15415 span: new AbsoluteSourceSpan(start, start + result.length),
15416 };
15417 }
15418 /**
15419 * Parse microsyntax template expression and return a list of bindings or
15420 * parsing errors in case the given expression is invalid.
15421 *
15422 * For example,
15423 * ```
15424 * <div *ngFor="let item of items; index as i; trackBy: func">
15425 * ```
15426 * contains five bindings:
15427 * 1. ngFor -> null
15428 * 2. item -> NgForOfContext.$implicit
15429 * 3. ngForOf -> items
15430 * 4. i -> NgForOfContext.index
15431 * 5. ngForTrackBy -> func
15432 *
15433 * For a full description of the microsyntax grammar, see
15434 * https://gist.github.com/mhevery/d3530294cff2e4a1b3fe15ff75d08855
15435 *
15436 * @param templateKey name of the microsyntax directive, like ngIf, ngFor,
15437 * without the *, along with its absolute span.
15438 */
15439 parseTemplateBindings(templateKey) {
15440 const bindings = [];
15441 // The first binding is for the template key itself
15442 // In *ngFor="let item of items", key = "ngFor", value = null
15443 // In *ngIf="cond | pipe", key = "ngIf", value = "cond | pipe"
15444 bindings.push(...this.parseDirectiveKeywordBindings(templateKey));
15445 while (this.index < this.tokens.length) {
15446 // If it starts with 'let', then this must be variable declaration
15447 const letBinding = this.parseLetBinding();
15448 if (letBinding) {
15449 bindings.push(letBinding);
15450 }
15451 else {
15452 // Two possible cases here, either `value "as" key` or
15453 // "directive-keyword expression". We don't know which case, but both
15454 // "value" and "directive-keyword" are template binding key, so consume
15455 // the key first.
15456 const key = this.expectTemplateBindingKey();
15457 // Peek at the next token, if it is "as" then this must be variable
15458 // declaration.
15459 const binding = this.parseAsBinding(key);
15460 if (binding) {
15461 bindings.push(binding);
15462 }
15463 else {
15464 // Otherwise the key must be a directive keyword, like "of". Transform
15465 // the key to actual key. Eg. of -> ngForOf, trackBy -> ngForTrackBy
15466 key.source =
15467 templateKey.source + key.source.charAt(0).toUpperCase() + key.source.substring(1);
15468 bindings.push(...this.parseDirectiveKeywordBindings(key));
15469 }
15470 }
15471 this.consumeStatementTerminator();
15472 }
15473 return new TemplateBindingParseResult(bindings, [] /* warnings */, this.errors);
15474 }
15475 /**
15476 * Parse a directive keyword, followed by a mandatory expression.
15477 * For example, "of items", "trackBy: func".
15478 * The bindings are: ngForOf -> items, ngForTrackBy -> func
15479 * There could be an optional "as" binding that follows the expression.
15480 * For example,
15481 * ```
15482 * *ngFor="let item of items | slice:0:1 as collection".
15483 * ^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
15484 * keyword bound target optional 'as' binding
15485 * ```
15486 *
15487 * @param key binding key, for example, ngFor, ngIf, ngForOf, along with its
15488 * absolute span.
15489 */
15490 parseDirectiveKeywordBindings(key) {
15491 const bindings = [];
15492 this.consumeOptionalCharacter($COLON); // trackBy: trackByFunction
15493 const value = this.getDirectiveBoundTarget();
15494 let spanEnd = this.currentAbsoluteOffset;
15495 // The binding could optionally be followed by "as". For example,
15496 // *ngIf="cond | pipe as x". In this case, the key in the "as" binding
15497 // is "x" and the value is the template key itself ("ngIf"). Note that the
15498 // 'key' in the current context now becomes the "value" in the next binding.
15499 const asBinding = this.parseAsBinding(key);
15500 if (!asBinding) {
15501 this.consumeStatementTerminator();
15502 spanEnd = this.currentAbsoluteOffset;
15503 }
15504 const sourceSpan = new AbsoluteSourceSpan(key.span.start, spanEnd);
15505 bindings.push(new ExpressionBinding(sourceSpan, key, value));
15506 if (asBinding) {
15507 bindings.push(asBinding);
15508 }
15509 return bindings;
15510 }
15511 /**
15512 * Return the expression AST for the bound target of a directive keyword
15513 * binding. For example,
15514 * ```
15515 * *ngIf="condition | pipe"
15516 * ^^^^^^^^^^^^^^^^ bound target for "ngIf"
15517 * *ngFor="let item of items"
15518 * ^^^^^ bound target for "ngForOf"
15519 * ```
15520 */
15521 getDirectiveBoundTarget() {
15522 if (this.next === EOF || this.peekKeywordAs() || this.peekKeywordLet()) {
15523 return null;
15524 }
15525 const ast = this.parsePipe(); // example: "condition | async"
15526 const { start, end } = ast.span;
15527 const value = this.input.substring(start, end);
15528 return new ASTWithSource(ast, value, this.location, this.absoluteOffset + start, this.errors);
15529 }
15530 /**
15531 * Return the binding for a variable declared using `as`. Note that the order
15532 * of the key-value pair in this declaration is reversed. For example,
15533 * ```
15534 * *ngFor="let item of items; index as i"
15535 * ^^^^^ ^
15536 * value key
15537 * ```
15538 *
15539 * @param value name of the value in the declaration, "ngIf" in the example
15540 * above, along with its absolute span.
15541 */
15542 parseAsBinding(value) {
15543 if (!this.peekKeywordAs()) {
15544 return null;
15545 }
15546 this.advance(); // consume the 'as' keyword
15547 const key = this.expectTemplateBindingKey();
15548 this.consumeStatementTerminator();
15549 const sourceSpan = new AbsoluteSourceSpan(value.span.start, this.currentAbsoluteOffset);
15550 return new VariableBinding(sourceSpan, key, value);
15551 }
15552 /**
15553 * Return the binding for a variable declared using `let`. For example,
15554 * ```
15555 * *ngFor="let item of items; let i=index;"
15556 * ^^^^^^^^ ^^^^^^^^^^^
15557 * ```
15558 * In the first binding, `item` is bound to `NgForOfContext.$implicit`.
15559 * In the second binding, `i` is bound to `NgForOfContext.index`.
15560 */
15561 parseLetBinding() {
15562 if (!this.peekKeywordLet()) {
15563 return null;
15564 }
15565 const spanStart = this.currentAbsoluteOffset;
15566 this.advance(); // consume the 'let' keyword
15567 const key = this.expectTemplateBindingKey();
15568 let value = null;
15569 if (this.consumeOptionalOperator('=')) {
15570 value = this.expectTemplateBindingKey();
15571 }
15572 this.consumeStatementTerminator();
15573 const sourceSpan = new AbsoluteSourceSpan(spanStart, this.currentAbsoluteOffset);
15574 return new VariableBinding(sourceSpan, key, value);
15575 }
15576 /**
15577 * Consume the optional statement terminator: semicolon or comma.
15578 */
15579 consumeStatementTerminator() {
15580 this.consumeOptionalCharacter($SEMICOLON) || this.consumeOptionalCharacter($COMMA);
15581 }
15582 /**
15583 * Records an error and skips over the token stream until reaching a recoverable point. See
15584 * `this.skip` for more details on token skipping.
15585 */
15586 error(message, index = null) {
15587 this.errors.push(new ParserError(message, this.input, this.locationText(index), this.location));
15588 this.skip();
15589 }
15590 locationText(index = null) {
15591 if (index == null)
15592 index = this.index;
15593 return (index < this.tokens.length) ? `at column ${this.tokens[index].index + 1} in` :
15594 `at the end of the expression`;
15595 }
15596 /**
15597 * Error recovery should skip tokens until it encounters a recovery point.
15598 *
15599 * The following are treated as unconditional recovery points:
15600 * - end of input
15601 * - ';' (parseChain() is always the root production, and it expects a ';')
15602 * - '|' (since pipes may be chained and each pipe expression may be treated independently)
15603 *
15604 * The following are conditional recovery points:
15605 * - ')', '}', ']' if one of calling productions is expecting one of these symbols
15606 * - This allows skip() to recover from errors such as '(a.) + 1' allowing more of the AST to
15607 * be retained (it doesn't skip any tokens as the ')' is retained because of the '(' begins
15608 * an '(' <expr> ')' production).
15609 * The recovery points of grouping symbols must be conditional as they must be skipped if
15610 * none of the calling productions are not expecting the closing token else we will never
15611 * make progress in the case of an extraneous group closing symbol (such as a stray ')').
15612 * That is, we skip a closing symbol if we are not in a grouping production.
15613 * - '=' in a `Writable` context
15614 * - In this context, we are able to recover after seeing the `=` operator, which
15615 * signals the presence of an independent rvalue expression following the `=` operator.
15616 *
15617 * If a production expects one of these token it increments the corresponding nesting count,
15618 * and then decrements it just prior to checking if the token is in the input.
15619 */
15620 skip() {
15621 let n = this.next;
15622 while (this.index < this.tokens.length && !n.isCharacter($SEMICOLON) &&
15623 !n.isOperator('|') && (this.rparensExpected <= 0 || !n.isCharacter($RPAREN)) &&
15624 (this.rbracesExpected <= 0 || !n.isCharacter($RBRACE)) &&
15625 (this.rbracketsExpected <= 0 || !n.isCharacter($RBRACKET)) &&
15626 (!(this.context & ParseContextFlags.Writable) || !n.isOperator('='))) {
15627 if (this.next.isError()) {
15628 this.errors.push(new ParserError(this.next.toString(), this.input, this.locationText(), this.location));
15629 }
15630 this.advance();
15631 n = this.next;
15632 }
15633 }
15634 }
15635 class SimpleExpressionChecker {
15636 constructor() {
15637 this.errors = [];
15638 }
15639 visitImplicitReceiver(ast, context) { }
15640 visitThisReceiver(ast, context) { }
15641 visitInterpolation(ast, context) { }
15642 visitLiteralPrimitive(ast, context) { }
15643 visitPropertyRead(ast, context) { }
15644 visitPropertyWrite(ast, context) { }
15645 visitSafePropertyRead(ast, context) { }
15646 visitMethodCall(ast, context) { }
15647 visitSafeMethodCall(ast, context) { }
15648 visitFunctionCall(ast, context) { }
15649 visitLiteralArray(ast, context) {
15650 this.visitAll(ast.expressions, context);
15651 }
15652 visitLiteralMap(ast, context) {
15653 this.visitAll(ast.values, context);
15654 }
15655 visitUnary(ast, context) { }
15656 visitBinary(ast, context) { }
15657 visitPrefixNot(ast, context) { }
15658 visitNonNullAssert(ast, context) { }
15659 visitConditional(ast, context) { }
15660 visitPipe(ast, context) {
15661 this.errors.push('pipes');
15662 }
15663 visitKeyedRead(ast, context) { }
15664 visitKeyedWrite(ast, context) { }
15665 visitAll(asts, context) {
15666 return asts.map(node => node.visit(this, context));
15667 }
15668 visitChain(ast, context) { }
15669 visitQuote(ast, context) { }
15670 }
15671 /**
15672 * This class implements SimpleExpressionChecker used in View Engine and performs more strict checks
15673 * to make sure host bindings do not contain pipes. In View Engine, having pipes in host bindings is
15674 * not supported as well, but in some cases (like `!(value | async)`) the error is not triggered at
15675 * compile time. In order to preserve View Engine behavior, more strict checks are introduced for
15676 * Ivy mode only.
15677 */
15678 class IvySimpleExpressionChecker extends RecursiveAstVisitor {
15679 constructor() {
15680 super(...arguments);
15681 this.errors = [];
15682 }
15683 visitPipe() {
15684 this.errors.push('pipes');
15685 }
15686 }
15687
15688 /**
15689 * @license
15690 * Copyright Google LLC All Rights Reserved.
15691 *
15692 * Use of this source code is governed by an MIT-style license that can be
15693 * found in the LICENSE file at https://angular.io/license
15694 */
15695 // =================================================================================================
15696 // =================================================================================================
15697 // =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
15698 // =================================================================================================
15699 // =================================================================================================
15700 //
15701 // DO NOT EDIT THIS LIST OF SECURITY SENSITIVE PROPERTIES WITHOUT A SECURITY REVIEW!
15702 // Reach out to mprobst for details.
15703 //
15704 // =================================================================================================
15705 /** Map from tagName|propertyName to SecurityContext. Properties applying to all tags use '*'. */
15706 let _SECURITY_SCHEMA;
15707 function SECURITY_SCHEMA() {
15708 if (!_SECURITY_SCHEMA) {
15709 _SECURITY_SCHEMA = {};
15710 // Case is insignificant below, all element and attribute names are lower-cased for lookup.
15711 registerContext(SecurityContext.HTML, [
15712 'iframe|srcdoc',
15713 '*|innerHTML',
15714 '*|outerHTML',
15715 ]);
15716 registerContext(SecurityContext.STYLE, ['*|style']);
15717 // NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them.
15718 registerContext(SecurityContext.URL, [
15719 '*|formAction', 'area|href', 'area|ping', 'audio|src', 'a|href',
15720 'a|ping', 'blockquote|cite', 'body|background', 'del|cite', 'form|action',
15721 'img|src', 'img|srcset', 'input|src', 'ins|cite', 'q|cite',
15722 'source|src', 'source|srcset', 'track|src', 'video|poster', 'video|src',
15723 ]);
15724 registerContext(SecurityContext.RESOURCE_URL, [
15725 'applet|code',
15726 'applet|codebase',
15727 'base|href',
15728 'embed|src',
15729 'frame|src',
15730 'head|profile',
15731 'html|manifest',
15732 'iframe|src',
15733 'link|href',
15734 'media|src',
15735 'object|codebase',
15736 'object|data',
15737 'script|src',
15738 ]);
15739 }
15740 return _SECURITY_SCHEMA;
15741 }
15742 function registerContext(ctx, specs) {
15743 for (const spec of specs)
15744 _SECURITY_SCHEMA[spec.toLowerCase()] = ctx;
15745 }
15746
15747 /**
15748 * @license
15749 * Copyright Google LLC All Rights Reserved.
15750 *
15751 * Use of this source code is governed by an MIT-style license that can be
15752 * found in the LICENSE file at https://angular.io/license
15753 */
15754 class ElementSchemaRegistry {
15755 }
15756
15757 /**
15758 * @license
15759 * Copyright Google LLC All Rights Reserved.
15760 *
15761 * Use of this source code is governed by an MIT-style license that can be
15762 * found in the LICENSE file at https://angular.io/license
15763 */
15764 const BOOLEAN = 'boolean';
15765 const NUMBER = 'number';
15766 const STRING = 'string';
15767 const OBJECT = 'object';
15768 /**
15769 * This array represents the DOM schema. It encodes inheritance, properties, and events.
15770 *
15771 * ## Overview
15772 *
15773 * Each line represents one kind of element. The `element_inheritance` and properties are joined
15774 * using `element_inheritance|properties` syntax.
15775 *
15776 * ## Element Inheritance
15777 *
15778 * The `element_inheritance` can be further subdivided as `element1,element2,...^parentElement`.
15779 * Here the individual elements are separated by `,` (commas). Every element in the list
15780 * has identical properties.
15781 *
15782 * An `element` may inherit additional properties from `parentElement` If no `^parentElement` is
15783 * specified then `""` (blank) element is assumed.
15784 *
15785 * NOTE: The blank element inherits from root `[Element]` element, the super element of all
15786 * elements.
15787 *
15788 * NOTE an element prefix such as `:svg:` has no special meaning to the schema.
15789 *
15790 * ## Properties
15791 *
15792 * Each element has a set of properties separated by `,` (commas). Each property can be prefixed
15793 * by a special character designating its type:
15794 *
15795 * - (no prefix): property is a string.
15796 * - `*`: property represents an event.
15797 * - `!`: property is a boolean.
15798 * - `#`: property is a number.
15799 * - `%`: property is an object.
15800 *
15801 * ## Query
15802 *
15803 * The class creates an internal squas representation which allows to easily answer the query of
15804 * if a given property exist on a given element.
15805 *
15806 * NOTE: We don't yet support querying for types or events.
15807 * NOTE: This schema is auto extracted from `schema_extractor.ts` located in the test folder,
15808 * see dom_element_schema_registry_spec.ts
15809 */
15810 // =================================================================================================
15811 // =================================================================================================
15812 // =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
15813 // =================================================================================================
15814 // =================================================================================================
15815 //
15816 // DO NOT EDIT THIS DOM SCHEMA WITHOUT A SECURITY REVIEW!
15817 //
15818 // Newly added properties must be security reviewed and assigned an appropriate SecurityContext in
15819 // dom_security_schema.ts. Reach out to mprobst & rjamet for details.
15820 //
15821 // =================================================================================================
15822 const SCHEMA = [
15823 '[Element]|textContent,%classList,className,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*copy,*cut,*paste,*search,*selectstart,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerHTML,#scrollLeft,#scrollTop,slot' +
15824 /* added manually to avoid breaking changes */
15825 ',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored',
15826 '[HTMLElement]^[Element]|accessKey,contentEditable,dir,!draggable,!hidden,innerText,lang,*abort,*auxclick,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*cuechange,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*seeked,*seeking,*select,*show,*stalled,*submit,*suspend,*timeupdate,*toggle,*volumechange,*waiting,outerText,!spellcheck,%style,#tabIndex,title,!translate',
15827 'abbr,address,article,aside,b,bdi,bdo,cite,code,dd,dfn,dt,em,figcaption,figure,footer,header,i,kbd,main,mark,nav,noscript,rb,rp,rt,rtc,ruby,s,samp,section,small,strong,sub,sup,u,var,wbr^[HTMLElement]|accessKey,contentEditable,dir,!draggable,!hidden,innerText,lang,*abort,*auxclick,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*cuechange,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*seeked,*seeking,*select,*show,*stalled,*submit,*suspend,*timeupdate,*toggle,*volumechange,*waiting,outerText,!spellcheck,%style,#tabIndex,title,!translate',
15828 'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,src,%srcObject,#volume',
15829 ':svg:^[HTMLElement]|*abort,*auxclick,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*cuechange,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*seeked,*seeking,*select,*show,*stalled,*submit,*suspend,*timeupdate,*toggle,*volumechange,*waiting,%style,#tabIndex',
15830 ':svg:graphics^:svg:|',
15831 ':svg:animation^:svg:|*begin,*end,*repeat',
15832 ':svg:geometry^:svg:|',
15833 ':svg:componentTransferFunction^:svg:|',
15834 ':svg:gradient^:svg:|',
15835 ':svg:textContent^:svg:graphics|',
15836 ':svg:textPositioning^:svg:textContent|',
15837 'a^[HTMLElement]|charset,coords,download,hash,host,hostname,href,hreflang,name,password,pathname,ping,port,protocol,referrerPolicy,rel,rev,search,shape,target,text,type,username',
15838 'area^[HTMLElement]|alt,coords,download,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,referrerPolicy,rel,search,shape,target,username',
15839 'audio^media|',
15840 'br^[HTMLElement]|clear',
15841 'base^[HTMLElement]|href,target',
15842 'body^[HTMLElement]|aLink,background,bgColor,link,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,text,vLink',
15843 'button^[HTMLElement]|!autofocus,!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value',
15844 'canvas^[HTMLElement]|#height,#width',
15845 'content^[HTMLElement]|select',
15846 'dl^[HTMLElement]|!compact',
15847 'datalist^[HTMLElement]|',
15848 'details^[HTMLElement]|!open',
15849 'dialog^[HTMLElement]|!open,returnValue',
15850 'dir^[HTMLElement]|!compact',
15851 'div^[HTMLElement]|align',
15852 'embed^[HTMLElement]|align,height,name,src,type,width',
15853 'fieldset^[HTMLElement]|!disabled,name',
15854 'font^[HTMLElement]|color,face,size',
15855 'form^[HTMLElement]|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target',
15856 'frame^[HTMLElement]|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src',
15857 'frameset^[HTMLElement]|cols,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows',
15858 'hr^[HTMLElement]|align,color,!noShade,size,width',
15859 'head^[HTMLElement]|',
15860 'h1,h2,h3,h4,h5,h6^[HTMLElement]|align',
15861 'html^[HTMLElement]|version',
15862 'iframe^[HTMLElement]|align,!allowFullscreen,frameBorder,height,longDesc,marginHeight,marginWidth,name,referrerPolicy,%sandbox,scrolling,src,srcdoc,width',
15863 'img^[HTMLElement]|align,alt,border,%crossOrigin,#height,#hspace,!isMap,longDesc,lowsrc,name,referrerPolicy,sizes,src,srcset,useMap,#vspace,#width',
15864 'input^[HTMLElement]|accept,align,alt,autocapitalize,autocomplete,!autofocus,!checked,!defaultChecked,defaultValue,dirName,!disabled,%files,formAction,formEnctype,formMethod,!formNoValidate,formTarget,#height,!incremental,!indeterminate,max,#maxLength,min,#minLength,!multiple,name,pattern,placeholder,!readOnly,!required,selectionDirection,#selectionEnd,#selectionStart,#size,src,step,type,useMap,value,%valueAsDate,#valueAsNumber,#width',
15865 'li^[HTMLElement]|type,#value',
15866 'label^[HTMLElement]|htmlFor',
15867 'legend^[HTMLElement]|align',
15868 'link^[HTMLElement]|as,charset,%crossOrigin,!disabled,href,hreflang,integrity,media,referrerPolicy,rel,%relList,rev,%sizes,target,type',
15869 'map^[HTMLElement]|name',
15870 'marquee^[HTMLElement]|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width',
15871 'menu^[HTMLElement]|!compact',
15872 'meta^[HTMLElement]|content,httpEquiv,name,scheme',
15873 'meter^[HTMLElement]|#high,#low,#max,#min,#optimum,#value',
15874 'ins,del^[HTMLElement]|cite,dateTime',
15875 'ol^[HTMLElement]|!compact,!reversed,#start,type',
15876 'object^[HTMLElement]|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width',
15877 'optgroup^[HTMLElement]|!disabled,label',
15878 'option^[HTMLElement]|!defaultSelected,!disabled,label,!selected,text,value',
15879 'output^[HTMLElement]|defaultValue,%htmlFor,name,value',
15880 'p^[HTMLElement]|align',
15881 'param^[HTMLElement]|name,type,value,valueType',
15882 'picture^[HTMLElement]|',
15883 'pre^[HTMLElement]|#width',
15884 'progress^[HTMLElement]|#max,#value',
15885 'q,blockquote,cite^[HTMLElement]|',
15886 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,src,text,type',
15887 'select^[HTMLElement]|autocomplete,!autofocus,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value',
15888 'shadow^[HTMLElement]|',
15889 'slot^[HTMLElement]|name',
15890 'source^[HTMLElement]|media,sizes,src,srcset,type',
15891 'span^[HTMLElement]|',
15892 'style^[HTMLElement]|!disabled,media,type',
15893 'caption^[HTMLElement]|align',
15894 'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width',
15895 'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width',
15896 'table^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width',
15897 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign',
15898 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign',
15899 'template^[HTMLElement]|',
15900 'textarea^[HTMLElement]|autocapitalize,autocomplete,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap',
15901 'title^[HTMLElement]|text',
15902 'track^[HTMLElement]|!default,kind,label,src,srclang',
15903 'ul^[HTMLElement]|!compact,type',
15904 'unknown^[HTMLElement]|',
15905 'video^media|#height,poster,#width',
15906 ':svg:a^:svg:graphics|',
15907 ':svg:animate^:svg:animation|',
15908 ':svg:animateMotion^:svg:animation|',
15909 ':svg:animateTransform^:svg:animation|',
15910 ':svg:circle^:svg:geometry|',
15911 ':svg:clipPath^:svg:graphics|',
15912 ':svg:defs^:svg:graphics|',
15913 ':svg:desc^:svg:|',
15914 ':svg:discard^:svg:|',
15915 ':svg:ellipse^:svg:geometry|',
15916 ':svg:feBlend^:svg:|',
15917 ':svg:feColorMatrix^:svg:|',
15918 ':svg:feComponentTransfer^:svg:|',
15919 ':svg:feComposite^:svg:|',
15920 ':svg:feConvolveMatrix^:svg:|',
15921 ':svg:feDiffuseLighting^:svg:|',
15922 ':svg:feDisplacementMap^:svg:|',
15923 ':svg:feDistantLight^:svg:|',
15924 ':svg:feDropShadow^:svg:|',
15925 ':svg:feFlood^:svg:|',
15926 ':svg:feFuncA^:svg:componentTransferFunction|',
15927 ':svg:feFuncB^:svg:componentTransferFunction|',
15928 ':svg:feFuncG^:svg:componentTransferFunction|',
15929 ':svg:feFuncR^:svg:componentTransferFunction|',
15930 ':svg:feGaussianBlur^:svg:|',
15931 ':svg:feImage^:svg:|',
15932 ':svg:feMerge^:svg:|',
15933 ':svg:feMergeNode^:svg:|',
15934 ':svg:feMorphology^:svg:|',
15935 ':svg:feOffset^:svg:|',
15936 ':svg:fePointLight^:svg:|',
15937 ':svg:feSpecularLighting^:svg:|',
15938 ':svg:feSpotLight^:svg:|',
15939 ':svg:feTile^:svg:|',
15940 ':svg:feTurbulence^:svg:|',
15941 ':svg:filter^:svg:|',
15942 ':svg:foreignObject^:svg:graphics|',
15943 ':svg:g^:svg:graphics|',
15944 ':svg:image^:svg:graphics|',
15945 ':svg:line^:svg:geometry|',
15946 ':svg:linearGradient^:svg:gradient|',
15947 ':svg:mpath^:svg:|',
15948 ':svg:marker^:svg:|',
15949 ':svg:mask^:svg:|',
15950 ':svg:metadata^:svg:|',
15951 ':svg:path^:svg:geometry|',
15952 ':svg:pattern^:svg:|',
15953 ':svg:polygon^:svg:geometry|',
15954 ':svg:polyline^:svg:geometry|',
15955 ':svg:radialGradient^:svg:gradient|',
15956 ':svg:rect^:svg:geometry|',
15957 ':svg:svg^:svg:graphics|#currentScale,#zoomAndPan',
15958 ':svg:script^:svg:|type',
15959 ':svg:set^:svg:animation|',
15960 ':svg:stop^:svg:|',
15961 ':svg:style^:svg:|!disabled,media,title,type',
15962 ':svg:switch^:svg:graphics|',
15963 ':svg:symbol^:svg:|',
15964 ':svg:tspan^:svg:textPositioning|',
15965 ':svg:text^:svg:textPositioning|',
15966 ':svg:textPath^:svg:textContent|',
15967 ':svg:title^:svg:|',
15968 ':svg:use^:svg:graphics|',
15969 ':svg:view^:svg:|#zoomAndPan',
15970 'data^[HTMLElement]|value',
15971 'keygen^[HTMLElement]|!autofocus,challenge,!disabled,form,keytype,name',
15972 'menuitem^[HTMLElement]|type,label,icon,!disabled,!checked,radiogroup,!default',
15973 'summary^[HTMLElement]|',
15974 'time^[HTMLElement]|dateTime',
15975 ':svg:cursor^:svg:|',
15976 ];
15977 const _ATTR_TO_PROP = {
15978 'class': 'className',
15979 'for': 'htmlFor',
15980 'formaction': 'formAction',
15981 'innerHtml': 'innerHTML',
15982 'readonly': 'readOnly',
15983 'tabindex': 'tabIndex',
15984 };
15985 // Invert _ATTR_TO_PROP.
15986 const _PROP_TO_ATTR = Object.keys(_ATTR_TO_PROP).reduce((inverted, attr) => {
15987 inverted[_ATTR_TO_PROP[attr]] = attr;
15988 return inverted;
15989 }, {});
15990 class DomElementSchemaRegistry extends ElementSchemaRegistry {
15991 constructor() {
15992 super();
15993 this._schema = {};
15994 SCHEMA.forEach(encodedType => {
15995 const type = {};
15996 const [strType, strProperties] = encodedType.split('|');
15997 const properties = strProperties.split(',');
15998 const [typeNames, superName] = strType.split('^');
15999 typeNames.split(',').forEach(tag => this._schema[tag.toLowerCase()] = type);
16000 const superType = superName && this._schema[superName.toLowerCase()];
16001 if (superType) {
16002 Object.keys(superType).forEach((prop) => {
16003 type[prop] = superType[prop];
16004 });
16005 }
16006 properties.forEach((property) => {
16007 if (property.length > 0) {
16008 switch (property[0]) {
16009 case '*':
16010 // We don't yet support events.
16011 // If ever allowing to bind to events, GO THROUGH A SECURITY REVIEW, allowing events
16012 // will
16013 // almost certainly introduce bad XSS vulnerabilities.
16014 // type[property.substring(1)] = EVENT;
16015 break;
16016 case '!':
16017 type[property.substring(1)] = BOOLEAN;
16018 break;
16019 case '#':
16020 type[property.substring(1)] = NUMBER;
16021 break;
16022 case '%':
16023 type[property.substring(1)] = OBJECT;
16024 break;
16025 default:
16026 type[property] = STRING;
16027 }
16028 }
16029 });
16030 });
16031 }
16032 hasProperty(tagName, propName, schemaMetas) {
16033 if (schemaMetas.some((schema) => schema.name === NO_ERRORS_SCHEMA.name)) {
16034 return true;
16035 }
16036 if (tagName.indexOf('-') > -1) {
16037 if (isNgContainer(tagName) || isNgContent(tagName)) {
16038 return false;
16039 }
16040 if (schemaMetas.some((schema) => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
16041 // Can't tell now as we don't know which properties a custom element will get
16042 // once it is instantiated
16043 return true;
16044 }
16045 }
16046 const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown'];
16047 return !!elementProperties[propName];
16048 }
16049 hasElement(tagName, schemaMetas) {
16050 if (schemaMetas.some((schema) => schema.name === NO_ERRORS_SCHEMA.name)) {
16051 return true;
16052 }
16053 if (tagName.indexOf('-') > -1) {
16054 if (isNgContainer(tagName) || isNgContent(tagName)) {
16055 return true;
16056 }
16057 if (schemaMetas.some((schema) => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
16058 // Allow any custom elements
16059 return true;
16060 }
16061 }
16062 return !!this._schema[tagName.toLowerCase()];
16063 }
16064 /**
16065 * securityContext returns the security context for the given property on the given DOM tag.
16066 *
16067 * Tag and property name are statically known and cannot change at runtime, i.e. it is not
16068 * possible to bind a value into a changing attribute or tag name.
16069 *
16070 * The filtering is based on a list of allowed tags|attributes. All attributes in the schema
16071 * above are assumed to have the 'NONE' security context, i.e. that they are safe inert
16072 * string values. Only specific well known attack vectors are assigned their appropriate context.
16073 */
16074 securityContext(tagName, propName, isAttribute) {
16075 if (isAttribute) {
16076 // NB: For security purposes, use the mapped property name, not the attribute name.
16077 propName = this.getMappedPropName(propName);
16078 }
16079 // Make sure comparisons are case insensitive, so that case differences between attribute and
16080 // property names do not have a security impact.
16081 tagName = tagName.toLowerCase();
16082 propName = propName.toLowerCase();
16083 let ctx = SECURITY_SCHEMA()[tagName + '|' + propName];
16084 if (ctx) {
16085 return ctx;
16086 }
16087 ctx = SECURITY_SCHEMA()['*|' + propName];
16088 return ctx ? ctx : SecurityContext.NONE;
16089 }
16090 getMappedPropName(propName) {
16091 return _ATTR_TO_PROP[propName] || propName;
16092 }
16093 getDefaultComponentElementName() {
16094 return 'ng-component';
16095 }
16096 validateProperty(name) {
16097 if (name.toLowerCase().startsWith('on')) {
16098 const msg = `Binding to event property '${name}' is disallowed for security reasons, ` +
16099 `please use (${name.slice(2)})=...` +
16100 `\nIf '${name}' is a directive input, make sure the directive is imported by the` +
16101 ` current module.`;
16102 return { error: true, msg: msg };
16103 }
16104 else {
16105 return { error: false };
16106 }
16107 }
16108 validateAttribute(name) {
16109 if (name.toLowerCase().startsWith('on')) {
16110 const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
16111 `please use (${name.slice(2)})=...`;
16112 return { error: true, msg: msg };
16113 }
16114 else {
16115 return { error: false };
16116 }
16117 }
16118 allKnownElementNames() {
16119 return Object.keys(this._schema);
16120 }
16121 allKnownAttributesOfElement(tagName) {
16122 const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown'];
16123 // Convert properties to attributes.
16124 return Object.keys(elementProperties).map(prop => { var _a; return (_a = _PROP_TO_ATTR[prop]) !== null && _a !== void 0 ? _a : prop; });
16125 }
16126 normalizeAnimationStyleProperty(propName) {
16127 return dashCaseToCamelCase(propName);
16128 }
16129 normalizeAnimationStyleValue(camelCaseProp, userProvidedProp, val) {
16130 let unit = '';
16131 const strVal = val.toString().trim();
16132 let errorMsg = null;
16133 if (_isPixelDimensionStyle(camelCaseProp) && val !== 0 && val !== '0') {
16134 if (typeof val === 'number') {
16135 unit = 'px';
16136 }
16137 else {
16138 const valAndSuffixMatch = val.match(/^[+-]?[\d\.]+([a-z]*)$/);
16139 if (valAndSuffixMatch && valAndSuffixMatch[1].length == 0) {
16140 errorMsg = `Please provide a CSS unit value for ${userProvidedProp}:${val}`;
16141 }
16142 }
16143 }
16144 return { error: errorMsg, value: strVal + unit };
16145 }
16146 }
16147 function _isPixelDimensionStyle(prop) {
16148 switch (prop) {
16149 case 'width':
16150 case 'height':
16151 case 'minWidth':
16152 case 'minHeight':
16153 case 'maxWidth':
16154 case 'maxHeight':
16155 case 'left':
16156 case 'top':
16157 case 'bottom':
16158 case 'right':
16159 case 'fontSize':
16160 case 'outlineWidth':
16161 case 'outlineOffset':
16162 case 'paddingTop':
16163 case 'paddingLeft':
16164 case 'paddingBottom':
16165 case 'paddingRight':
16166 case 'marginTop':
16167 case 'marginLeft':
16168 case 'marginBottom':
16169 case 'marginRight':
16170 case 'borderRadius':
16171 case 'borderWidth':
16172 case 'borderTopWidth':
16173 case 'borderLeftWidth':
16174 case 'borderRightWidth':
16175 case 'borderBottomWidth':
16176 case 'textIndent':
16177 return true;
16178 default:
16179 return false;
16180 }
16181 }
16182
16183 /**
16184 * @license
16185 * Copyright Google LLC All Rights Reserved.
16186 *
16187 * Use of this source code is governed by an MIT-style license that can be
16188 * found in the LICENSE file at https://angular.io/license
16189 */
16190 /**
16191 * Set of tagName|propertyName corresponding to Trusted Types sinks. Properties applying to all
16192 * tags use '*'.
16193 *
16194 * Extracted from, and should be kept in sync with
16195 * https://w3c.github.io/webappsec-trusted-types/dist/spec/#integrations
16196 */
16197 const TRUSTED_TYPES_SINKS = new Set([
16198 // NOTE: All strings in this set *must* be lowercase!
16199 // TrustedHTML
16200 'iframe|srcdoc',
16201 '*|innerhtml',
16202 '*|outerhtml',
16203 // NB: no TrustedScript here, as the corresponding tags are stripped by the compiler.
16204 // TrustedScriptURL
16205 'embed|src',
16206 'object|codebase',
16207 'object|data',
16208 ]);
16209 /**
16210 * isTrustedTypesSink returns true if the given property on the given DOM tag is a Trusted Types
16211 * sink. In that case, use `ElementSchemaRegistry.securityContext` to determine which particular
16212 * Trusted Type is required for values passed to the sink:
16213 * - SecurityContext.HTML corresponds to TrustedHTML
16214 * - SecurityContext.RESOURCE_URL corresponds to TrustedScriptURL
16215 */
16216 function isTrustedTypesSink(tagName, propName) {
16217 // Make sure comparisons are case insensitive, so that case differences between attribute and
16218 // property names do not have a security impact.
16219 tagName = tagName.toLowerCase();
16220 propName = propName.toLowerCase();
16221 return TRUSTED_TYPES_SINKS.has(tagName + '|' + propName) ||
16222 TRUSTED_TYPES_SINKS.has('*|' + propName);
16223 }
16224
16225 /**
16226 * @license
16227 * Copyright Google LLC All Rights Reserved.
16228 *
16229 * Use of this source code is governed by an MIT-style license that can be
16230 * found in the LICENSE file at https://angular.io/license
16231 */
16232 const BIND_NAME_REGEXP = /^(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*)$/;
16233 // Group 1 = "bind-"
16234 const KW_BIND_IDX = 1;
16235 // Group 2 = "let-"
16236 const KW_LET_IDX = 2;
16237 // Group 3 = "ref-/#"
16238 const KW_REF_IDX = 3;
16239 // Group 4 = "on-"
16240 const KW_ON_IDX = 4;
16241 // Group 5 = "bindon-"
16242 const KW_BINDON_IDX = 5;
16243 // Group 6 = "@"
16244 const KW_AT_IDX = 6;
16245 // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@"
16246 const IDENT_KW_IDX = 7;
16247 const BINDING_DELIMS = {
16248 BANANA_BOX: { start: '[(', end: ')]' },
16249 PROPERTY: { start: '[', end: ']' },
16250 EVENT: { start: '(', end: ')' },
16251 };
16252 const TEMPLATE_ATTR_PREFIX$1 = '*';
16253 function htmlAstToRender3Ast(htmlNodes, bindingParser) {
16254 const transformer = new HtmlAstToIvyAst(bindingParser);
16255 const ivyNodes = visitAll$1(transformer, htmlNodes);
16256 // Errors might originate in either the binding parser or the html to ivy transformer
16257 const allErrors = bindingParser.errors.concat(transformer.errors);
16258 return {
16259 nodes: ivyNodes,
16260 errors: allErrors,
16261 styleUrls: transformer.styleUrls,
16262 styles: transformer.styles,
16263 ngContentSelectors: transformer.ngContentSelectors,
16264 };
16265 }
16266 class HtmlAstToIvyAst {
16267 constructor(bindingParser) {
16268 this.bindingParser = bindingParser;
16269 this.errors = [];
16270 this.styles = [];
16271 this.styleUrls = [];
16272 this.ngContentSelectors = [];
16273 this.inI18nBlock = false;
16274 }
16275 // HTML visitor
16276 visitElement(element) {
16277 const isI18nRootElement = isI18nRootNode(element.i18n);
16278 if (isI18nRootElement) {
16279 if (this.inI18nBlock) {
16280 this.reportError('Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.', element.sourceSpan);
16281 }
16282 this.inI18nBlock = true;
16283 }
16284 const preparsedElement = preparseElement(element);
16285 if (preparsedElement.type === PreparsedElementType.SCRIPT) {
16286 return null;
16287 }
16288 else if (preparsedElement.type === PreparsedElementType.STYLE) {
16289 const contents = textContents(element);
16290 if (contents !== null) {
16291 this.styles.push(contents);
16292 }
16293 return null;
16294 }
16295 else if (preparsedElement.type === PreparsedElementType.STYLESHEET &&
16296 isStyleUrlResolvable(preparsedElement.hrefAttr)) {
16297 this.styleUrls.push(preparsedElement.hrefAttr);
16298 return null;
16299 }
16300 // Whether the element is a `<ng-template>`
16301 const isTemplateElement = isNgTemplate(element.name);
16302 const parsedProperties = [];
16303 const boundEvents = [];
16304 const variables = [];
16305 const references = [];
16306 const attributes = [];
16307 const i18nAttrsMeta = {};
16308 const templateParsedProperties = [];
16309 const templateVariables = [];
16310 // Whether the element has any *-attribute
16311 let elementHasInlineTemplate = false;
16312 for (const attribute of element.attrs) {
16313 let hasBinding = false;
16314 const normalizedName = normalizeAttributeName(attribute.name);
16315 // `*attr` defines template bindings
16316 let isTemplateBinding = false;
16317 if (attribute.i18n) {
16318 i18nAttrsMeta[attribute.name] = attribute.i18n;
16319 }
16320 if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX$1)) {
16321 // *-attributes
16322 if (elementHasInlineTemplate) {
16323 this.reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attribute.sourceSpan);
16324 }
16325 isTemplateBinding = true;
16326 elementHasInlineTemplate = true;
16327 const templateValue = attribute.value;
16328 const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX$1.length);
16329 const parsedVariables = [];
16330 const absoluteValueOffset = attribute.valueSpan ?
16331 attribute.valueSpan.start.offset :
16332 // If there is no value span the attribute does not have a value, like `attr` in
16333 //`<div attr></div>`. In this case, point to one character beyond the last character of
16334 // the attribute name.
16335 attribute.sourceSpan.start.offset + attribute.name.length;
16336 this.bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables, true /* isIvyAst */);
16337 templateVariables.push(...parsedVariables.map(v => new Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan)));
16338 }
16339 else {
16340 // Check for variables, events, property bindings, interpolation
16341 hasBinding = this.parseAttribute(isTemplateElement, attribute, [], parsedProperties, boundEvents, variables, references);
16342 }
16343 if (!hasBinding && !isTemplateBinding) {
16344 // don't include the bindings as attributes as well in the AST
16345 attributes.push(this.visitAttribute(attribute));
16346 }
16347 }
16348 const children = visitAll$1(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children);
16349 let parsedElement;
16350 if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
16351 // `<ng-content>`
16352 if (element.children &&
16353 !element.children.every((node) => isEmptyTextNode(node) || isCommentNode(node))) {
16354 this.reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
16355 }
16356 const selector = preparsedElement.selectAttr;
16357 const attrs = element.attrs.map(attr => this.visitAttribute(attr));
16358 parsedElement = new Content(selector, attrs, element.sourceSpan, element.i18n);
16359 this.ngContentSelectors.push(selector);
16360 }
16361 else if (isTemplateElement) {
16362 // `<ng-template>`
16363 const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
16364 parsedElement = new Template(element.name, attributes, attrs.bound, boundEvents, [ /* no template attributes */], children, references, variables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
16365 }
16366 else {
16367 const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
16368 parsedElement = new Element(element.name, attributes, attrs.bound, boundEvents, children, references, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
16369 }
16370 if (elementHasInlineTemplate) {
16371 // If this node is an inline-template (e.g. has *ngFor) then we need to create a template
16372 // node that contains this node.
16373 // Moreover, if the node is an element, then we need to hoist its attributes to the template
16374 // node for matching against content projection selectors.
16375 const attrs = this.extractAttributes('ng-template', templateParsedProperties, i18nAttrsMeta);
16376 const templateAttrs = [];
16377 attrs.literal.forEach(attr => templateAttrs.push(attr));
16378 attrs.bound.forEach(attr => templateAttrs.push(attr));
16379 const hoistedAttrs = parsedElement instanceof Element ?
16380 {
16381 attributes: parsedElement.attributes,
16382 inputs: parsedElement.inputs,
16383 outputs: parsedElement.outputs,
16384 } :
16385 { attributes: [], inputs: [], outputs: [] };
16386 // For <ng-template>s with structural directives on them, avoid passing i18n information to
16387 // the wrapping template to prevent unnecessary i18n instructions from being generated. The
16388 // necessary i18n meta information will be extracted from child elements.
16389 const i18n = isTemplateElement && isI18nRootElement ? undefined : element.i18n;
16390 // TODO(pk): test for this case
16391 parsedElement = new Template(parsedElement.name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, templateAttrs, [parsedElement], [ /* no references */], templateVariables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, i18n);
16392 }
16393 if (isI18nRootElement) {
16394 this.inI18nBlock = false;
16395 }
16396 return parsedElement;
16397 }
16398 visitAttribute(attribute) {
16399 return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
16400 }
16401 visitText(text) {
16402 return this._visitTextWithInterpolation(text.value, text.sourceSpan, text.i18n);
16403 }
16404 visitExpansion(expansion) {
16405 if (!expansion.i18n) {
16406 // do not generate Icu in case it was created
16407 // outside of i18n block in a template
16408 return null;
16409 }
16410 if (!isI18nRootNode(expansion.i18n)) {
16411 throw new Error(`Invalid type "${expansion.i18n.constructor}" for "i18n" property of ${expansion.sourceSpan.toString()}. Expected a "Message"`);
16412 }
16413 const message = expansion.i18n;
16414 const vars = {};
16415 const placeholders = {};
16416 // extract VARs from ICUs - we process them separately while
16417 // assembling resulting message via goog.getMsg function, since
16418 // we need to pass them to top-level goog.getMsg call
16419 Object.keys(message.placeholders).forEach(key => {
16420 const value = message.placeholders[key];
16421 if (key.startsWith(I18N_ICU_VAR_PREFIX)) {
16422 // Currently when the `plural` or `select` keywords in an ICU contain trailing spaces (e.g.
16423 // `{count, select , ...}`), these spaces are also included into the key names in ICU vars
16424 // (e.g. "VAR_SELECT "). These trailing spaces are not desirable, since they will later be
16425 // converted into `_` symbols while normalizing placeholder names, which might lead to
16426 // mismatches at runtime (i.e. placeholder will not be replaced with the correct value).
16427 const formattedKey = key.trim();
16428 const ast = this.bindingParser.parseInterpolationExpression(value.text, value.sourceSpan);
16429 vars[formattedKey] = new BoundText(ast, value.sourceSpan);
16430 }
16431 else {
16432 placeholders[key] = this._visitTextWithInterpolation(value.text, value.sourceSpan);
16433 }
16434 });
16435 return new Icu(vars, placeholders, expansion.sourceSpan, message);
16436 }
16437 visitExpansionCase(expansionCase) {
16438 return null;
16439 }
16440 visitComment(comment) {
16441 return null;
16442 }
16443 // convert view engine `ParsedProperty` to a format suitable for IVY
16444 extractAttributes(elementName, properties, i18nPropsMeta) {
16445 const bound = [];
16446 const literal = [];
16447 properties.forEach(prop => {
16448 const i18n = i18nPropsMeta[prop.name];
16449 if (prop.isLiteral) {
16450 literal.push(new TextAttribute(prop.name, prop.expression.source || '', prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n));
16451 }
16452 else {
16453 // Note that validation is skipped and property mapping is disabled
16454 // due to the fact that we need to make sure a given prop is not an
16455 // input of a directive and directive matching happens at runtime.
16456 const bep = this.bindingParser.createBoundElementProperty(elementName, prop, /* skipValidation */ true, /* mapPropertyName */ false);
16457 bound.push(BoundAttribute.fromBoundElementProperty(bep, i18n));
16458 }
16459 });
16460 return { bound, literal };
16461 }
16462 parseAttribute(isTemplateElement, attribute, matchableAttributes, parsedProperties, boundEvents, variables, references) {
16463 const name = normalizeAttributeName(attribute.name);
16464 const value = attribute.value;
16465 const srcSpan = attribute.sourceSpan;
16466 const absoluteOffset = attribute.valueSpan ? attribute.valueSpan.start.offset : srcSpan.start.offset;
16467 function createKeySpan(srcSpan, prefix, identifier) {
16468 // We need to adjust the start location for the keySpan to account for the removed 'data-'
16469 // prefix from `normalizeAttributeName`.
16470 const normalizationAdjustment = attribute.name.length - name.length;
16471 const keySpanStart = srcSpan.start.moveBy(prefix.length + normalizationAdjustment);
16472 const keySpanEnd = keySpanStart.moveBy(identifier.length);
16473 return new ParseSourceSpan(keySpanStart, keySpanEnd, keySpanStart, identifier);
16474 }
16475 const bindParts = name.match(BIND_NAME_REGEXP);
16476 if (bindParts) {
16477 if (bindParts[KW_BIND_IDX] != null) {
16478 const identifier = bindParts[IDENT_KW_IDX];
16479 const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier);
16480 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16481 }
16482 else if (bindParts[KW_LET_IDX]) {
16483 if (isTemplateElement) {
16484 const identifier = bindParts[IDENT_KW_IDX];
16485 const keySpan = createKeySpan(srcSpan, bindParts[KW_LET_IDX], identifier);
16486 this.parseVariable(identifier, value, srcSpan, keySpan, attribute.valueSpan, variables);
16487 }
16488 else {
16489 this.reportError(`"let-" is only supported on ng-template elements.`, srcSpan);
16490 }
16491 }
16492 else if (bindParts[KW_REF_IDX]) {
16493 const identifier = bindParts[IDENT_KW_IDX];
16494 const keySpan = createKeySpan(srcSpan, bindParts[KW_REF_IDX], identifier);
16495 this.parseReference(identifier, value, srcSpan, keySpan, attribute.valueSpan, references);
16496 }
16497 else if (bindParts[KW_ON_IDX]) {
16498 const events = [];
16499 const identifier = bindParts[IDENT_KW_IDX];
16500 const keySpan = createKeySpan(srcSpan, bindParts[KW_ON_IDX], identifier);
16501 this.bindingParser.parseEvent(identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
16502 addEvents(events, boundEvents);
16503 }
16504 else if (bindParts[KW_BINDON_IDX]) {
16505 const identifier = bindParts[IDENT_KW_IDX];
16506 const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier);
16507 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16508 this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
16509 }
16510 else if (bindParts[KW_AT_IDX]) {
16511 const keySpan = createKeySpan(srcSpan, '', name);
16512 this.bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16513 }
16514 return true;
16515 }
16516 // We didn't see a kw-prefixed property binding, but we have not yet checked
16517 // for the []/()/[()] syntax.
16518 let delims = null;
16519 if (name.startsWith(BINDING_DELIMS.BANANA_BOX.start)) {
16520 delims = BINDING_DELIMS.BANANA_BOX;
16521 }
16522 else if (name.startsWith(BINDING_DELIMS.PROPERTY.start)) {
16523 delims = BINDING_DELIMS.PROPERTY;
16524 }
16525 else if (name.startsWith(BINDING_DELIMS.EVENT.start)) {
16526 delims = BINDING_DELIMS.EVENT;
16527 }
16528 if (delims !== null &&
16529 // NOTE: older versions of the parser would match a start/end delimited
16530 // binding iff the property name was terminated by the ending delimiter
16531 // and the identifier in the binding was non-empty.
16532 // TODO(ayazhafiz): update this to handle malformed bindings.
16533 name.endsWith(delims.end) && name.length > delims.start.length + delims.end.length) {
16534 const identifier = name.substring(delims.start.length, name.length - delims.end.length);
16535 const keySpan = createKeySpan(srcSpan, delims.start, identifier);
16536 if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {
16537 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16538 this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
16539 }
16540 else if (delims.start === BINDING_DELIMS.PROPERTY.start) {
16541 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16542 }
16543 else {
16544 const events = [];
16545 this.bindingParser.parseEvent(identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
16546 addEvents(events, boundEvents);
16547 }
16548 return true;
16549 }
16550 // No explicit binding found.
16551 const keySpan = createKeySpan(srcSpan, '' /* prefix */, name);
16552 const hasBinding = this.bindingParser.parsePropertyInterpolation(name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16553 return hasBinding;
16554 }
16555 _visitTextWithInterpolation(value, sourceSpan, i18n) {
16556 const valueNoNgsp = replaceNgsp(value);
16557 const expr = this.bindingParser.parseInterpolation(valueNoNgsp, sourceSpan);
16558 return expr ? new BoundText(expr, sourceSpan, i18n) : new Text(valueNoNgsp, sourceSpan);
16559 }
16560 parseVariable(identifier, value, sourceSpan, keySpan, valueSpan, variables) {
16561 if (identifier.indexOf('-') > -1) {
16562 this.reportError(`"-" is not allowed in variable names`, sourceSpan);
16563 }
16564 else if (identifier.length === 0) {
16565 this.reportError(`Variable does not have a name`, sourceSpan);
16566 }
16567 variables.push(new Variable(identifier, value, sourceSpan, keySpan, valueSpan));
16568 }
16569 parseReference(identifier, value, sourceSpan, keySpan, valueSpan, references) {
16570 if (identifier.indexOf('-') > -1) {
16571 this.reportError(`"-" is not allowed in reference names`, sourceSpan);
16572 }
16573 else if (identifier.length === 0) {
16574 this.reportError(`Reference does not have a name`, sourceSpan);
16575 }
16576 else if (references.some(reference => reference.name === identifier)) {
16577 this.reportError(`Reference "#${identifier}" is defined more than once`, sourceSpan);
16578 }
16579 references.push(new Reference(identifier, value, sourceSpan, keySpan, valueSpan));
16580 }
16581 parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) {
16582 const events = [];
16583 this.bindingParser.parseEvent(`${name}Change`, `${expression}=$event`, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
16584 addEvents(events, boundEvents);
16585 }
16586 reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
16587 this.errors.push(new ParseError(sourceSpan, message, level));
16588 }
16589 }
16590 class NonBindableVisitor {
16591 visitElement(ast) {
16592 const preparsedElement = preparseElement(ast);
16593 if (preparsedElement.type === PreparsedElementType.SCRIPT ||
16594 preparsedElement.type === PreparsedElementType.STYLE ||
16595 preparsedElement.type === PreparsedElementType.STYLESHEET) {
16596 // Skipping <script> for security reasons
16597 // Skipping <style> and stylesheets as we already processed them
16598 // in the StyleCompiler
16599 return null;
16600 }
16601 const children = visitAll$1(this, ast.children, null);
16602 return new Element(ast.name, visitAll$1(this, ast.attrs),
16603 /* inputs */ [], /* outputs */ [], children, /* references */ [], ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
16604 }
16605 visitComment(comment) {
16606 return null;
16607 }
16608 visitAttribute(attribute) {
16609 return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
16610 }
16611 visitText(text) {
16612 return new Text(text.value, text.sourceSpan);
16613 }
16614 visitExpansion(expansion) {
16615 return null;
16616 }
16617 visitExpansionCase(expansionCase) {
16618 return null;
16619 }
16620 }
16621 const NON_BINDABLE_VISITOR = new NonBindableVisitor();
16622 function normalizeAttributeName(attrName) {
16623 return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
16624 }
16625 function addEvents(events, boundEvents) {
16626 boundEvents.push(...events.map(e => BoundEvent.fromParsedEvent(e)));
16627 }
16628 function isEmptyTextNode(node) {
16629 return node instanceof Text$2 && node.value.trim().length == 0;
16630 }
16631 function isCommentNode(node) {
16632 return node instanceof Comment;
16633 }
16634 function textContents(node) {
16635 if (node.children.length !== 1 || !(node.children[0] instanceof Text$2)) {
16636 return null;
16637 }
16638 else {
16639 return node.children[0].value;
16640 }
16641 }
16642
16643 /**
16644 * @license
16645 * Copyright Google LLC All Rights Reserved.
16646 *
16647 * Use of this source code is governed by an MIT-style license that can be
16648 * found in the LICENSE file at https://angular.io/license
16649 */
16650 var TagType;
16651 (function (TagType) {
16652 TagType[TagType["ELEMENT"] = 0] = "ELEMENT";
16653 TagType[TagType["TEMPLATE"] = 1] = "TEMPLATE";
16654 })(TagType || (TagType = {}));
16655 /**
16656 * Generates an object that is used as a shared state between parent and all child contexts.
16657 */
16658 function setupRegistry() {
16659 return { getUniqueId: getSeqNumberGenerator(), icus: new Map() };
16660 }
16661 /**
16662 * I18nContext is a helper class which keeps track of all i18n-related aspects
16663 * (accumulates placeholders, bindings, etc) between i18nStart and i18nEnd instructions.
16664 *
16665 * When we enter a nested template, the top-level context is being passed down
16666 * to the nested component, which uses this context to generate a child instance
16667 * of I18nContext class (to handle nested template) and at the end, reconciles it back
16668 * with the parent context.
16669 *
16670 * @param index Instruction index of i18nStart, which initiates this context
16671 * @param ref Reference to a translation const that represents the content if thus context
16672 * @param level Nestng level defined for child contexts
16673 * @param templateIndex Instruction index of a template which this context belongs to
16674 * @param meta Meta information (id, meaning, description, etc) associated with this context
16675 */
16676 class I18nContext {
16677 constructor(index, ref, level = 0, templateIndex = null, meta, registry) {
16678 this.index = index;
16679 this.ref = ref;
16680 this.level = level;
16681 this.templateIndex = templateIndex;
16682 this.meta = meta;
16683 this.registry = registry;
16684 this.bindings = new Set();
16685 this.placeholders = new Map();
16686 this.isEmitted = false;
16687 this._unresolvedCtxCount = 0;
16688 this._registry = registry || setupRegistry();
16689 this.id = this._registry.getUniqueId();
16690 }
16691 appendTag(type, node, index, closed) {
16692 if (node.isVoid && closed) {
16693 return; // ignore "close" for void tags
16694 }
16695 const ph = node.isVoid || !closed ? node.startName : node.closeName;
16696 const content = { type, index, ctx: this.id, isVoid: node.isVoid, closed };
16697 updatePlaceholderMap(this.placeholders, ph, content);
16698 }
16699 get icus() {
16700 return this._registry.icus;
16701 }
16702 get isRoot() {
16703 return this.level === 0;
16704 }
16705 get isResolved() {
16706 return this._unresolvedCtxCount === 0;
16707 }
16708 getSerializedPlaceholders() {
16709 const result = new Map();
16710 this.placeholders.forEach((values, key) => result.set(key, values.map(serializePlaceholderValue)));
16711 return result;
16712 }
16713 // public API to accumulate i18n-related content
16714 appendBinding(binding) {
16715 this.bindings.add(binding);
16716 }
16717 appendIcu(name, ref) {
16718 updatePlaceholderMap(this._registry.icus, name, ref);
16719 }
16720 appendBoundText(node) {
16721 const phs = assembleBoundTextPlaceholders(node, this.bindings.size, this.id);
16722 phs.forEach((values, key) => updatePlaceholderMap(this.placeholders, key, ...values));
16723 }
16724 appendTemplate(node, index) {
16725 // add open and close tags at the same time,
16726 // since we process nested templates separately
16727 this.appendTag(TagType.TEMPLATE, node, index, false);
16728 this.appendTag(TagType.TEMPLATE, node, index, true);
16729 this._unresolvedCtxCount++;
16730 }
16731 appendElement(node, index, closed) {
16732 this.appendTag(TagType.ELEMENT, node, index, closed);
16733 }
16734 appendProjection(node, index) {
16735 // Add open and close tags at the same time, since `<ng-content>` has no content,
16736 // so when we come across `<ng-content>` we can register both open and close tags.
16737 // Note: runtime i18n logic doesn't distinguish `<ng-content>` tag placeholders and
16738 // regular element tag placeholders, so we generate element placeholders for both types.
16739 this.appendTag(TagType.ELEMENT, node, index, false);
16740 this.appendTag(TagType.ELEMENT, node, index, true);
16741 }
16742 /**
16743 * Generates an instance of a child context based on the root one,
16744 * when we enter a nested template within I18n section.
16745 *
16746 * @param index Instruction index of corresponding i18nStart, which initiates this context
16747 * @param templateIndex Instruction index of a template which this context belongs to
16748 * @param meta Meta information (id, meaning, description, etc) associated with this context
16749 *
16750 * @returns I18nContext instance
16751 */
16752 forkChildContext(index, templateIndex, meta) {
16753 return new I18nContext(index, this.ref, this.level + 1, templateIndex, meta, this._registry);
16754 }
16755 /**
16756 * Reconciles child context into parent one once the end of the i18n block is reached (i18nEnd).
16757 *
16758 * @param context Child I18nContext instance to be reconciled with parent context.
16759 */
16760 reconcileChildContext(context) {
16761 // set the right context id for open and close
16762 // template tags, so we can use it as sub-block ids
16763 ['start', 'close'].forEach((op) => {
16764 const key = context.meta[`${op}Name`];
16765 const phs = this.placeholders.get(key) || [];
16766 const tag = phs.find(findTemplateFn(this.id, context.templateIndex));
16767 if (tag) {
16768 tag.ctx = context.id;
16769 }
16770 });
16771 // reconcile placeholders
16772 const childPhs = context.placeholders;
16773 childPhs.forEach((values, key) => {
16774 const phs = this.placeholders.get(key);
16775 if (!phs) {
16776 this.placeholders.set(key, values);
16777 return;
16778 }
16779 // try to find matching template...
16780 const tmplIdx = phs.findIndex(findTemplateFn(context.id, context.templateIndex));
16781 if (tmplIdx >= 0) {
16782 // ... if found - replace it with nested template content
16783 const isCloseTag = key.startsWith('CLOSE');
16784 const isTemplateTag = key.endsWith('NG-TEMPLATE');
16785 if (isTemplateTag) {
16786 // current template's content is placed before or after
16787 // parent template tag, depending on the open/close atrribute
16788 phs.splice(tmplIdx + (isCloseTag ? 0 : 1), 0, ...values);
16789 }
16790 else {
16791 const idx = isCloseTag ? values.length - 1 : 0;
16792 values[idx].tmpl = phs[tmplIdx];
16793 phs.splice(tmplIdx, 1, ...values);
16794 }
16795 }
16796 else {
16797 // ... otherwise just append content to placeholder value
16798 phs.push(...values);
16799 }
16800 this.placeholders.set(key, phs);
16801 });
16802 this._unresolvedCtxCount--;
16803 }
16804 }
16805 //
16806 // Helper methods
16807 //
16808 function wrap(symbol, index, contextId, closed) {
16809 const state = closed ? '/' : '';
16810 return wrapI18nPlaceholder(`${state}${symbol}${index}`, contextId);
16811 }
16812 function wrapTag(symbol, { index, ctx, isVoid }, closed) {
16813 return isVoid ? wrap(symbol, index, ctx) + wrap(symbol, index, ctx, true) :
16814 wrap(symbol, index, ctx, closed);
16815 }
16816 function findTemplateFn(ctx, templateIndex) {
16817 return (token) => typeof token === 'object' && token.type === TagType.TEMPLATE &&
16818 token.index === templateIndex && token.ctx === ctx;
16819 }
16820 function serializePlaceholderValue(value) {
16821 const element = (data, closed) => wrapTag('#', data, closed);
16822 const template = (data, closed) => wrapTag('*', data, closed);
16823 switch (value.type) {
16824 case TagType.ELEMENT:
16825 // close element tag
16826 if (value.closed) {
16827 return element(value, true) + (value.tmpl ? template(value.tmpl, true) : '');
16828 }
16829 // open element tag that also initiates a template
16830 if (value.tmpl) {
16831 return template(value.tmpl) + element(value) +
16832 (value.isVoid ? template(value.tmpl, true) : '');
16833 }
16834 return element(value);
16835 case TagType.TEMPLATE:
16836 return template(value, value.closed);
16837 default:
16838 return value;
16839 }
16840 }
16841
16842 /**
16843 * @license
16844 * Copyright Google LLC All Rights Reserved.
16845 *
16846 * Use of this source code is governed by an MIT-style license that can be
16847 * found in the LICENSE file at https://angular.io/license
16848 */
16849 class IcuSerializerVisitor {
16850 visitText(text) {
16851 return text.value;
16852 }
16853 visitContainer(container) {
16854 return container.children.map(child => child.visit(this)).join('');
16855 }
16856 visitIcu(icu) {
16857 const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
16858 const result = `{${icu.expressionPlaceholder}, ${icu.type}, ${strCases.join(' ')}}`;
16859 return result;
16860 }
16861 visitTagPlaceholder(ph) {
16862 return ph.isVoid ?
16863 this.formatPh(ph.startName) :
16864 `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
16865 }
16866 visitPlaceholder(ph) {
16867 return this.formatPh(ph.name);
16868 }
16869 visitIcuPlaceholder(ph, context) {
16870 return this.formatPh(ph.name);
16871 }
16872 formatPh(value) {
16873 return `{${formatI18nPlaceholderName(value, /* useCamelCase */ false)}}`;
16874 }
16875 }
16876 const serializer = new IcuSerializerVisitor();
16877 function serializeIcuNode(icu) {
16878 return icu.visit(serializer);
16879 }
16880
16881 /**
16882 * @license
16883 * Copyright Google LLC All Rights Reserved.
16884 *
16885 * Use of this source code is governed by an MIT-style license that can be
16886 * found in the LICENSE file at https://angular.io/license
16887 */
16888 const TAG_TO_PLACEHOLDER_NAMES = {
16889 'A': 'LINK',
16890 'B': 'BOLD_TEXT',
16891 'BR': 'LINE_BREAK',
16892 'EM': 'EMPHASISED_TEXT',
16893 'H1': 'HEADING_LEVEL1',
16894 'H2': 'HEADING_LEVEL2',
16895 'H3': 'HEADING_LEVEL3',
16896 'H4': 'HEADING_LEVEL4',
16897 'H5': 'HEADING_LEVEL5',
16898 'H6': 'HEADING_LEVEL6',
16899 'HR': 'HORIZONTAL_RULE',
16900 'I': 'ITALIC_TEXT',
16901 'LI': 'LIST_ITEM',
16902 'LINK': 'MEDIA_LINK',
16903 'OL': 'ORDERED_LIST',
16904 'P': 'PARAGRAPH',
16905 'Q': 'QUOTATION',
16906 'S': 'STRIKETHROUGH_TEXT',
16907 'SMALL': 'SMALL_TEXT',
16908 'SUB': 'SUBSTRIPT',
16909 'SUP': 'SUPERSCRIPT',
16910 'TBODY': 'TABLE_BODY',
16911 'TD': 'TABLE_CELL',
16912 'TFOOT': 'TABLE_FOOTER',
16913 'TH': 'TABLE_HEADER_CELL',
16914 'THEAD': 'TABLE_HEADER',
16915 'TR': 'TABLE_ROW',
16916 'TT': 'MONOSPACED_TEXT',
16917 'U': 'UNDERLINED_TEXT',
16918 'UL': 'UNORDERED_LIST',
16919 };
16920 /**
16921 * Creates unique names for placeholder with different content.
16922 *
16923 * Returns the same placeholder name when the content is identical.
16924 */
16925 class PlaceholderRegistry {
16926 constructor() {
16927 // Count the occurrence of the base name top generate a unique name
16928 this._placeHolderNameCounts = {};
16929 // Maps signature to placeholder names
16930 this._signatureToName = {};
16931 }
16932 getStartTagPlaceholderName(tag, attrs, isVoid) {
16933 const signature = this._hashTag(tag, attrs, isVoid);
16934 if (this._signatureToName[signature]) {
16935 return this._signatureToName[signature];
16936 }
16937 const upperTag = tag.toUpperCase();
16938 const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
16939 const name = this._generateUniqueName(isVoid ? baseName : `START_${baseName}`);
16940 this._signatureToName[signature] = name;
16941 return name;
16942 }
16943 getCloseTagPlaceholderName(tag) {
16944 const signature = this._hashClosingTag(tag);
16945 if (this._signatureToName[signature]) {
16946 return this._signatureToName[signature];
16947 }
16948 const upperTag = tag.toUpperCase();
16949 const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
16950 const name = this._generateUniqueName(`CLOSE_${baseName}`);
16951 this._signatureToName[signature] = name;
16952 return name;
16953 }
16954 getPlaceholderName(name, content) {
16955 const upperName = name.toUpperCase();
16956 const signature = `PH: ${upperName}=${content}`;
16957 if (this._signatureToName[signature]) {
16958 return this._signatureToName[signature];
16959 }
16960 const uniqueName = this._generateUniqueName(upperName);
16961 this._signatureToName[signature] = uniqueName;
16962 return uniqueName;
16963 }
16964 getUniquePlaceholder(name) {
16965 return this._generateUniqueName(name.toUpperCase());
16966 }
16967 // Generate a hash for a tag - does not take attribute order into account
16968 _hashTag(tag, attrs, isVoid) {
16969 const start = `<${tag}`;
16970 const strAttrs = Object.keys(attrs).sort().map((name) => ` ${name}=${attrs[name]}`).join('');
16971 const end = isVoid ? '/>' : `></${tag}>`;
16972 return start + strAttrs + end;
16973 }
16974 _hashClosingTag(tag) {
16975 return this._hashTag(`/${tag}`, {}, false);
16976 }
16977 _generateUniqueName(base) {
16978 const seen = this._placeHolderNameCounts.hasOwnProperty(base);
16979 if (!seen) {
16980 this._placeHolderNameCounts[base] = 1;
16981 return base;
16982 }
16983 const id = this._placeHolderNameCounts[base];
16984 this._placeHolderNameCounts[base] = id + 1;
16985 return `${base}_${id}`;
16986 }
16987 }
16988
16989 /**
16990 * @license
16991 * Copyright Google LLC All Rights Reserved.
16992 *
16993 * Use of this source code is governed by an MIT-style license that can be
16994 * found in the LICENSE file at https://angular.io/license
16995 */
16996 const _expParser = new Parser$1(new Lexer());
16997 /**
16998 * Returns a function converting html nodes to an i18n Message given an interpolationConfig
16999 */
17000 function createI18nMessageFactory(interpolationConfig) {
17001 const visitor = new _I18nVisitor(_expParser, interpolationConfig);
17002 return (nodes, meaning, description, customId, visitNodeFn) => visitor.toI18nMessage(nodes, meaning, description, customId, visitNodeFn);
17003 }
17004 function noopVisitNodeFn(_html, i18n) {
17005 return i18n;
17006 }
17007 class _I18nVisitor {
17008 constructor(_expressionParser, _interpolationConfig) {
17009 this._expressionParser = _expressionParser;
17010 this._interpolationConfig = _interpolationConfig;
17011 }
17012 toI18nMessage(nodes, meaning = '', description = '', customId = '', visitNodeFn) {
17013 const context = {
17014 isIcu: nodes.length == 1 && nodes[0] instanceof Expansion,
17015 icuDepth: 0,
17016 placeholderRegistry: new PlaceholderRegistry(),
17017 placeholderToContent: {},
17018 placeholderToMessage: {},
17019 visitNodeFn: visitNodeFn || noopVisitNodeFn,
17020 };
17021 const i18nodes = visitAll$1(this, nodes, context);
17022 return new Message(i18nodes, context.placeholderToContent, context.placeholderToMessage, meaning, description, customId);
17023 }
17024 visitElement(el, context) {
17025 var _a;
17026 const children = visitAll$1(this, el.children, context);
17027 const attrs = {};
17028 el.attrs.forEach(attr => {
17029 // Do not visit the attributes, translatable ones are top-level ASTs
17030 attrs[attr.name] = attr.value;
17031 });
17032 const isVoid = getHtmlTagDefinition(el.name).isVoid;
17033 const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid);
17034 context.placeholderToContent[startPhName] = {
17035 text: el.startSourceSpan.toString(),
17036 sourceSpan: el.startSourceSpan,
17037 };
17038 let closePhName = '';
17039 if (!isVoid) {
17040 closePhName = context.placeholderRegistry.getCloseTagPlaceholderName(el.name);
17041 context.placeholderToContent[closePhName] = {
17042 text: `</${el.name}>`,
17043 sourceSpan: (_a = el.endSourceSpan) !== null && _a !== void 0 ? _a : el.sourceSpan,
17044 };
17045 }
17046 const node = new TagPlaceholder(el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
17047 return context.visitNodeFn(el, node);
17048 }
17049 visitAttribute(attribute, context) {
17050 const node = this._visitTextWithInterpolation(attribute.value, attribute.valueSpan || attribute.sourceSpan, context, attribute.i18n);
17051 return context.visitNodeFn(attribute, node);
17052 }
17053 visitText(text, context) {
17054 const node = this._visitTextWithInterpolation(text.value, text.sourceSpan, context, text.i18n);
17055 return context.visitNodeFn(text, node);
17056 }
17057 visitComment(comment, context) {
17058 return null;
17059 }
17060 visitExpansion(icu, context) {
17061 context.icuDepth++;
17062 const i18nIcuCases = {};
17063 const i18nIcu = new Icu$1(icu.switchValue, icu.type, i18nIcuCases, icu.sourceSpan);
17064 icu.cases.forEach((caze) => {
17065 i18nIcuCases[caze.value] = new Container(caze.expression.map((node) => node.visit(this, context)), caze.expSourceSpan);
17066 });
17067 context.icuDepth--;
17068 if (context.isIcu || context.icuDepth > 0) {
17069 // Returns an ICU node when:
17070 // - the message (vs a part of the message) is an ICU message, or
17071 // - the ICU message is nested.
17072 const expPh = context.placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`);
17073 i18nIcu.expressionPlaceholder = expPh;
17074 context.placeholderToContent[expPh] = {
17075 text: icu.switchValue,
17076 sourceSpan: icu.switchValueSourceSpan,
17077 };
17078 return context.visitNodeFn(icu, i18nIcu);
17079 }
17080 // Else returns a placeholder
17081 // ICU placeholders should not be replaced with their original content but with the their
17082 // translations.
17083 // TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
17084 const phName = context.placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
17085 context.placeholderToMessage[phName] = this.toI18nMessage([icu], '', '', '', undefined);
17086 const node = new IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
17087 return context.visitNodeFn(icu, node);
17088 }
17089 visitExpansionCase(_icuCase, _context) {
17090 throw new Error('Unreachable code');
17091 }
17092 /**
17093 * Split the, potentially interpolated, text up into text and placeholder pieces.
17094 *
17095 * @param text The potentially interpolated string to be split.
17096 * @param sourceSpan The span of the whole of the `text` string.
17097 * @param context The current context of the visitor, used to compute and store placeholders.
17098 * @param previousI18n Any i18n metadata associated with this `text` from a previous pass.
17099 */
17100 _visitTextWithInterpolation(text, sourceSpan, context, previousI18n) {
17101 const { strings, expressions } = this._expressionParser.splitInterpolation(text, sourceSpan.start.toString(), this._interpolationConfig);
17102 // No expressions, return a single text.
17103 if (expressions.length === 0) {
17104 return new Text$1(text, sourceSpan);
17105 }
17106 // Return a sequence of `Text` and `Placeholder` nodes grouped in a `Container`.
17107 const nodes = [];
17108 for (let i = 0; i < strings.length - 1; i++) {
17109 this._addText(nodes, strings[i], sourceSpan);
17110 this._addPlaceholder(nodes, context, expressions[i], sourceSpan);
17111 }
17112 // The last index contains no expression
17113 this._addText(nodes, strings[strings.length - 1], sourceSpan);
17114 // Whitespace removal may have invalidated the interpolation source-spans.
17115 reusePreviousSourceSpans(nodes, previousI18n);
17116 return new Container(nodes, sourceSpan);
17117 }
17118 /**
17119 * Create a new `Text` node from the `textPiece` and add it to the `nodes` collection.
17120 *
17121 * @param nodes The nodes to which the created `Text` node should be added.
17122 * @param textPiece The text and relative span information for this `Text` node.
17123 * @param interpolationSpan The span of the whole interpolated text.
17124 */
17125 _addText(nodes, textPiece, interpolationSpan) {
17126 if (textPiece.text.length > 0) {
17127 // No need to add empty strings
17128 const stringSpan = getOffsetSourceSpan(interpolationSpan, textPiece);
17129 nodes.push(new Text$1(textPiece.text, stringSpan));
17130 }
17131 }
17132 /**
17133 * Create a new `Placeholder` node from the `expression` and add it to the `nodes` collection.
17134 *
17135 * @param nodes The nodes to which the created `Text` node should be added.
17136 * @param context The current context of the visitor, used to compute and store placeholders.
17137 * @param expression The expression text and relative span information for this `Placeholder`
17138 * node.
17139 * @param interpolationSpan The span of the whole interpolated text.
17140 */
17141 _addPlaceholder(nodes, context, expression, interpolationSpan) {
17142 const sourceSpan = getOffsetSourceSpan(interpolationSpan, expression);
17143 const baseName = extractPlaceholderName(expression.text) || 'INTERPOLATION';
17144 const phName = context.placeholderRegistry.getPlaceholderName(baseName, expression.text);
17145 const text = this._interpolationConfig.start + expression.text + this._interpolationConfig.end;
17146 context.placeholderToContent[phName] = { text, sourceSpan };
17147 nodes.push(new Placeholder(expression.text, phName, sourceSpan));
17148 }
17149 }
17150 /**
17151 * Re-use the source-spans from `previousI18n` metadata for the `nodes`.
17152 *
17153 * Whitespace removal can invalidate the source-spans of interpolation nodes, so we
17154 * reuse the source-span stored from a previous pass before the whitespace was removed.
17155 *
17156 * @param nodes The `Text` and `Placeholder` nodes to be processed.
17157 * @param previousI18n Any i18n metadata for these `nodes` stored from a previous pass.
17158 */
17159 function reusePreviousSourceSpans(nodes, previousI18n) {
17160 if (previousI18n instanceof Message) {
17161 // The `previousI18n` is an i18n `Message`, so we are processing an `Attribute` with i18n
17162 // metadata. The `Message` should consist only of a single `Container` that contains the
17163 // parts (`Text` and `Placeholder`) to process.
17164 assertSingleContainerMessage(previousI18n);
17165 previousI18n = previousI18n.nodes[0];
17166 }
17167 if (previousI18n instanceof Container) {
17168 // The `previousI18n` is a `Container`, which means that this is a second i18n extraction pass
17169 // after whitespace has been removed from the AST ndoes.
17170 assertEquivalentNodes(previousI18n.children, nodes);
17171 // Reuse the source-spans from the first pass.
17172 for (let i = 0; i < nodes.length; i++) {
17173 nodes[i].sourceSpan = previousI18n.children[i].sourceSpan;
17174 }
17175 }
17176 }
17177 /**
17178 * Asserts that the `message` contains exactly one `Container` node.
17179 */
17180 function assertSingleContainerMessage(message) {
17181 const nodes = message.nodes;
17182 if (nodes.length !== 1 || !(nodes[0] instanceof Container)) {
17183 throw new Error('Unexpected previous i18n message - expected it to consist of only a single `Container` node.');
17184 }
17185 }
17186 /**
17187 * Asserts that the `previousNodes` and `node` collections have the same number of elements and
17188 * corresponding elements have the same node type.
17189 */
17190 function assertEquivalentNodes(previousNodes, nodes) {
17191 if (previousNodes.length !== nodes.length) {
17192 throw new Error('The number of i18n message children changed between first and second pass.');
17193 }
17194 if (previousNodes.some((node, i) => nodes[i].constructor !== node.constructor)) {
17195 throw new Error('The types of the i18n message children changed between first and second pass.');
17196 }
17197 }
17198 /**
17199 * Create a new `ParseSourceSpan` from the `sourceSpan`, offset by the `start` and `end` values.
17200 */
17201 function getOffsetSourceSpan(sourceSpan, { start, end }) {
17202 return new ParseSourceSpan(sourceSpan.fullStart.moveBy(start), sourceSpan.fullStart.moveBy(end));
17203 }
17204 const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g;
17205 function extractPlaceholderName(input) {
17206 return input.split(_CUSTOM_PH_EXP)[2];
17207 }
17208
17209 /**
17210 * @license
17211 * Copyright Google LLC All Rights Reserved.
17212 *
17213 * Use of this source code is governed by an MIT-style license that can be
17214 * found in the LICENSE file at https://angular.io/license
17215 */
17216 /**
17217 * An i18n error.
17218 */
17219 class I18nError extends ParseError {
17220 constructor(span, msg) {
17221 super(span, msg);
17222 }
17223 }
17224
17225 /**
17226 * @license
17227 * Copyright Google LLC All Rights Reserved.
17228 *
17229 * Use of this source code is governed by an MIT-style license that can be
17230 * found in the LICENSE file at https://angular.io/license
17231 */
17232 const setI18nRefs = (htmlNode, i18nNode) => {
17233 if (htmlNode instanceof NodeWithI18n) {
17234 if (i18nNode instanceof IcuPlaceholder && htmlNode.i18n instanceof Message) {
17235 // This html node represents an ICU but this is a second processing pass, and the legacy id
17236 // was computed in the previous pass and stored in the `i18n` property as a message.
17237 // We are about to wipe out that property so capture the previous message to be reused when
17238 // generating the message for this ICU later. See `_generateI18nMessage()`.
17239 i18nNode.previousMessage = htmlNode.i18n;
17240 }
17241 htmlNode.i18n = i18nNode;
17242 }
17243 return i18nNode;
17244 };
17245 /**
17246 * This visitor walks over HTML parse tree and converts information stored in
17247 * i18n-related attributes ("i18n" and "i18n-*") into i18n meta object that is
17248 * stored with other element's and attribute's information.
17249 */
17250 class I18nMetaVisitor {
17251 constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false) {
17252 this.interpolationConfig = interpolationConfig;
17253 this.keepI18nAttrs = keepI18nAttrs;
17254 this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
17255 // whether visited nodes contain i18n information
17256 this.hasI18nMeta = false;
17257 this._errors = [];
17258 // i18n message generation factory
17259 this._createI18nMessage = createI18nMessageFactory(this.interpolationConfig);
17260 }
17261 _generateI18nMessage(nodes, meta = '', visitNodeFn) {
17262 const { meaning, description, customId } = this._parseMetadata(meta);
17263 const message = this._createI18nMessage(nodes, meaning, description, customId, visitNodeFn);
17264 this._setMessageId(message, meta);
17265 this._setLegacyIds(message, meta);
17266 return message;
17267 }
17268 visitAllWithErrors(nodes) {
17269 const result = nodes.map(node => node.visit(this, null));
17270 return new ParseTreeResult(result, this._errors);
17271 }
17272 visitElement(element) {
17273 if (hasI18nAttrs(element)) {
17274 this.hasI18nMeta = true;
17275 const attrs = [];
17276 const attrsMeta = {};
17277 for (const attr of element.attrs) {
17278 if (attr.name === I18N_ATTR) {
17279 // root 'i18n' node attribute
17280 const i18n = element.i18n || attr.value;
17281 const message = this._generateI18nMessage(element.children, i18n, setI18nRefs);
17282 // do not assign empty i18n meta
17283 if (message.nodes.length) {
17284 element.i18n = message;
17285 }
17286 }
17287 else if (attr.name.startsWith(I18N_ATTR_PREFIX)) {
17288 // 'i18n-*' attributes
17289 const name = attr.name.slice(I18N_ATTR_PREFIX.length);
17290 if (isTrustedTypesSink(element.name, name)) {
17291 this._reportError(attr, `Translating attribute '${name}' is disallowed for security reasons.`);
17292 }
17293 else {
17294 attrsMeta[name] = attr.value;
17295 }
17296 }
17297 else {
17298 // non-i18n attributes
17299 attrs.push(attr);
17300 }
17301 }
17302 // set i18n meta for attributes
17303 if (Object.keys(attrsMeta).length) {
17304 for (const attr of attrs) {
17305 const meta = attrsMeta[attr.name];
17306 // do not create translation for empty attributes
17307 if (meta !== undefined && attr.value) {
17308 attr.i18n = this._generateI18nMessage([attr], attr.i18n || meta);
17309 }
17310 }
17311 }
17312 if (!this.keepI18nAttrs) {
17313 // update element's attributes,
17314 // keeping only non-i18n related ones
17315 element.attrs = attrs;
17316 }
17317 }
17318 visitAll$1(this, element.children, element.i18n);
17319 return element;
17320 }
17321 visitExpansion(expansion, currentMessage) {
17322 let message;
17323 const meta = expansion.i18n;
17324 this.hasI18nMeta = true;
17325 if (meta instanceof IcuPlaceholder) {
17326 // set ICU placeholder name (e.g. "ICU_1"),
17327 // generated while processing root element contents,
17328 // so we can reference it when we output translation
17329 const name = meta.name;
17330 message = this._generateI18nMessage([expansion], meta);
17331 const icu = icuFromI18nMessage(message);
17332 icu.name = name;
17333 }
17334 else {
17335 // ICU is a top level message, try to use metadata from container element if provided via
17336 // `context` argument. Note: context may not be available for standalone ICUs (without
17337 // wrapping element), so fallback to ICU metadata in this case.
17338 message = this._generateI18nMessage([expansion], currentMessage || meta);
17339 }
17340 expansion.i18n = message;
17341 return expansion;
17342 }
17343 visitText(text) {
17344 return text;
17345 }
17346 visitAttribute(attribute) {
17347 return attribute;
17348 }
17349 visitComment(comment) {
17350 return comment;
17351 }
17352 visitExpansionCase(expansionCase) {
17353 return expansionCase;
17354 }
17355 /**
17356 * Parse the general form `meta` passed into extract the explicit metadata needed to create a
17357 * `Message`.
17358 *
17359 * There are three possibilities for the `meta` variable
17360 * 1) a string from an `i18n` template attribute: parse it to extract the metadata values.
17361 * 2) a `Message` from a previous processing pass: reuse the metadata values in the message.
17362 * 4) other: ignore this and just process the message metadata as normal
17363 *
17364 * @param meta the bucket that holds information about the message
17365 * @returns the parsed metadata.
17366 */
17367 _parseMetadata(meta) {
17368 return typeof meta === 'string' ? parseI18nMeta(meta) :
17369 meta instanceof Message ? meta : {};
17370 }
17371 /**
17372 * Generate (or restore) message id if not specified already.
17373 */
17374 _setMessageId(message, meta) {
17375 if (!message.id) {
17376 message.id = meta instanceof Message && meta.id || decimalDigest(message);
17377 }
17378 }
17379 /**
17380 * Update the `message` with a `legacyId` if necessary.
17381 *
17382 * @param message the message whose legacy id should be set
17383 * @param meta information about the message being processed
17384 */
17385 _setLegacyIds(message, meta) {
17386 if (this.enableI18nLegacyMessageIdFormat) {
17387 message.legacyIds = [computeDigest(message), computeDecimalDigest(message)];
17388 }
17389 else if (typeof meta !== 'string') {
17390 // This occurs if we are doing the 2nd pass after whitespace removal (see `parseTemplate()` in
17391 // `packages/compiler/src/render3/view/template.ts`).
17392 // In that case we want to reuse the legacy message generated in the 1st pass (see
17393 // `setI18nRefs()`).
17394 const previousMessage = meta instanceof Message ?
17395 meta :
17396 meta instanceof IcuPlaceholder ? meta.previousMessage : undefined;
17397 message.legacyIds = previousMessage ? previousMessage.legacyIds : [];
17398 }
17399 }
17400 _reportError(node, msg) {
17401 this._errors.push(new I18nError(node.sourceSpan, msg));
17402 }
17403 }
17404 /** I18n separators for metadata **/
17405 const I18N_MEANING_SEPARATOR = '|';
17406 const I18N_ID_SEPARATOR = '@@';
17407 /**
17408 * Parses i18n metas like:
17409 * - "@@id",
17410 * - "description[@@id]",
17411 * - "meaning|description[@@id]"
17412 * and returns an object with parsed output.
17413 *
17414 * @param meta String that represents i18n meta
17415 * @returns Object with id, meaning and description fields
17416 */
17417 function parseI18nMeta(meta = '') {
17418 let customId;
17419 let meaning;
17420 let description;
17421 meta = meta.trim();
17422 if (meta) {
17423 const idIndex = meta.indexOf(I18N_ID_SEPARATOR);
17424 const descIndex = meta.indexOf(I18N_MEANING_SEPARATOR);
17425 let meaningAndDesc;
17426 [meaningAndDesc, customId] =
17427 (idIndex > -1) ? [meta.slice(0, idIndex), meta.slice(idIndex + 2)] : [meta, ''];
17428 [meaning, description] = (descIndex > -1) ?
17429 [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] :
17430 ['', meaningAndDesc];
17431 }
17432 return { customId, meaning, description };
17433 }
17434 // Converts i18n meta information for a message (id, description, meaning)
17435 // to a JsDoc statement formatted as expected by the Closure compiler.
17436 function i18nMetaToJSDoc(meta) {
17437 const tags = [];
17438 if (meta.description) {
17439 tags.push({ tagName: "desc" /* Desc */, text: meta.description });
17440 }
17441 if (meta.meaning) {
17442 tags.push({ tagName: "meaning" /* Meaning */, text: meta.meaning });
17443 }
17444 return tags.length == 0 ? null : jsDocComment(tags);
17445 }
17446
17447 /** Closure uses `goog.getMsg(message)` to lookup translations */
17448 const GOOG_GET_MSG = 'goog.getMsg';
17449 function createGoogleGetMsgStatements(variable$1, message, closureVar, params) {
17450 const messageString = serializeI18nMessageForGetMsg(message);
17451 const args = [literal(messageString)];
17452 if (Object.keys(params).length) {
17453 args.push(mapLiteral(params, true));
17454 }
17455 // /**
17456 // * @desc description of message
17457 // * @meaning meaning of message
17458 // */
17459 // const MSG_... = goog.getMsg(..);
17460 // I18N_X = MSG_...;
17461 const googGetMsgStmt = closureVar.set(variable(GOOG_GET_MSG).callFn(args)).toConstDecl();
17462 const metaComment = i18nMetaToJSDoc(message);
17463 if (metaComment !== null) {
17464 googGetMsgStmt.addLeadingComment(metaComment);
17465 }
17466 const i18nAssignmentStmt = new ExpressionStatement(variable$1.set(closureVar));
17467 return [googGetMsgStmt, i18nAssignmentStmt];
17468 }
17469 /**
17470 * This visitor walks over i18n tree and generates its string representation, including ICUs and
17471 * placeholders in `{$placeholder}` (for plain messages) or `{PLACEHOLDER}` (inside ICUs) format.
17472 */
17473 class GetMsgSerializerVisitor {
17474 formatPh(value) {
17475 return `{$${formatI18nPlaceholderName(value)}}`;
17476 }
17477 visitText(text) {
17478 return text.value;
17479 }
17480 visitContainer(container) {
17481 return container.children.map(child => child.visit(this)).join('');
17482 }
17483 visitIcu(icu) {
17484 return serializeIcuNode(icu);
17485 }
17486 visitTagPlaceholder(ph) {
17487 return ph.isVoid ?
17488 this.formatPh(ph.startName) :
17489 `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
17490 }
17491 visitPlaceholder(ph) {
17492 return this.formatPh(ph.name);
17493 }
17494 visitIcuPlaceholder(ph, context) {
17495 return this.formatPh(ph.name);
17496 }
17497 }
17498 const serializerVisitor$1 = new GetMsgSerializerVisitor();
17499 function serializeI18nMessageForGetMsg(message) {
17500 return message.nodes.map(node => node.visit(serializerVisitor$1, null)).join('');
17501 }
17502
17503 function createLocalizeStatements(variable, message, params) {
17504 const { messageParts, placeHolders } = serializeI18nMessageForLocalize(message);
17505 const sourceSpan = getSourceSpan(message);
17506 const expressions = placeHolders.map(ph => params[ph.text]);
17507 const localizedString$1 = localizedString(message, messageParts, placeHolders, expressions, sourceSpan);
17508 const variableInitialization = variable.set(localizedString$1);
17509 return [new ExpressionStatement(variableInitialization)];
17510 }
17511 /**
17512 * This visitor walks over an i18n tree, capturing literal strings and placeholders.
17513 *
17514 * The result can be used for generating the `$localize` tagged template literals.
17515 */
17516 class LocalizeSerializerVisitor {
17517 visitText(text, context) {
17518 if (context[context.length - 1] instanceof LiteralPiece) {
17519 // Two literal pieces in a row means that there was some comment node in-between.
17520 context[context.length - 1].text += text.value;
17521 }
17522 else {
17523 context.push(new LiteralPiece(text.value, text.sourceSpan));
17524 }
17525 }
17526 visitContainer(container, context) {
17527 container.children.forEach(child => child.visit(this, context));
17528 }
17529 visitIcu(icu, context) {
17530 context.push(new LiteralPiece(serializeIcuNode(icu), icu.sourceSpan));
17531 }
17532 visitTagPlaceholder(ph, context) {
17533 var _a, _b;
17534 context.push(this.createPlaceholderPiece(ph.startName, (_a = ph.startSourceSpan) !== null && _a !== void 0 ? _a : ph.sourceSpan));
17535 if (!ph.isVoid) {
17536 ph.children.forEach(child => child.visit(this, context));
17537 context.push(this.createPlaceholderPiece(ph.closeName, (_b = ph.endSourceSpan) !== null && _b !== void 0 ? _b : ph.sourceSpan));
17538 }
17539 }
17540 visitPlaceholder(ph, context) {
17541 context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
17542 }
17543 visitIcuPlaceholder(ph, context) {
17544 context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
17545 }
17546 createPlaceholderPiece(name, sourceSpan) {
17547 return new PlaceholderPiece(formatI18nPlaceholderName(name, /* useCamelCase */ false), sourceSpan);
17548 }
17549 }
17550 const serializerVisitor$2 = new LocalizeSerializerVisitor();
17551 /**
17552 * Serialize an i18n message into two arrays: messageParts and placeholders.
17553 *
17554 * These arrays will be used to generate `$localize` tagged template literals.
17555 *
17556 * @param message The message to be serialized.
17557 * @returns an object containing the messageParts and placeholders.
17558 */
17559 function serializeI18nMessageForLocalize(message) {
17560 const pieces = [];
17561 message.nodes.forEach(node => node.visit(serializerVisitor$2, pieces));
17562 return processMessagePieces(pieces);
17563 }
17564 function getSourceSpan(message) {
17565 const startNode = message.nodes[0];
17566 const endNode = message.nodes[message.nodes.length - 1];
17567 return new ParseSourceSpan(startNode.sourceSpan.start, endNode.sourceSpan.end, startNode.sourceSpan.fullStart, startNode.sourceSpan.details);
17568 }
17569 /**
17570 * Convert the list of serialized MessagePieces into two arrays.
17571 *
17572 * One contains the literal string pieces and the other the placeholders that will be replaced by
17573 * expressions when rendering `$localize` tagged template literals.
17574 *
17575 * @param pieces The pieces to process.
17576 * @returns an object containing the messageParts and placeholders.
17577 */
17578 function processMessagePieces(pieces) {
17579 const messageParts = [];
17580 const placeHolders = [];
17581 if (pieces[0] instanceof PlaceholderPiece) {
17582 // The first piece was a placeholder so we need to add an initial empty message part.
17583 messageParts.push(createEmptyMessagePart(pieces[0].sourceSpan.start));
17584 }
17585 for (let i = 0; i < pieces.length; i++) {
17586 const part = pieces[i];
17587 if (part instanceof LiteralPiece) {
17588 messageParts.push(part);
17589 }
17590 else {
17591 placeHolders.push(part);
17592 if (pieces[i - 1] instanceof PlaceholderPiece) {
17593 // There were two placeholders in a row, so we need to add an empty message part.
17594 messageParts.push(createEmptyMessagePart(pieces[i - 1].sourceSpan.end));
17595 }
17596 }
17597 }
17598 if (pieces[pieces.length - 1] instanceof PlaceholderPiece) {
17599 // The last piece was a placeholder so we need to add a final empty message part.
17600 messageParts.push(createEmptyMessagePart(pieces[pieces.length - 1].sourceSpan.end));
17601 }
17602 return { messageParts, placeHolders };
17603 }
17604 function createEmptyMessagePart(location) {
17605 return new LiteralPiece('', new ParseSourceSpan(location, location));
17606 }
17607
17608 /**
17609 * @license
17610 * Copyright Google LLC All Rights Reserved.
17611 *
17612 * Use of this source code is governed by an MIT-style license that can be
17613 * found in the LICENSE file at https://angular.io/license
17614 */
17615 // Selector attribute name of `<ng-content>`
17616 const NG_CONTENT_SELECT_ATTR$1 = 'select';
17617 // Attribute name of `ngProjectAs`.
17618 const NG_PROJECT_AS_ATTR_NAME = 'ngProjectAs';
17619 // Global symbols available only inside event bindings.
17620 const EVENT_BINDING_SCOPE_GLOBALS = new Set(['$event']);
17621 // List of supported global targets for event listeners
17622 const GLOBAL_TARGET_RESOLVERS = new Map([['window', Identifiers$1.resolveWindow], ['document', Identifiers$1.resolveDocument], ['body', Identifiers$1.resolveBody]]);
17623 const LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t'];
17624 // if (rf & flags) { .. }
17625 function renderFlagCheckIfStmt(flags, statements) {
17626 return ifStmt(variable(RENDER_FLAGS).bitwiseAnd(literal(flags), null, false), statements);
17627 }
17628 function prepareEventListenerParameters(eventAst, handlerName = null, scope = null) {
17629 const { type, name, target, phase, handler } = eventAst;
17630 if (target && !GLOBAL_TARGET_RESOLVERS.has(target)) {
17631 throw new Error(`Unexpected global target '${target}' defined for '${name}' event.
17632 Supported list of global targets: ${Array.from(GLOBAL_TARGET_RESOLVERS.keys())}.`);
17633 }
17634 const eventArgumentName = '$event';
17635 const implicitReceiverAccesses = new Set();
17636 const implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ?
17637 variable(CONTEXT_NAME) :
17638 scope.getOrCreateSharedContextVar(0);
17639 const bindingExpr = convertActionBinding(scope, implicitReceiverExpr, handler, 'b', () => error('Unexpected interpolation'), eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
17640 const statements = [];
17641 if (scope) {
17642 statements.push(...scope.restoreViewStatement());
17643 statements.push(...scope.variableDeclarations());
17644 }
17645 statements.push(...bindingExpr.render3Stmts);
17646 const eventName = type === 1 /* Animation */ ? prepareSyntheticListenerName(name, phase) : name;
17647 const fnName = handlerName && sanitizeIdentifier(handlerName);
17648 const fnArgs = [];
17649 if (implicitReceiverAccesses.has(eventArgumentName)) {
17650 fnArgs.push(new FnParam(eventArgumentName, DYNAMIC_TYPE));
17651 }
17652 const handlerFn = fn(fnArgs, statements, INFERRED_TYPE, null, fnName);
17653 const params = [literal(eventName), handlerFn];
17654 if (target) {
17655 params.push(literal(false), // `useCapture` flag, defaults to `false`
17656 importExpr(GLOBAL_TARGET_RESOLVERS.get(target)));
17657 }
17658 return params;
17659 }
17660 function createComponentDefConsts() {
17661 return {
17662 prepareStatements: [],
17663 constExpressions: [],
17664 i18nVarRefsCache: new Map(),
17665 };
17666 }
17667 class TemplateDefinitionBuilder {
17668 constructor(constantPool, parentBindingScope, level = 0, contextName, i18nContext, templateIndex, templateName, directiveMatcher, directives, pipeTypeByName, pipes, _namespace, relativeContextFilePath, i18nUseExternalIds, _constants = createComponentDefConsts()) {
17669 this.constantPool = constantPool;
17670 this.level = level;
17671 this.contextName = contextName;
17672 this.i18nContext = i18nContext;
17673 this.templateIndex = templateIndex;
17674 this.templateName = templateName;
17675 this.directiveMatcher = directiveMatcher;
17676 this.directives = directives;
17677 this.pipeTypeByName = pipeTypeByName;
17678 this.pipes = pipes;
17679 this._namespace = _namespace;
17680 this.i18nUseExternalIds = i18nUseExternalIds;
17681 this._constants = _constants;
17682 this._dataIndex = 0;
17683 this._bindingContext = 0;
17684 this._prefixCode = [];
17685 /**
17686 * List of callbacks to generate creation mode instructions. We store them here as we process
17687 * the template so bindings in listeners are resolved only once all nodes have been visited.
17688 * This ensures all local refs and context variables are available for matching.
17689 */
17690 this._creationCodeFns = [];
17691 /**
17692 * List of callbacks to generate update mode instructions. We store them here as we process
17693 * the template so bindings are resolved only once all nodes have been visited. This ensures
17694 * all local refs and context variables are available for matching.
17695 */
17696 this._updateCodeFns = [];
17697 /** Index of the currently-selected node. */
17698 this._currentIndex = 0;
17699 /** Temporary variable declarations generated from visiting pipes, literals, etc. */
17700 this._tempVariables = [];
17701 /**
17702 * List of callbacks to build nested templates. Nested templates must not be visited until
17703 * after the parent template has finished visiting all of its nodes. This ensures that all
17704 * local ref bindings in nested templates are able to find local ref values if the refs
17705 * are defined after the template declaration.
17706 */
17707 this._nestedTemplateFns = [];
17708 this._unsupported = unsupported;
17709 // i18n context local to this template
17710 this.i18n = null;
17711 // Number of slots to reserve for pureFunctions
17712 this._pureFunctionSlots = 0;
17713 // Number of binding slots
17714 this._bindingSlots = 0;
17715 // Projection slots found in the template. Projection slots can distribute projected
17716 // nodes based on a selector, or can just use the wildcard selector to match
17717 // all nodes which aren't matching any selector.
17718 this._ngContentReservedSlots = [];
17719 // Number of non-default selectors found in all parent templates of this template. We need to
17720 // track it to properly adjust projection slot index in the `projection` instruction.
17721 this._ngContentSelectorsOffset = 0;
17722 // Expression that should be used as implicit receiver when converting template
17723 // expressions to output AST.
17724 this._implicitReceiverExpr = null;
17725 // These should be handled in the template or element directly.
17726 this.visitReference = invalid$1;
17727 this.visitVariable = invalid$1;
17728 this.visitTextAttribute = invalid$1;
17729 this.visitBoundAttribute = invalid$1;
17730 this.visitBoundEvent = invalid$1;
17731 this._bindingScope = parentBindingScope.nestedScope(level);
17732 // Turn the relative context file path into an identifier by replacing non-alphanumeric
17733 // characters with underscores.
17734 this.fileBasedI18nSuffix = relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_') + '_';
17735 this._valueConverter = new ValueConverter(constantPool, () => this.allocateDataSlot(), (numSlots) => this.allocatePureFunctionSlots(numSlots), (name, localName, slot, value) => {
17736 const pipeType = pipeTypeByName.get(name);
17737 if (pipeType) {
17738 this.pipes.add(pipeType);
17739 }
17740 this._bindingScope.set(this.level, localName, value);
17741 this.creationInstruction(null, Identifiers$1.pipe, [literal(slot), literal(name)]);
17742 });
17743 }
17744 buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n) {
17745 this._ngContentSelectorsOffset = ngContentSelectorsOffset;
17746 if (this._namespace !== Identifiers$1.namespaceHTML) {
17747 this.creationInstruction(null, this._namespace);
17748 }
17749 // Create variable bindings
17750 variables.forEach(v => this.registerContextVariables(v));
17751 // Initiate i18n context in case:
17752 // - this template has parent i18n context
17753 // - or the template has i18n meta associated with it,
17754 // but it's not initiated by the Element (e.g. <ng-template i18n>)
17755 const initI18nContext = this.i18nContext ||
17756 (isI18nRootNode(i18n) && !isSingleI18nIcu(i18n) &&
17757 !(isSingleElementTemplate(nodes) && nodes[0].i18n === i18n));
17758 const selfClosingI18nInstruction = hasTextChildrenOnly(nodes);
17759 if (initI18nContext) {
17760 this.i18nStart(null, i18n, selfClosingI18nInstruction);
17761 }
17762 // This is the initial pass through the nodes of this template. In this pass, we
17763 // queue all creation mode and update mode instructions for generation in the second
17764 // pass. It's necessary to separate the passes to ensure local refs are defined before
17765 // resolving bindings. We also count bindings in this pass as we walk bound expressions.
17766 visitAll(this, nodes);
17767 // Add total binding count to pure function count so pure function instructions are
17768 // generated with the correct slot offset when update instructions are processed.
17769 this._pureFunctionSlots += this._bindingSlots;
17770 // Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
17771 // `pipeBind` update instructions), so we have to update the slot offsets manually
17772 // to account for bindings.
17773 this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
17774 // Nested templates must be processed before creation instructions so template()
17775 // instructions can be generated with the correct internal const count.
17776 this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
17777 // Output the `projectionDef` instruction when some `<ng-content>` tags are present.
17778 // The `projectionDef` instruction is only emitted for the component template and
17779 // is skipped for nested templates (<ng-template> tags).
17780 if (this.level === 0 && this._ngContentReservedSlots.length) {
17781 const parameters = [];
17782 // By default the `projectionDef` instructions creates one slot for the wildcard
17783 // selector if no parameters are passed. Therefore we only want to allocate a new
17784 // array for the projection slots if the default projection slot is not sufficient.
17785 if (this._ngContentReservedSlots.length > 1 || this._ngContentReservedSlots[0] !== '*') {
17786 const r3ReservedSlots = this._ngContentReservedSlots.map(s => s !== '*' ? parseSelectorToR3Selector(s) : s);
17787 parameters.push(this.constantPool.getConstLiteral(asLiteral(r3ReservedSlots), true));
17788 }
17789 // Since we accumulate ngContent selectors while processing template elements,
17790 // we *prepend* `projectionDef` to creation instructions block, to put it before
17791 // any `projection` instructions
17792 this.creationInstruction(null, Identifiers$1.projectionDef, parameters, /* prepend */ true);
17793 }
17794 if (initI18nContext) {
17795 this.i18nEnd(null, selfClosingI18nInstruction);
17796 }
17797 // Generate all the creation mode instructions (e.g. resolve bindings in listeners)
17798 const creationStatements = this._creationCodeFns.map((fn) => fn());
17799 // Generate all the update mode instructions (e.g. resolve property or text bindings)
17800 const updateStatements = this._updateCodeFns.map((fn) => fn());
17801 // Variable declaration must occur after binding resolution so we can generate context
17802 // instructions that build on each other.
17803 // e.g. const b = nextContext().$implicit(); const b = nextContext();
17804 const creationVariables = this._bindingScope.viewSnapshotStatements();
17805 const updateVariables = this._bindingScope.variableDeclarations().concat(this._tempVariables);
17806 const creationBlock = creationStatements.length > 0 ?
17807 [renderFlagCheckIfStmt(1 /* Create */, creationVariables.concat(creationStatements))] :
17808 [];
17809 const updateBlock = updateStatements.length > 0 ?
17810 [renderFlagCheckIfStmt(2 /* Update */, updateVariables.concat(updateStatements))] :
17811 [];
17812 return fn(
17813 // i.e. (rf: RenderFlags, ctx: any)
17814 [new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
17815 // Temporary variable declarations for query refresh (i.e. let _t: any;)
17816 ...this._prefixCode,
17817 // Creating mode (i.e. if (rf & RenderFlags.Create) { ... })
17818 ...creationBlock,
17819 // Binding and refresh mode (i.e. if (rf & RenderFlags.Update) {...})
17820 ...updateBlock,
17821 ], INFERRED_TYPE, null, this.templateName);
17822 }
17823 // LocalResolver
17824 getLocal(name) {
17825 return this._bindingScope.get(name);
17826 }
17827 // LocalResolver
17828 notifyImplicitReceiverUse() {
17829 this._bindingScope.notifyImplicitReceiverUse();
17830 }
17831 i18nTranslate(message, params = {}, ref, transformFn) {
17832 const _ref = ref || this.i18nGenerateMainBlockVar();
17833 // Closure Compiler requires const names to start with `MSG_` but disallows any other const to
17834 // start with `MSG_`. We define a variable starting with `MSG_` just for the `goog.getMsg` call
17835 const closureVar = this.i18nGenerateClosureVar(message.id);
17836 const statements = getTranslationDeclStmts(message, _ref, closureVar, params, transformFn);
17837 this._constants.prepareStatements.push(...statements);
17838 return _ref;
17839 }
17840 registerContextVariables(variable$1) {
17841 const scopedName = this._bindingScope.freshReferenceName();
17842 const retrievalLevel = this.level;
17843 const lhs = variable(variable$1.name + scopedName);
17844 this._bindingScope.set(retrievalLevel, variable$1.name, lhs, 1 /* CONTEXT */, (scope, relativeLevel) => {
17845 let rhs;
17846 if (scope.bindingLevel === retrievalLevel) {
17847 // e.g. ctx
17848 rhs = variable(CONTEXT_NAME);
17849 }
17850 else {
17851 const sharedCtxVar = scope.getSharedContextName(retrievalLevel);
17852 // e.g. ctx_r0 OR x(2);
17853 rhs = sharedCtxVar ? sharedCtxVar : generateNextContextExpr(relativeLevel);
17854 }
17855 // e.g. const $item$ = x(2).$implicit;
17856 return [lhs.set(rhs.prop(variable$1.value || IMPLICIT_REFERENCE)).toConstDecl()];
17857 });
17858 }
17859 i18nAppendBindings(expressions) {
17860 if (expressions.length > 0) {
17861 expressions.forEach(expression => this.i18n.appendBinding(expression));
17862 }
17863 }
17864 i18nBindProps(props) {
17865 const bound = {};
17866 Object.keys(props).forEach(key => {
17867 const prop = props[key];
17868 if (prop instanceof Text) {
17869 bound[key] = literal(prop.value);
17870 }
17871 else {
17872 const value = prop.value.visit(this._valueConverter);
17873 this.allocateBindingSlots(value);
17874 if (value instanceof Interpolation) {
17875 const { strings, expressions } = value;
17876 const { id, bindings } = this.i18n;
17877 const label = assembleI18nBoundString(strings, bindings.size, id);
17878 this.i18nAppendBindings(expressions);
17879 bound[key] = literal(label);
17880 }
17881 }
17882 });
17883 return bound;
17884 }
17885 // Generates top level vars for i18n blocks (i.e. `i18n_N`).
17886 i18nGenerateMainBlockVar() {
17887 return variable(this.constantPool.uniqueName(TRANSLATION_VAR_PREFIX));
17888 }
17889 // Generates vars with Closure-specific names for i18n blocks (i.e. `MSG_XXX`).
17890 i18nGenerateClosureVar(messageId) {
17891 let name;
17892 const suffix = this.fileBasedI18nSuffix.toUpperCase();
17893 if (this.i18nUseExternalIds) {
17894 const prefix = getTranslationConstPrefix(`EXTERNAL_`);
17895 const uniqueSuffix = this.constantPool.uniqueName(suffix);
17896 name = `${prefix}${sanitizeIdentifier(messageId)}$$${uniqueSuffix}`;
17897 }
17898 else {
17899 const prefix = getTranslationConstPrefix(suffix);
17900 name = this.constantPool.uniqueName(prefix);
17901 }
17902 return variable(name);
17903 }
17904 i18nUpdateRef(context) {
17905 const { icus, meta, isRoot, isResolved, isEmitted } = context;
17906 if (isRoot && isResolved && !isEmitted && !isSingleI18nIcu(meta)) {
17907 context.isEmitted = true;
17908 const placeholders = context.getSerializedPlaceholders();
17909 let icuMapping = {};
17910 let params = placeholders.size ? placeholdersToParams(placeholders) : {};
17911 if (icus.size) {
17912 icus.forEach((refs, key) => {
17913 if (refs.length === 1) {
17914 // if we have one ICU defined for a given
17915 // placeholder - just output its reference
17916 params[key] = refs[0];
17917 }
17918 else {
17919 // ... otherwise we need to activate post-processing
17920 // to replace ICU placeholders with proper values
17921 const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
17922 params[key] = literal(placeholder);
17923 icuMapping[key] = literalArr(refs);
17924 }
17925 });
17926 }
17927 // translation requires post processing in 2 cases:
17928 // - if we have placeholders with multiple values (ex. `START_DIV`: [�#1�, �#2�, ...])
17929 // - if we have multiple ICUs that refer to the same placeholder name
17930 const needsPostprocessing = Array.from(placeholders.values()).some((value) => value.length > 1) ||
17931 Object.keys(icuMapping).length;
17932 let transformFn;
17933 if (needsPostprocessing) {
17934 transformFn = (raw) => {
17935 const args = [raw];
17936 if (Object.keys(icuMapping).length) {
17937 args.push(mapLiteral(icuMapping, true));
17938 }
17939 return instruction(null, Identifiers$1.i18nPostprocess, args);
17940 };
17941 }
17942 this.i18nTranslate(meta, params, context.ref, transformFn);
17943 }
17944 }
17945 i18nStart(span = null, meta, selfClosing) {
17946 const index = this.allocateDataSlot();
17947 this.i18n = this.i18nContext ?
17948 this.i18nContext.forkChildContext(index, this.templateIndex, meta) :
17949 new I18nContext(index, this.i18nGenerateMainBlockVar(), 0, this.templateIndex, meta);
17950 // generate i18nStart instruction
17951 const { id, ref } = this.i18n;
17952 const params = [literal(index), this.addToConsts(ref)];
17953 if (id > 0) {
17954 // do not push 3rd argument (sub-block id)
17955 // into i18nStart call for top level i18n context
17956 params.push(literal(id));
17957 }
17958 this.creationInstruction(span, selfClosing ? Identifiers$1.i18n : Identifiers$1.i18nStart, params);
17959 }
17960 i18nEnd(span = null, selfClosing) {
17961 if (!this.i18n) {
17962 throw new Error('i18nEnd is executed with no i18n context present');
17963 }
17964 if (this.i18nContext) {
17965 this.i18nContext.reconcileChildContext(this.i18n);
17966 this.i18nUpdateRef(this.i18nContext);
17967 }
17968 else {
17969 this.i18nUpdateRef(this.i18n);
17970 }
17971 // setup accumulated bindings
17972 const { index, bindings } = this.i18n;
17973 if (bindings.size) {
17974 const chainBindings = [];
17975 bindings.forEach(binding => {
17976 chainBindings.push({ sourceSpan: span, value: () => this.convertPropertyBinding(binding) });
17977 });
17978 // for i18n block, advance to the most recent element index (by taking the current number of
17979 // elements and subtracting one) before invoking `i18nExp` instructions, to make sure the
17980 // necessary lifecycle hooks of components/directives are properly flushed.
17981 this.updateInstructionChainWithAdvance(this.getConstCount() - 1, Identifiers$1.i18nExp, chainBindings);
17982 this.updateInstruction(span, Identifiers$1.i18nApply, [literal(index)]);
17983 }
17984 if (!selfClosing) {
17985 this.creationInstruction(span, Identifiers$1.i18nEnd);
17986 }
17987 this.i18n = null; // reset local i18n context
17988 }
17989 i18nAttributesInstruction(nodeIndex, attrs, sourceSpan) {
17990 let hasBindings = false;
17991 const i18nAttrArgs = [];
17992 const bindings = [];
17993 attrs.forEach(attr => {
17994 const message = attr.i18n;
17995 const converted = attr.value.visit(this._valueConverter);
17996 this.allocateBindingSlots(converted);
17997 if (converted instanceof Interpolation) {
17998 const placeholders = assembleBoundTextPlaceholders(message);
17999 const params = placeholdersToParams(placeholders);
18000 i18nAttrArgs.push(literal(attr.name), this.i18nTranslate(message, params));
18001 converted.expressions.forEach(expression => {
18002 hasBindings = true;
18003 bindings.push({
18004 sourceSpan,
18005 value: () => this.convertPropertyBinding(expression),
18006 });
18007 });
18008 }
18009 });
18010 if (bindings.length > 0) {
18011 this.updateInstructionChainWithAdvance(nodeIndex, Identifiers$1.i18nExp, bindings);
18012 }
18013 if (i18nAttrArgs.length > 0) {
18014 const index = literal(this.allocateDataSlot());
18015 const constIndex = this.addToConsts(literalArr(i18nAttrArgs));
18016 this.creationInstruction(sourceSpan, Identifiers$1.i18nAttributes, [index, constIndex]);
18017 if (hasBindings) {
18018 this.updateInstruction(sourceSpan, Identifiers$1.i18nApply, [index]);
18019 }
18020 }
18021 }
18022 getNamespaceInstruction(namespaceKey) {
18023 switch (namespaceKey) {
18024 case 'math':
18025 return Identifiers$1.namespaceMathML;
18026 case 'svg':
18027 return Identifiers$1.namespaceSVG;
18028 default:
18029 return Identifiers$1.namespaceHTML;
18030 }
18031 }
18032 addNamespaceInstruction(nsInstruction, element) {
18033 this._namespace = nsInstruction;
18034 this.creationInstruction(element.startSourceSpan, nsInstruction);
18035 }
18036 /**
18037 * Adds an update instruction for an interpolated property or attribute, such as
18038 * `prop="{{value}}"` or `attr.title="{{value}}"`
18039 */
18040 interpolatedUpdateInstruction(instruction, elementIndex, attrName, input, value, params) {
18041 this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, instruction, () => [literal(attrName), ...this.getUpdateInstructionArguments(value), ...params]);
18042 }
18043 visitContent(ngContent) {
18044 const slot = this.allocateDataSlot();
18045 const projectionSlotIdx = this._ngContentSelectorsOffset + this._ngContentReservedSlots.length;
18046 const parameters = [literal(slot)];
18047 this._ngContentReservedSlots.push(ngContent.selector);
18048 const nonContentSelectAttributes = ngContent.attributes.filter(attr => attr.name.toLowerCase() !== NG_CONTENT_SELECT_ATTR$1);
18049 const attributes = this.getAttributeExpressions(ngContent.name, nonContentSelectAttributes, [], []);
18050 if (attributes.length > 0) {
18051 parameters.push(literal(projectionSlotIdx), literalArr(attributes));
18052 }
18053 else if (projectionSlotIdx !== 0) {
18054 parameters.push(literal(projectionSlotIdx));
18055 }
18056 this.creationInstruction(ngContent.sourceSpan, Identifiers$1.projection, parameters);
18057 if (this.i18n) {
18058 this.i18n.appendProjection(ngContent.i18n, slot);
18059 }
18060 }
18061 visitElement(element) {
18062 var _a, _b;
18063 const elementIndex = this.allocateDataSlot();
18064 const stylingBuilder = new StylingBuilder(null);
18065 let isNonBindableMode = false;
18066 const isI18nRootElement = isI18nRootNode(element.i18n) && !isSingleI18nIcu(element.i18n);
18067 const outputAttrs = [];
18068 const [namespaceKey, elementName] = splitNsName(element.name);
18069 const isNgContainer$1 = isNgContainer(element.name);
18070 // Handle styling, i18n, ngNonBindable attributes
18071 for (const attr of element.attributes) {
18072 const { name, value } = attr;
18073 if (name === NON_BINDABLE_ATTR) {
18074 isNonBindableMode = true;
18075 }
18076 else if (name === 'style') {
18077 stylingBuilder.registerStyleAttr(value);
18078 }
18079 else if (name === 'class') {
18080 stylingBuilder.registerClassAttr(value);
18081 }
18082 else {
18083 outputAttrs.push(attr);
18084 }
18085 }
18086 // Match directives on non i18n attributes
18087 this.matchDirectives(element.name, element);
18088 // Regular element or ng-container creation mode
18089 const parameters = [literal(elementIndex)];
18090 if (!isNgContainer$1) {
18091 parameters.push(literal(elementName));
18092 }
18093 // Add the attributes
18094 const allOtherInputs = [];
18095 const boundI18nAttrs = [];
18096 element.inputs.forEach(input => {
18097 const stylingInputWasSet = stylingBuilder.registerBoundInput(input);
18098 if (!stylingInputWasSet) {
18099 if (input.type === 0 /* Property */ && input.i18n) {
18100 boundI18nAttrs.push(input);
18101 }
18102 else {
18103 allOtherInputs.push(input);
18104 }
18105 }
18106 });
18107 // add attributes for directive and projection matching purposes
18108 const attributes = this.getAttributeExpressions(element.name, outputAttrs, allOtherInputs, element.outputs, stylingBuilder, [], boundI18nAttrs);
18109 parameters.push(this.addAttrsToConsts(attributes));
18110 // local refs (ex.: <div #foo #bar="baz">)
18111 const refs = this.prepareRefsArray(element.references);
18112 parameters.push(this.addToConsts(refs));
18113 const wasInNamespace = this._namespace;
18114 const currentNamespace = this.getNamespaceInstruction(namespaceKey);
18115 // If the namespace is changing now, include an instruction to change it
18116 // during element creation.
18117 if (currentNamespace !== wasInNamespace) {
18118 this.addNamespaceInstruction(currentNamespace, element);
18119 }
18120 if (this.i18n) {
18121 this.i18n.appendElement(element.i18n, elementIndex);
18122 }
18123 // Note that we do not append text node instructions and ICUs inside i18n section,
18124 // so we exclude them while calculating whether current element has children
18125 const hasChildren = (!isI18nRootElement && this.i18n) ? !hasTextChildrenOnly(element.children) :
18126 element.children.length > 0;
18127 const createSelfClosingInstruction = !stylingBuilder.hasBindingsWithPipes &&
18128 element.outputs.length === 0 && boundI18nAttrs.length === 0 && !hasChildren;
18129 const createSelfClosingI18nInstruction = !createSelfClosingInstruction && hasTextChildrenOnly(element.children);
18130 if (createSelfClosingInstruction) {
18131 this.creationInstruction(element.sourceSpan, isNgContainer$1 ? Identifiers$1.elementContainer : Identifiers$1.element, trimTrailingNulls(parameters));
18132 }
18133 else {
18134 this.creationInstruction(element.startSourceSpan, isNgContainer$1 ? Identifiers$1.elementContainerStart : Identifiers$1.elementStart, trimTrailingNulls(parameters));
18135 if (isNonBindableMode) {
18136 this.creationInstruction(element.startSourceSpan, Identifiers$1.disableBindings);
18137 }
18138 if (boundI18nAttrs.length > 0) {
18139 this.i18nAttributesInstruction(elementIndex, boundI18nAttrs, (_a = element.startSourceSpan) !== null && _a !== void 0 ? _a : element.sourceSpan);
18140 }
18141 // Generate Listeners (outputs)
18142 if (element.outputs.length > 0) {
18143 const listeners = element.outputs.map((outputAst) => ({
18144 sourceSpan: outputAst.sourceSpan,
18145 params: this.prepareListenerParameter(element.name, outputAst, elementIndex)
18146 }));
18147 this.creationInstructionChain(Identifiers$1.listener, listeners);
18148 }
18149 // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
18150 // listeners, to make sure i18nAttributes instruction targets current element at runtime.
18151 if (isI18nRootElement) {
18152 this.i18nStart(element.startSourceSpan, element.i18n, createSelfClosingI18nInstruction);
18153 }
18154 }
18155 // the code here will collect all update-level styling instructions and add them to the
18156 // update block of the template function AOT code. Instructions like `styleProp`,
18157 // `styleMap`, `classMap`, `classProp`
18158 // are all generated and assigned in the code below.
18159 const stylingInstructions = stylingBuilder.buildUpdateLevelInstructions(this._valueConverter);
18160 const limit = stylingInstructions.length - 1;
18161 for (let i = 0; i <= limit; i++) {
18162 const instruction = stylingInstructions[i];
18163 this._bindingSlots += this.processStylingUpdateInstruction(elementIndex, instruction);
18164 }
18165 // the reason why `undefined` is used is because the renderer understands this as a
18166 // special value to symbolize that there is no RHS to this binding
18167 // TODO (matsko): revisit this once FW-959 is approached
18168 const emptyValueBindInstruction = literal(undefined);
18169 const propertyBindings = [];
18170 const attributeBindings = [];
18171 // Generate element input bindings
18172 allOtherInputs.forEach(input => {
18173 const inputType = input.type;
18174 if (inputType === 4 /* Animation */) {
18175 const value = input.value.visit(this._valueConverter);
18176 // animation bindings can be presented in the following formats:
18177 // 1. [@binding]="fooExp"
18178 // 2. [@binding]="{value:fooExp, params:{...}}"
18179 // 3. [@binding]
18180 // 4. @binding
18181 // All formats will be valid for when a synthetic binding is created.
18182 // The reasoning for this is because the renderer should get each
18183 // synthetic binding value in the order of the array that they are
18184 // defined in...
18185 const hasValue = value instanceof LiteralPrimitive ? !!value.value : true;
18186 this.allocateBindingSlots(value);
18187 propertyBindings.push({
18188 name: prepareSyntheticPropertyName(input.name),
18189 sourceSpan: input.sourceSpan,
18190 value: () => hasValue ? this.convertPropertyBinding(value) : emptyValueBindInstruction
18191 });
18192 }
18193 else {
18194 // we must skip attributes with associated i18n context, since these attributes are handled
18195 // separately and corresponding `i18nExp` and `i18nApply` instructions will be generated
18196 if (input.i18n)
18197 return;
18198 const value = input.value.visit(this._valueConverter);
18199 if (value !== undefined) {
18200 const params = [];
18201 const [attrNamespace, attrName] = splitNsName(input.name);
18202 const isAttributeBinding = inputType === 1 /* Attribute */;
18203 const sanitizationRef = resolveSanitizationFn(input.securityContext, isAttributeBinding);
18204 if (sanitizationRef)
18205 params.push(sanitizationRef);
18206 if (attrNamespace) {
18207 const namespaceLiteral = literal(attrNamespace);
18208 if (sanitizationRef) {
18209 params.push(namespaceLiteral);
18210 }
18211 else {
18212 // If there wasn't a sanitization ref, we need to add
18213 // an extra param so that we can pass in the namespace.
18214 params.push(literal(null), namespaceLiteral);
18215 }
18216 }
18217 this.allocateBindingSlots(value);
18218 if (inputType === 0 /* Property */) {
18219 if (value instanceof Interpolation) {
18220 // prop="{{value}}" and friends
18221 this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), elementIndex, attrName, input, value, params);
18222 }
18223 else {
18224 // [prop]="value"
18225 // Collect all the properties so that we can chain into a single function at the end.
18226 propertyBindings.push({
18227 name: attrName,
18228 sourceSpan: input.sourceSpan,
18229 value: () => this.convertPropertyBinding(value),
18230 params
18231 });
18232 }
18233 }
18234 else if (inputType === 1 /* Attribute */) {
18235 if (value instanceof Interpolation && getInterpolationArgsLength(value) > 1) {
18236 // attr.name="text{{value}}" and friends
18237 this.interpolatedUpdateInstruction(getAttributeInterpolationExpression(value), elementIndex, attrName, input, value, params);
18238 }
18239 else {
18240 const boundValue = value instanceof Interpolation ? value.expressions[0] : value;
18241 // [attr.name]="value" or attr.name="{{value}}"
18242 // Collect the attribute bindings so that they can be chained at the end.
18243 attributeBindings.push({
18244 name: attrName,
18245 sourceSpan: input.sourceSpan,
18246 value: () => this.convertPropertyBinding(boundValue),
18247 params
18248 });
18249 }
18250 }
18251 else {
18252 // class prop
18253 this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, Identifiers$1.classProp, () => {
18254 return [
18255 literal(elementIndex), literal(attrName), this.convertPropertyBinding(value),
18256 ...params
18257 ];
18258 });
18259 }
18260 }
18261 }
18262 });
18263 if (propertyBindings.length > 0) {
18264 this.updateInstructionChainWithAdvance(elementIndex, Identifiers$1.property, propertyBindings);
18265 }
18266 if (attributeBindings.length > 0) {
18267 this.updateInstructionChainWithAdvance(elementIndex, Identifiers$1.attribute, attributeBindings);
18268 }
18269 // Traverse element child nodes
18270 visitAll(this, element.children);
18271 if (!isI18nRootElement && this.i18n) {
18272 this.i18n.appendElement(element.i18n, elementIndex, true);
18273 }
18274 if (!createSelfClosingInstruction) {
18275 // Finish element construction mode.
18276 const span = (_b = element.endSourceSpan) !== null && _b !== void 0 ? _b : element.sourceSpan;
18277 if (isI18nRootElement) {
18278 this.i18nEnd(span, createSelfClosingI18nInstruction);
18279 }
18280 if (isNonBindableMode) {
18281 this.creationInstruction(span, Identifiers$1.enableBindings);
18282 }
18283 this.creationInstruction(span, isNgContainer$1 ? Identifiers$1.elementContainerEnd : Identifiers$1.elementEnd);
18284 }
18285 }
18286 visitTemplate(template) {
18287 var _a;
18288 const NG_TEMPLATE_TAG_NAME = 'ng-template';
18289 const templateIndex = this.allocateDataSlot();
18290 if (this.i18n) {
18291 this.i18n.appendTemplate(template.i18n, templateIndex);
18292 }
18293 const tagName = sanitizeIdentifier(template.tagName || '');
18294 const contextName = `${this.contextName}${tagName ? '_' + tagName : ''}_${templateIndex}`;
18295 const templateName = `${contextName}_Template`;
18296 const parameters = [
18297 literal(templateIndex),
18298 variable(templateName),
18299 // We don't care about the tag's namespace here, because we infer
18300 // it based on the parent nodes inside the template instruction.
18301 literal(template.tagName ? splitNsName(template.tagName)[1] : template.tagName),
18302 ];
18303 // find directives matching on a given <ng-template> node
18304 this.matchDirectives(NG_TEMPLATE_TAG_NAME, template);
18305 // prepare attributes parameter (including attributes used for directive matching)
18306 const attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs);
18307 parameters.push(this.addAttrsToConsts(attrsExprs));
18308 // local refs (ex.: <ng-template #foo>)
18309 if (template.references && template.references.length) {
18310 const refs = this.prepareRefsArray(template.references);
18311 parameters.push(this.addToConsts(refs));
18312 parameters.push(importExpr(Identifiers$1.templateRefExtractor));
18313 }
18314 // Create the template function
18315 const templateVisitor = new TemplateDefinitionBuilder(this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n, templateIndex, templateName, this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds, this._constants);
18316 // Nested templates must not be visited until after their parent templates have completed
18317 // processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
18318 // be able to support bindings in nested templates to local refs that occur after the
18319 // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div>
18320 this._nestedTemplateFns.push(() => {
18321 const templateFunctionExpr = templateVisitor.buildTemplateFunction(template.children, template.variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, template.i18n);
18322 this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName));
18323 if (templateVisitor._ngContentReservedSlots.length) {
18324 this._ngContentReservedSlots.push(...templateVisitor._ngContentReservedSlots);
18325 }
18326 });
18327 // e.g. template(1, MyComp_Template_1)
18328 this.creationInstruction(template.sourceSpan, Identifiers$1.templateCreate, () => {
18329 parameters.splice(2, 0, literal(templateVisitor.getConstCount()), literal(templateVisitor.getVarCount()));
18330 return trimTrailingNulls(parameters);
18331 });
18332 // handle property bindings e.g. ɵɵproperty('ngForOf', ctx.items), et al;
18333 this.templatePropertyBindings(templateIndex, template.templateAttrs);
18334 // Only add normal input/output binding instructions on explicit <ng-template> elements.
18335 if (template.tagName === NG_TEMPLATE_TAG_NAME) {
18336 const [i18nInputs, inputs] = partitionArray(template.inputs, hasI18nMeta);
18337 // Add i18n attributes that may act as inputs to directives. If such attributes are present,
18338 // generate `i18nAttributes` instruction. Note: we generate it only for explicit <ng-template>
18339 // elements, in case of inline templates, corresponding instructions will be generated in the
18340 // nested template function.
18341 if (i18nInputs.length > 0) {
18342 this.i18nAttributesInstruction(templateIndex, i18nInputs, (_a = template.startSourceSpan) !== null && _a !== void 0 ? _a : template.sourceSpan);
18343 }
18344 // Add the input bindings
18345 if (inputs.length > 0) {
18346 this.templatePropertyBindings(templateIndex, inputs);
18347 }
18348 // Generate listeners for directive output
18349 if (template.outputs.length > 0) {
18350 const listeners = template.outputs.map((outputAst) => ({
18351 sourceSpan: outputAst.sourceSpan,
18352 params: this.prepareListenerParameter('ng_template', outputAst, templateIndex)
18353 }));
18354 this.creationInstructionChain(Identifiers$1.listener, listeners);
18355 }
18356 }
18357 }
18358 visitBoundText(text) {
18359 if (this.i18n) {
18360 const value = text.value.visit(this._valueConverter);
18361 this.allocateBindingSlots(value);
18362 if (value instanceof Interpolation) {
18363 this.i18n.appendBoundText(text.i18n);
18364 this.i18nAppendBindings(value.expressions);
18365 }
18366 return;
18367 }
18368 const nodeIndex = this.allocateDataSlot();
18369 this.creationInstruction(text.sourceSpan, Identifiers$1.text, [literal(nodeIndex)]);
18370 const value = text.value.visit(this._valueConverter);
18371 this.allocateBindingSlots(value);
18372 if (value instanceof Interpolation) {
18373 this.updateInstructionWithAdvance(nodeIndex, text.sourceSpan, getTextInterpolationExpression(value), () => this.getUpdateInstructionArguments(value));
18374 }
18375 else {
18376 error('Text nodes should be interpolated and never bound directly.');
18377 }
18378 }
18379 visitText(text) {
18380 // when a text element is located within a translatable
18381 // block, we exclude this text element from instructions set,
18382 // since it will be captured in i18n content and processed at runtime
18383 if (!this.i18n) {
18384 this.creationInstruction(text.sourceSpan, Identifiers$1.text, [literal(this.allocateDataSlot()), literal(text.value)]);
18385 }
18386 }
18387 visitIcu(icu) {
18388 let initWasInvoked = false;
18389 // if an ICU was created outside of i18n block, we still treat
18390 // it as a translatable entity and invoke i18nStart and i18nEnd
18391 // to generate i18n context and the necessary instructions
18392 if (!this.i18n) {
18393 initWasInvoked = true;
18394 this.i18nStart(null, icu.i18n, true);
18395 }
18396 const i18n = this.i18n;
18397 const vars = this.i18nBindProps(icu.vars);
18398 const placeholders = this.i18nBindProps(icu.placeholders);
18399 // output ICU directly and keep ICU reference in context
18400 const message = icu.i18n;
18401 // we always need post-processing function for ICUs, to make sure that:
18402 // - all placeholders in a form of {PLACEHOLDER} are replaced with actual values (note:
18403 // `goog.getMsg` does not process ICUs and uses the `{PLACEHOLDER}` format for placeholders
18404 // inside ICUs)
18405 // - all ICU vars (such as `VAR_SELECT` or `VAR_PLURAL`) are replaced with correct values
18406 const transformFn = (raw) => {
18407 const params = Object.assign(Object.assign({}, vars), placeholders);
18408 const formatted = i18nFormatPlaceholderNames(params, /* useCamelCase */ false);
18409 return instruction(null, Identifiers$1.i18nPostprocess, [raw, mapLiteral(formatted, true)]);
18410 };
18411 // in case the whole i18n message is a single ICU - we do not need to
18412 // create a separate top-level translation, we can use the root ref instead
18413 // and make this ICU a top-level translation
18414 // note: ICU placeholders are replaced with actual values in `i18nPostprocess` function
18415 // separately, so we do not pass placeholders into `i18nTranslate` function.
18416 if (isSingleI18nIcu(i18n.meta)) {
18417 this.i18nTranslate(message, /* placeholders */ {}, i18n.ref, transformFn);
18418 }
18419 else {
18420 // output ICU directly and keep ICU reference in context
18421 const ref = this.i18nTranslate(message, /* placeholders */ {}, /* ref */ undefined, transformFn);
18422 i18n.appendIcu(icuFromI18nMessage(message).name, ref);
18423 }
18424 if (initWasInvoked) {
18425 this.i18nEnd(null, true);
18426 }
18427 return null;
18428 }
18429 allocateDataSlot() {
18430 return this._dataIndex++;
18431 }
18432 getConstCount() {
18433 return this._dataIndex;
18434 }
18435 getVarCount() {
18436 return this._pureFunctionSlots;
18437 }
18438 getConsts() {
18439 return this._constants;
18440 }
18441 getNgContentSelectors() {
18442 return this._ngContentReservedSlots.length ?
18443 this.constantPool.getConstLiteral(asLiteral(this._ngContentReservedSlots), true) :
18444 null;
18445 }
18446 bindingContext() {
18447 return `${this._bindingContext++}`;
18448 }
18449 templatePropertyBindings(templateIndex, attrs) {
18450 const propertyBindings = [];
18451 attrs.forEach(input => {
18452 if (input instanceof BoundAttribute) {
18453 const value = input.value.visit(this._valueConverter);
18454 if (value !== undefined) {
18455 this.allocateBindingSlots(value);
18456 if (value instanceof Interpolation) {
18457 // Params typically contain attribute namespace and value sanitizer, which is applicable
18458 // for regular HTML elements, but not applicable for <ng-template> (since props act as
18459 // inputs to directives), so keep params array empty.
18460 const params = [];
18461 // prop="{{value}}" case
18462 this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), templateIndex, input.name, input, value, params);
18463 }
18464 else {
18465 // [prop]="value" case
18466 propertyBindings.push({
18467 name: input.name,
18468 sourceSpan: input.sourceSpan,
18469 value: () => this.convertPropertyBinding(value)
18470 });
18471 }
18472 }
18473 }
18474 });
18475 if (propertyBindings.length > 0) {
18476 this.updateInstructionChainWithAdvance(templateIndex, Identifiers$1.property, propertyBindings);
18477 }
18478 }
18479 // Bindings must only be resolved after all local refs have been visited, so all
18480 // instructions are queued in callbacks that execute once the initial pass has completed.
18481 // Otherwise, we wouldn't be able to support local refs that are defined after their
18482 // bindings. e.g. {{ foo }} <div #foo></div>
18483 instructionFn(fns, span, reference, paramsOrFn, prepend = false) {
18484 fns[prepend ? 'unshift' : 'push'](() => {
18485 const params = Array.isArray(paramsOrFn) ? paramsOrFn : paramsOrFn();
18486 return instruction(span, reference, params).toStmt();
18487 });
18488 }
18489 processStylingUpdateInstruction(elementIndex, instruction) {
18490 let allocateBindingSlots = 0;
18491 if (instruction) {
18492 const calls = [];
18493 instruction.calls.forEach(call => {
18494 allocateBindingSlots += call.allocateBindingSlots;
18495 calls.push({
18496 sourceSpan: call.sourceSpan,
18497 value: () => {
18498 return call.params(value => (call.supportsInterpolation && value instanceof Interpolation) ?
18499 this.getUpdateInstructionArguments(value) :
18500 this.convertPropertyBinding(value));
18501 }
18502 });
18503 });
18504 this.updateInstructionChainWithAdvance(elementIndex, instruction.reference, calls);
18505 }
18506 return allocateBindingSlots;
18507 }
18508 creationInstruction(span, reference, paramsOrFn, prepend) {
18509 this.instructionFn(this._creationCodeFns, span, reference, paramsOrFn || [], prepend);
18510 }
18511 creationInstructionChain(reference, calls) {
18512 const span = calls.length ? calls[0].sourceSpan : null;
18513 this._creationCodeFns.push(() => {
18514 return chainedInstruction(reference, calls.map(call => call.params()), span).toStmt();
18515 });
18516 }
18517 updateInstructionWithAdvance(nodeIndex, span, reference, paramsOrFn) {
18518 this.addAdvanceInstructionIfNecessary(nodeIndex, span);
18519 this.updateInstruction(span, reference, paramsOrFn);
18520 }
18521 updateInstruction(span, reference, paramsOrFn) {
18522 this.instructionFn(this._updateCodeFns, span, reference, paramsOrFn || []);
18523 }
18524 updateInstructionChain(reference, bindings) {
18525 const span = bindings.length ? bindings[0].sourceSpan : null;
18526 this._updateCodeFns.push(() => {
18527 const calls = bindings.map(property => {
18528 const value = property.value();
18529 const fnParams = Array.isArray(value) ? value : [value];
18530 if (property.params) {
18531 fnParams.push(...property.params);
18532 }
18533 if (property.name) {
18534 // We want the property name to always be the first function parameter.
18535 fnParams.unshift(literal(property.name));
18536 }
18537 return fnParams;
18538 });
18539 return chainedInstruction(reference, calls, span).toStmt();
18540 });
18541 }
18542 updateInstructionChainWithAdvance(nodeIndex, reference, bindings) {
18543 this.addAdvanceInstructionIfNecessary(nodeIndex, bindings.length ? bindings[0].sourceSpan : null);
18544 this.updateInstructionChain(reference, bindings);
18545 }
18546 addAdvanceInstructionIfNecessary(nodeIndex, span) {
18547 if (nodeIndex !== this._currentIndex) {
18548 const delta = nodeIndex - this._currentIndex;
18549 if (delta < 1) {
18550 throw new Error('advance instruction can only go forwards');
18551 }
18552 this.instructionFn(this._updateCodeFns, span, Identifiers$1.advance, [literal(delta)]);
18553 this._currentIndex = nodeIndex;
18554 }
18555 }
18556 allocatePureFunctionSlots(numSlots) {
18557 const originalSlots = this._pureFunctionSlots;
18558 this._pureFunctionSlots += numSlots;
18559 return originalSlots;
18560 }
18561 allocateBindingSlots(value) {
18562 this._bindingSlots += value instanceof Interpolation ? value.expressions.length : 1;
18563 }
18564 /**
18565 * Gets an expression that refers to the implicit receiver. The implicit
18566 * receiver is always the root level context.
18567 */
18568 getImplicitReceiverExpr() {
18569 if (this._implicitReceiverExpr) {
18570 return this._implicitReceiverExpr;
18571 }
18572 return this._implicitReceiverExpr = this.level === 0 ?
18573 variable(CONTEXT_NAME) :
18574 this._bindingScope.getOrCreateSharedContextVar(0);
18575 }
18576 convertPropertyBinding(value) {
18577 const convertedPropertyBinding = convertPropertyBinding(this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.Expression, () => error('Unexpected interpolation'));
18578 const valExpr = convertedPropertyBinding.currValExpr;
18579 this._tempVariables.push(...convertedPropertyBinding.stmts);
18580 return valExpr;
18581 }
18582 /**
18583 * Gets a list of argument expressions to pass to an update instruction expression. Also updates
18584 * the temp variables state with temp variables that were identified as needing to be created
18585 * while visiting the arguments.
18586 * @param value The original expression we will be resolving an arguments list from.
18587 */
18588 getUpdateInstructionArguments(value) {
18589 const { args, stmts } = convertUpdateArguments(this, this.getImplicitReceiverExpr(), value, this.bindingContext());
18590 this._tempVariables.push(...stmts);
18591 return args;
18592 }
18593 matchDirectives(elementName, elOrTpl) {
18594 if (this.directiveMatcher) {
18595 const selector = createCssSelector(elementName, getAttrsForDirectiveMatching(elOrTpl));
18596 this.directiveMatcher.match(selector, (cssSelector, staticType) => {
18597 this.directives.add(staticType);
18598 });
18599 }
18600 }
18601 /**
18602 * Prepares all attribute expression values for the `TAttributes` array.
18603 *
18604 * The purpose of this function is to properly construct an attributes array that
18605 * is passed into the `elementStart` (or just `element`) functions. Because there
18606 * are many different types of attributes, the array needs to be constructed in a
18607 * special way so that `elementStart` can properly evaluate them.
18608 *
18609 * The format looks like this:
18610 *
18611 * ```
18612 * attrs = [prop, value, prop2, value2,
18613 * PROJECT_AS, selector,
18614 * CLASSES, class1, class2,
18615 * STYLES, style1, value1, style2, value2,
18616 * BINDINGS, name1, name2, name3,
18617 * TEMPLATE, name4, name5, name6,
18618 * I18N, name7, name8, ...]
18619 * ```
18620 *
18621 * Note that this function will fully ignore all synthetic (@foo) attribute values
18622 * because those values are intended to always be generated as property instructions.
18623 */
18624 getAttributeExpressions(elementName, renderAttributes, inputs, outputs, styles, templateAttrs = [], boundI18nAttrs = []) {
18625 const alreadySeen = new Set();
18626 const attrExprs = [];
18627 let ngProjectAsAttr;
18628 for (const attr of renderAttributes) {
18629 if (attr.name === NG_PROJECT_AS_ATTR_NAME) {
18630 ngProjectAsAttr = attr;
18631 }
18632 // Note that static i18n attributes aren't in the i18n array,
18633 // because they're treated in the same way as regular attributes.
18634 if (attr.i18n) {
18635 // When i18n attributes are present on elements with structural directives
18636 // (e.g. `<div *ngIf title="Hello" i18n-title>`), we want to avoid generating
18637 // duplicate i18n translation blocks for `ɵɵtemplate` and `ɵɵelement` instruction
18638 // attributes. So we do a cache lookup to see if suitable i18n translation block
18639 // already exists.
18640 const { i18nVarRefsCache } = this._constants;
18641 let i18nVarRef;
18642 if (i18nVarRefsCache.has(attr.i18n)) {
18643 i18nVarRef = i18nVarRefsCache.get(attr.i18n);
18644 }
18645 else {
18646 i18nVarRef = this.i18nTranslate(attr.i18n);
18647 i18nVarRefsCache.set(attr.i18n, i18nVarRef);
18648 }
18649 attrExprs.push(literal(attr.name), i18nVarRef);
18650 }
18651 else {
18652 attrExprs.push(...getAttributeNameLiterals(attr.name), trustedConstAttribute(elementName, attr));
18653 }
18654 }
18655 // Keep ngProjectAs next to the other name, value pairs so we can verify that we match
18656 // ngProjectAs marker in the attribute name slot.
18657 if (ngProjectAsAttr) {
18658 attrExprs.push(...getNgProjectAsLiteral(ngProjectAsAttr));
18659 }
18660 function addAttrExpr(key, value) {
18661 if (typeof key === 'string') {
18662 if (!alreadySeen.has(key)) {
18663 attrExprs.push(...getAttributeNameLiterals(key));
18664 value !== undefined && attrExprs.push(value);
18665 alreadySeen.add(key);
18666 }
18667 }
18668 else {
18669 attrExprs.push(literal(key));
18670 }
18671 }
18672 // it's important that this occurs before BINDINGS and TEMPLATE because once `elementStart`
18673 // comes across the BINDINGS or TEMPLATE markers then it will continue reading each value as
18674 // as single property value cell by cell.
18675 if (styles) {
18676 styles.populateInitialStylingAttrs(attrExprs);
18677 }
18678 if (inputs.length || outputs.length) {
18679 const attrsLengthBeforeInputs = attrExprs.length;
18680 for (let i = 0; i < inputs.length; i++) {
18681 const input = inputs[i];
18682 // We don't want the animation and attribute bindings in the
18683 // attributes array since they aren't used for directive matching.
18684 if (input.type !== 4 /* Animation */ && input.type !== 1 /* Attribute */) {
18685 addAttrExpr(input.name);
18686 }
18687 }
18688 for (let i = 0; i < outputs.length; i++) {
18689 const output = outputs[i];
18690 if (output.type !== 1 /* Animation */) {
18691 addAttrExpr(output.name);
18692 }
18693 }
18694 // this is a cheap way of adding the marker only after all the input/output
18695 // values have been filtered (by not including the animation ones) and added
18696 // to the expressions. The marker is important because it tells the runtime
18697 // code that this is where attributes without values start...
18698 if (attrExprs.length !== attrsLengthBeforeInputs) {
18699 attrExprs.splice(attrsLengthBeforeInputs, 0, literal(3 /* Bindings */));
18700 }
18701 }
18702 if (templateAttrs.length) {
18703 attrExprs.push(literal(4 /* Template */));
18704 templateAttrs.forEach(attr => addAttrExpr(attr.name));
18705 }
18706 if (boundI18nAttrs.length) {
18707 attrExprs.push(literal(6 /* I18n */));
18708 boundI18nAttrs.forEach(attr => addAttrExpr(attr.name));
18709 }
18710 return attrExprs;
18711 }
18712 addToConsts(expression) {
18713 if (isNull(expression)) {
18714 return TYPED_NULL_EXPR;
18715 }
18716 const consts = this._constants.constExpressions;
18717 // Try to reuse a literal that's already in the array, if possible.
18718 for (let i = 0; i < consts.length; i++) {
18719 if (consts[i].isEquivalent(expression)) {
18720 return literal(i);
18721 }
18722 }
18723 return literal(consts.push(expression) - 1);
18724 }
18725 addAttrsToConsts(attrs) {
18726 return attrs.length > 0 ? this.addToConsts(literalArr(attrs)) : TYPED_NULL_EXPR;
18727 }
18728 prepareRefsArray(references) {
18729 if (!references || references.length === 0) {
18730 return TYPED_NULL_EXPR;
18731 }
18732 const refsParam = flatten(references.map(reference => {
18733 const slot = this.allocateDataSlot();
18734 // Generate the update temporary.
18735 const variableName = this._bindingScope.freshReferenceName();
18736 const retrievalLevel = this.level;
18737 const lhs = variable(variableName);
18738 this._bindingScope.set(retrievalLevel, reference.name, lhs, 0 /* DEFAULT */, (scope, relativeLevel) => {
18739 // e.g. nextContext(2);
18740 const nextContextStmt = relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : [];
18741 // e.g. const $foo$ = reference(1);
18742 const refExpr = lhs.set(importExpr(Identifiers$1.reference).callFn([literal(slot)]));
18743 return nextContextStmt.concat(refExpr.toConstDecl());
18744 }, true);
18745 return [reference.name, reference.value];
18746 }));
18747 return asLiteral(refsParam);
18748 }
18749 prepareListenerParameter(tagName, outputAst, index) {
18750 return () => {
18751 const eventName = outputAst.name;
18752 const bindingFnName = outputAst.type === 1 /* Animation */ ?
18753 // synthetic @listener.foo values are treated the exact same as are standard listeners
18754 prepareSyntheticListenerFunctionName(eventName, outputAst.phase) :
18755 sanitizeIdentifier(eventName);
18756 const handlerName = `${this.templateName}_${tagName}_${bindingFnName}_${index}_listener`;
18757 const scope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel, EVENT_BINDING_SCOPE_GLOBALS);
18758 return prepareEventListenerParameters(outputAst, handlerName, scope);
18759 };
18760 }
18761 }
18762 class ValueConverter extends AstMemoryEfficientTransformer {
18763 constructor(constantPool, allocateSlot, allocatePureFunctionSlots, definePipe) {
18764 super();
18765 this.constantPool = constantPool;
18766 this.allocateSlot = allocateSlot;
18767 this.allocatePureFunctionSlots = allocatePureFunctionSlots;
18768 this.definePipe = definePipe;
18769 this._pipeBindExprs = [];
18770 }
18771 // AstMemoryEfficientTransformer
18772 visitPipe(pipe, context) {
18773 // Allocate a slot to create the pipe
18774 const slot = this.allocateSlot();
18775 const slotPseudoLocal = `PIPE:${slot}`;
18776 // Allocate one slot for the result plus one slot per pipe argument
18777 const pureFunctionSlot = this.allocatePureFunctionSlots(2 + pipe.args.length);
18778 const target = new PropertyRead(pipe.span, pipe.sourceSpan, pipe.nameSpan, new ImplicitReceiver(pipe.span, pipe.sourceSpan), slotPseudoLocal);
18779 const { identifier, isVarLength } = pipeBindingCallInfo(pipe.args);
18780 this.definePipe(pipe.name, slotPseudoLocal, slot, importExpr(identifier));
18781 const args = [pipe.exp, ...pipe.args];
18782 const convertedArgs = isVarLength ?
18783 this.visitAll([new LiteralArray(pipe.span, pipe.sourceSpan, args)]) :
18784 this.visitAll(args);
18785 const pipeBindExpr = new FunctionCall(pipe.span, pipe.sourceSpan, target, [
18786 new LiteralPrimitive(pipe.span, pipe.sourceSpan, slot),
18787 new LiteralPrimitive(pipe.span, pipe.sourceSpan, pureFunctionSlot),
18788 ...convertedArgs,
18789 ]);
18790 this._pipeBindExprs.push(pipeBindExpr);
18791 return pipeBindExpr;
18792 }
18793 updatePipeSlotOffsets(bindingSlots) {
18794 this._pipeBindExprs.forEach((pipe) => {
18795 // update the slot offset arg (index 1) to account for binding slots
18796 const slotOffset = pipe.args[1];
18797 slotOffset.value += bindingSlots;
18798 });
18799 }
18800 visitLiteralArray(array, context) {
18801 return new BuiltinFunctionCall(array.span, array.sourceSpan, this.visitAll(array.expressions), values => {
18802 // If the literal has calculated (non-literal) elements transform it into
18803 // calls to literal factories that compose the literal and will cache intermediate
18804 // values.
18805 const literal = literalArr(values);
18806 return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
18807 });
18808 }
18809 visitLiteralMap(map, context) {
18810 return new BuiltinFunctionCall(map.span, map.sourceSpan, this.visitAll(map.values), values => {
18811 // If the literal has calculated (non-literal) elements transform it into
18812 // calls to literal factories that compose the literal and will cache intermediate
18813 // values.
18814 const literal = literalMap(values.map((value, index) => ({ key: map.keys[index].key, value, quoted: map.keys[index].quoted })));
18815 return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
18816 });
18817 }
18818 }
18819 // Pipes always have at least one parameter, the value they operate on
18820 const pipeBindingIdentifiers = [Identifiers$1.pipeBind1, Identifiers$1.pipeBind2, Identifiers$1.pipeBind3, Identifiers$1.pipeBind4];
18821 function pipeBindingCallInfo(args) {
18822 const identifier = pipeBindingIdentifiers[args.length];
18823 return {
18824 identifier: identifier || Identifiers$1.pipeBindV,
18825 isVarLength: !identifier,
18826 };
18827 }
18828 const pureFunctionIdentifiers = [
18829 Identifiers$1.pureFunction0, Identifiers$1.pureFunction1, Identifiers$1.pureFunction2, Identifiers$1.pureFunction3, Identifiers$1.pureFunction4,
18830 Identifiers$1.pureFunction5, Identifiers$1.pureFunction6, Identifiers$1.pureFunction7, Identifiers$1.pureFunction8
18831 ];
18832 function pureFunctionCallInfo(args) {
18833 const identifier = pureFunctionIdentifiers[args.length];
18834 return {
18835 identifier: identifier || Identifiers$1.pureFunctionV,
18836 isVarLength: !identifier,
18837 };
18838 }
18839 function instruction(span, reference, params) {
18840 return importExpr(reference, null, span).callFn(params, span);
18841 }
18842 // e.g. x(2);
18843 function generateNextContextExpr(relativeLevelDiff) {
18844 return importExpr(Identifiers$1.nextContext)
18845 .callFn(relativeLevelDiff > 1 ? [literal(relativeLevelDiff)] : []);
18846 }
18847 function getLiteralFactory(constantPool, literal$1, allocateSlots) {
18848 const { literalFactory, literalFactoryArguments } = constantPool.getLiteralFactory(literal$1);
18849 // Allocate 1 slot for the result plus 1 per argument
18850 const startSlot = allocateSlots(1 + literalFactoryArguments.length);
18851 const { identifier, isVarLength } = pureFunctionCallInfo(literalFactoryArguments);
18852 // Literal factories are pure functions that only need to be re-invoked when the parameters
18853 // change.
18854 const args = [literal(startSlot), literalFactory];
18855 if (isVarLength) {
18856 args.push(literalArr(literalFactoryArguments));
18857 }
18858 else {
18859 args.push(...literalFactoryArguments);
18860 }
18861 return importExpr(identifier).callFn(args);
18862 }
18863 /**
18864 * Gets an array of literals that can be added to an expression
18865 * to represent the name and namespace of an attribute. E.g.
18866 * `:xlink:href` turns into `[AttributeMarker.NamespaceURI, 'xlink', 'href']`.
18867 *
18868 * @param name Name of the attribute, including the namespace.
18869 */
18870 function getAttributeNameLiterals(name) {
18871 const [attributeNamespace, attributeName] = splitNsName(name);
18872 const nameLiteral = literal(attributeName);
18873 if (attributeNamespace) {
18874 return [
18875 literal(0 /* NamespaceURI */), literal(attributeNamespace), nameLiteral
18876 ];
18877 }
18878 return [nameLiteral];
18879 }
18880 /** The prefix used to get a shared context in BindingScope's map. */
18881 const SHARED_CONTEXT_KEY = '$$shared_ctx$$';
18882 class BindingScope {
18883 constructor(bindingLevel = 0, parent = null, globals) {
18884 this.bindingLevel = bindingLevel;
18885 this.parent = parent;
18886 this.globals = globals;
18887 /** Keeps a map from local variables to their BindingData. */
18888 this.map = new Map();
18889 this.referenceNameIndex = 0;
18890 this.restoreViewVariable = null;
18891 if (globals !== undefined) {
18892 for (const name of globals) {
18893 this.set(0, name, variable(name));
18894 }
18895 }
18896 }
18897 static createRootScope() {
18898 return new BindingScope();
18899 }
18900 get(name) {
18901 let current = this;
18902 while (current) {
18903 let value = current.map.get(name);
18904 if (value != null) {
18905 if (current !== this) {
18906 // make a local copy and reset the `declare` state
18907 value = {
18908 retrievalLevel: value.retrievalLevel,
18909 lhs: value.lhs,
18910 declareLocalCallback: value.declareLocalCallback,
18911 declare: false,
18912 priority: value.priority,
18913 localRef: value.localRef
18914 };
18915 // Cache the value locally.
18916 this.map.set(name, value);
18917 // Possibly generate a shared context var
18918 this.maybeGenerateSharedContextVar(value);
18919 this.maybeRestoreView(value.retrievalLevel, value.localRef);
18920 }
18921 if (value.declareLocalCallback && !value.declare) {
18922 value.declare = true;
18923 }
18924 return value.lhs;
18925 }
18926 current = current.parent;
18927 }
18928 // If we get to this point, we are looking for a property on the top level component
18929 // - If level === 0, we are on the top and don't need to re-declare `ctx`.
18930 // - If level > 0, we are in an embedded view. We need to retrieve the name of the
18931 // local var we used to store the component context, e.g. const $comp$ = x();
18932 return this.bindingLevel === 0 ? null : this.getComponentProperty(name);
18933 }
18934 /**
18935 * Create a local variable for later reference.
18936 *
18937 * @param retrievalLevel The level from which this value can be retrieved
18938 * @param name Name of the variable.
18939 * @param lhs AST representing the left hand side of the `let lhs = rhs;`.
18940 * @param priority The sorting priority of this var
18941 * @param declareLocalCallback The callback to invoke when declaring this local var
18942 * @param localRef Whether or not this is a local ref
18943 */
18944 set(retrievalLevel, name, lhs, priority = 0 /* DEFAULT */, declareLocalCallback, localRef) {
18945 if (this.map.has(name)) {
18946 if (localRef) {
18947 // Do not throw an error if it's a local ref and do not update existing value,
18948 // so the first defined ref is always returned.
18949 return this;
18950 }
18951 error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`);
18952 }
18953 this.map.set(name, {
18954 retrievalLevel: retrievalLevel,
18955 lhs: lhs,
18956 declare: false,
18957 declareLocalCallback: declareLocalCallback,
18958 priority: priority,
18959 localRef: localRef || false
18960 });
18961 return this;
18962 }
18963 // Implemented as part of LocalResolver.
18964 getLocal(name) {
18965 return this.get(name);
18966 }
18967 // Implemented as part of LocalResolver.
18968 notifyImplicitReceiverUse() {
18969 if (this.bindingLevel !== 0) {
18970 // Since the implicit receiver is accessed in an embedded view, we need to
18971 // ensure that we declare a shared context variable for the current template
18972 // in the update variables.
18973 this.map.get(SHARED_CONTEXT_KEY + 0).declare = true;
18974 }
18975 }
18976 nestedScope(level, globals) {
18977 const newScope = new BindingScope(level, this, globals);
18978 if (level > 0)
18979 newScope.generateSharedContextVar(0);
18980 return newScope;
18981 }
18982 /**
18983 * Gets or creates a shared context variable and returns its expression. Note that
18984 * this does not mean that the shared variable will be declared. Variables in the
18985 * binding scope will be only declared if they are used.
18986 */
18987 getOrCreateSharedContextVar(retrievalLevel) {
18988 const bindingKey = SHARED_CONTEXT_KEY + retrievalLevel;
18989 if (!this.map.has(bindingKey)) {
18990 this.generateSharedContextVar(retrievalLevel);
18991 }
18992 // Shared context variables are always generated as "ReadVarExpr".
18993 return this.map.get(bindingKey).lhs;
18994 }
18995 getSharedContextName(retrievalLevel) {
18996 const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + retrievalLevel);
18997 // Shared context variables are always generated as "ReadVarExpr".
18998 return sharedCtxObj && sharedCtxObj.declare ? sharedCtxObj.lhs : null;
18999 }
19000 maybeGenerateSharedContextVar(value) {
19001 if (value.priority === 1 /* CONTEXT */ &&
19002 value.retrievalLevel < this.bindingLevel) {
19003 const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + value.retrievalLevel);
19004 if (sharedCtxObj) {
19005 sharedCtxObj.declare = true;
19006 }
19007 else {
19008 this.generateSharedContextVar(value.retrievalLevel);
19009 }
19010 }
19011 }
19012 generateSharedContextVar(retrievalLevel) {
19013 const lhs = variable(CONTEXT_NAME + this.freshReferenceName());
19014 this.map.set(SHARED_CONTEXT_KEY + retrievalLevel, {
19015 retrievalLevel: retrievalLevel,
19016 lhs: lhs,
19017 declareLocalCallback: (scope, relativeLevel) => {
19018 // const ctx_r0 = nextContext(2);
19019 return [lhs.set(generateNextContextExpr(relativeLevel)).toConstDecl()];
19020 },
19021 declare: false,
19022 priority: 2 /* SHARED_CONTEXT */,
19023 localRef: false
19024 });
19025 }
19026 getComponentProperty(name) {
19027 const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0);
19028 componentValue.declare = true;
19029 this.maybeRestoreView(0, false);
19030 return componentValue.lhs.prop(name);
19031 }
19032 maybeRestoreView(retrievalLevel, localRefLookup) {
19033 // We want to restore the current view in listener fns if:
19034 // 1 - we are accessing a value in a parent view, which requires walking the view tree rather
19035 // than using the ctx arg. In this case, the retrieval and binding level will be different.
19036 // 2 - we are looking up a local ref, which requires restoring the view where the local
19037 // ref is stored
19038 if (this.isListenerScope() && (retrievalLevel < this.bindingLevel || localRefLookup)) {
19039 if (!this.parent.restoreViewVariable) {
19040 // parent saves variable to generate a shared `const $s$ = getCurrentView();` instruction
19041 this.parent.restoreViewVariable = variable(this.parent.freshReferenceName());
19042 }
19043 this.restoreViewVariable = this.parent.restoreViewVariable;
19044 }
19045 }
19046 restoreViewStatement() {
19047 // restoreView($state$);
19048 return this.restoreViewVariable ?
19049 [instruction(null, Identifiers$1.restoreView, [this.restoreViewVariable]).toStmt()] :
19050 [];
19051 }
19052 viewSnapshotStatements() {
19053 // const $state$ = getCurrentView();
19054 const getCurrentViewInstruction = instruction(null, Identifiers$1.getCurrentView, []);
19055 return this.restoreViewVariable ?
19056 [this.restoreViewVariable.set(getCurrentViewInstruction).toConstDecl()] :
19057 [];
19058 }
19059 isListenerScope() {
19060 return this.parent && this.parent.bindingLevel === this.bindingLevel;
19061 }
19062 variableDeclarations() {
19063 let currentContextLevel = 0;
19064 return Array.from(this.map.values())
19065 .filter(value => value.declare)
19066 .sort((a, b) => b.retrievalLevel - a.retrievalLevel || b.priority - a.priority)
19067 .reduce((stmts, value) => {
19068 const levelDiff = this.bindingLevel - value.retrievalLevel;
19069 const currStmts = value.declareLocalCallback(this, levelDiff - currentContextLevel);
19070 currentContextLevel = levelDiff;
19071 return stmts.concat(currStmts);
19072 }, []);
19073 }
19074 freshReferenceName() {
19075 let current = this;
19076 // Find the top scope as it maintains the global reference count
19077 while (current.parent)
19078 current = current.parent;
19079 const ref = `${REFERENCE_PREFIX}${current.referenceNameIndex++}`;
19080 return ref;
19081 }
19082 }
19083 /**
19084 * Creates a `CssSelector` given a tag name and a map of attributes
19085 */
19086 function createCssSelector(elementName, attributes) {
19087 const cssSelector = new CssSelector();
19088 const elementNameNoNs = splitNsName(elementName)[1];
19089 cssSelector.setElement(elementNameNoNs);
19090 Object.getOwnPropertyNames(attributes).forEach((name) => {
19091 const nameNoNs = splitNsName(name)[1];
19092 const value = attributes[name];
19093 cssSelector.addAttribute(nameNoNs, value);
19094 if (name.toLowerCase() === 'class') {
19095 const classes = value.trim().split(/\s+/);
19096 classes.forEach(className => cssSelector.addClassName(className));
19097 }
19098 });
19099 return cssSelector;
19100 }
19101 /**
19102 * Creates an array of expressions out of an `ngProjectAs` attributes
19103 * which can be added to the instruction parameters.
19104 */
19105 function getNgProjectAsLiteral(attribute) {
19106 // Parse the attribute value into a CssSelectorList. Note that we only take the
19107 // first selector, because we don't support multiple selectors in ngProjectAs.
19108 const parsedR3Selector = parseSelectorToR3Selector(attribute.value)[0];
19109 return [literal(5 /* ProjectAs */), asLiteral(parsedR3Selector)];
19110 }
19111 /**
19112 * Gets the instruction to generate for an interpolated property
19113 * @param interpolation An Interpolation AST
19114 */
19115 function getPropertyInterpolationExpression(interpolation) {
19116 switch (getInterpolationArgsLength(interpolation)) {
19117 case 1:
19118 return Identifiers$1.propertyInterpolate;
19119 case 3:
19120 return Identifiers$1.propertyInterpolate1;
19121 case 5:
19122 return Identifiers$1.propertyInterpolate2;
19123 case 7:
19124 return Identifiers$1.propertyInterpolate3;
19125 case 9:
19126 return Identifiers$1.propertyInterpolate4;
19127 case 11:
19128 return Identifiers$1.propertyInterpolate5;
19129 case 13:
19130 return Identifiers$1.propertyInterpolate6;
19131 case 15:
19132 return Identifiers$1.propertyInterpolate7;
19133 case 17:
19134 return Identifiers$1.propertyInterpolate8;
19135 default:
19136 return Identifiers$1.propertyInterpolateV;
19137 }
19138 }
19139 /**
19140 * Gets the instruction to generate for an interpolated attribute
19141 * @param interpolation An Interpolation AST
19142 */
19143 function getAttributeInterpolationExpression(interpolation) {
19144 switch (getInterpolationArgsLength(interpolation)) {
19145 case 3:
19146 return Identifiers$1.attributeInterpolate1;
19147 case 5:
19148 return Identifiers$1.attributeInterpolate2;
19149 case 7:
19150 return Identifiers$1.attributeInterpolate3;
19151 case 9:
19152 return Identifiers$1.attributeInterpolate4;
19153 case 11:
19154 return Identifiers$1.attributeInterpolate5;
19155 case 13:
19156 return Identifiers$1.attributeInterpolate6;
19157 case 15:
19158 return Identifiers$1.attributeInterpolate7;
19159 case 17:
19160 return Identifiers$1.attributeInterpolate8;
19161 default:
19162 return Identifiers$1.attributeInterpolateV;
19163 }
19164 }
19165 /**
19166 * Gets the instruction to generate for interpolated text.
19167 * @param interpolation An Interpolation AST
19168 */
19169 function getTextInterpolationExpression(interpolation) {
19170 switch (getInterpolationArgsLength(interpolation)) {
19171 case 1:
19172 return Identifiers$1.textInterpolate;
19173 case 3:
19174 return Identifiers$1.textInterpolate1;
19175 case 5:
19176 return Identifiers$1.textInterpolate2;
19177 case 7:
19178 return Identifiers$1.textInterpolate3;
19179 case 9:
19180 return Identifiers$1.textInterpolate4;
19181 case 11:
19182 return Identifiers$1.textInterpolate5;
19183 case 13:
19184 return Identifiers$1.textInterpolate6;
19185 case 15:
19186 return Identifiers$1.textInterpolate7;
19187 case 17:
19188 return Identifiers$1.textInterpolate8;
19189 default:
19190 return Identifiers$1.textInterpolateV;
19191 }
19192 }
19193 /**
19194 * Parse a template into render3 `Node`s and additional metadata, with no other dependencies.
19195 *
19196 * @param template text of the template to parse
19197 * @param templateUrl URL to use for source mapping of the parsed template
19198 * @param options options to modify how the template is parsed
19199 */
19200 function parseTemplate(template, templateUrl, options = {}) {
19201 var _a;
19202 const { interpolationConfig, preserveWhitespaces, enableI18nLegacyMessageIdFormat } = options;
19203 const isInline = (_a = options.isInline) !== null && _a !== void 0 ? _a : false;
19204 const bindingParser = makeBindingParser(interpolationConfig);
19205 const htmlParser = new HtmlParser();
19206 const parseResult = htmlParser.parse(template, templateUrl, Object.assign(Object.assign({ leadingTriviaChars: LEADING_TRIVIA_CHARS }, options), { tokenizeExpansionForms: true }));
19207 if (!options.alwaysAttemptHtmlToR3AstConversion && parseResult.errors &&
19208 parseResult.errors.length > 0) {
19209 return {
19210 interpolationConfig,
19211 preserveWhitespaces,
19212 template,
19213 templateUrl,
19214 isInline,
19215 errors: parseResult.errors,
19216 nodes: [],
19217 styleUrls: [],
19218 styles: [],
19219 ngContentSelectors: []
19220 };
19221 }
19222 let rootNodes = parseResult.rootNodes;
19223 // process i18n meta information (scan attributes, generate ids)
19224 // before we run whitespace removal process, because existing i18n
19225 // extraction process (ng extract-i18n) relies on a raw content to generate
19226 // message ids
19227 const i18nMetaVisitor = new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ !preserveWhitespaces, enableI18nLegacyMessageIdFormat);
19228 const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes);
19229 if (!options.alwaysAttemptHtmlToR3AstConversion && i18nMetaResult.errors &&
19230 i18nMetaResult.errors.length > 0) {
19231 return {
19232 interpolationConfig,
19233 preserveWhitespaces,
19234 template,
19235 templateUrl,
19236 isInline,
19237 errors: i18nMetaResult.errors,
19238 nodes: [],
19239 styleUrls: [],
19240 styles: [],
19241 ngContentSelectors: []
19242 };
19243 }
19244 rootNodes = i18nMetaResult.rootNodes;
19245 if (!preserveWhitespaces) {
19246 rootNodes = visitAll$1(new WhitespaceVisitor(), rootNodes);
19247 // run i18n meta visitor again in case whitespaces are removed (because that might affect
19248 // generated i18n message content) and first pass indicated that i18n content is present in a
19249 // template. During this pass i18n IDs generated at the first pass will be preserved, so we can
19250 // mimic existing extraction process (ng extract-i18n)
19251 if (i18nMetaVisitor.hasI18nMeta) {
19252 rootNodes = visitAll$1(new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ false), rootNodes);
19253 }
19254 }
19255 const { nodes, errors, styleUrls, styles, ngContentSelectors } = htmlAstToRender3Ast(rootNodes, bindingParser);
19256 errors.push(...parseResult.errors, ...i18nMetaResult.errors);
19257 return {
19258 interpolationConfig,
19259 preserveWhitespaces,
19260 errors: errors.length > 0 ? errors : null,
19261 template,
19262 templateUrl,
19263 isInline,
19264 nodes,
19265 styleUrls,
19266 styles,
19267 ngContentSelectors
19268 };
19269 }
19270 const elementRegistry = new DomElementSchemaRegistry();
19271 /**
19272 * Construct a `BindingParser` with a default configuration.
19273 */
19274 function makeBindingParser(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
19275 return new BindingParser(new IvyParser(new Lexer()), interpolationConfig, elementRegistry, null, []);
19276 }
19277 function resolveSanitizationFn(context, isAttribute) {
19278 switch (context) {
19279 case SecurityContext.HTML:
19280 return importExpr(Identifiers$1.sanitizeHtml);
19281 case SecurityContext.SCRIPT:
19282 return importExpr(Identifiers$1.sanitizeScript);
19283 case SecurityContext.STYLE:
19284 // the compiler does not fill in an instruction for [style.prop?] binding
19285 // values because the style algorithm knows internally what props are subject
19286 // to sanitization (only [attr.style] values are explicitly sanitized)
19287 return isAttribute ? importExpr(Identifiers$1.sanitizeStyle) : null;
19288 case SecurityContext.URL:
19289 return importExpr(Identifiers$1.sanitizeUrl);
19290 case SecurityContext.RESOURCE_URL:
19291 return importExpr(Identifiers$1.sanitizeResourceUrl);
19292 default:
19293 return null;
19294 }
19295 }
19296 function trustedConstAttribute(tagName, attr) {
19297 const value = asLiteral(attr.value);
19298 if (isTrustedTypesSink(tagName, attr.name)) {
19299 switch (elementRegistry.securityContext(tagName, attr.name, /* isAttribute */ true)) {
19300 case SecurityContext.HTML:
19301 return taggedTemplate(importExpr(Identifiers$1.trustConstantHtml), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan);
19302 // NB: no SecurityContext.SCRIPT here, as the corresponding tags are stripped by the compiler.
19303 case SecurityContext.RESOURCE_URL:
19304 return taggedTemplate(importExpr(Identifiers$1.trustConstantResourceUrl), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan);
19305 default:
19306 return value;
19307 }
19308 }
19309 else {
19310 return value;
19311 }
19312 }
19313 function isSingleElementTemplate(children) {
19314 return children.length === 1 && children[0] instanceof Element;
19315 }
19316 function isTextNode(node) {
19317 return node instanceof Text || node instanceof BoundText || node instanceof Icu;
19318 }
19319 function hasTextChildrenOnly(children) {
19320 return children.every(isTextNode);
19321 }
19322 /** Name of the global variable that is used to determine if we use Closure translations or not */
19323 const NG_I18N_CLOSURE_MODE = 'ngI18nClosureMode';
19324 /**
19325 * Generate statements that define a given translation message.
19326 *
19327 * ```
19328 * var I18N_1;
19329 * if (typeof ngI18nClosureMode !== undefined && ngI18nClosureMode) {
19330 * var MSG_EXTERNAL_XXX = goog.getMsg(
19331 * "Some message with {$interpolation}!",
19332 * { "interpolation": "\uFFFD0\uFFFD" }
19333 * );
19334 * I18N_1 = MSG_EXTERNAL_XXX;
19335 * }
19336 * else {
19337 * I18N_1 = $localize`Some message with ${'\uFFFD0\uFFFD'}!`;
19338 * }
19339 * ```
19340 *
19341 * @param message The original i18n AST message node
19342 * @param variable The variable that will be assigned the translation, e.g. `I18N_1`.
19343 * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
19344 * @param params Object mapping placeholder names to their values (e.g.
19345 * `{ "interpolation": "\uFFFD0\uFFFD" }`).
19346 * @param transformFn Optional transformation function that will be applied to the translation (e.g.
19347 * post-processing).
19348 * @returns An array of statements that defined a given translation.
19349 */
19350 function getTranslationDeclStmts(message, variable, closureVar, params = {}, transformFn) {
19351 const statements = [
19352 declareI18nVariable(variable),
19353 ifStmt(createClosureModeGuard(), createGoogleGetMsgStatements(variable, message, closureVar, i18nFormatPlaceholderNames(params, /* useCamelCase */ true)), createLocalizeStatements(variable, message, i18nFormatPlaceholderNames(params, /* useCamelCase */ false))),
19354 ];
19355 if (transformFn) {
19356 statements.push(new ExpressionStatement(variable.set(transformFn(variable))));
19357 }
19358 return statements;
19359 }
19360 /**
19361 * Create the expression that will be used to guard the closure mode block
19362 * It is equivalent to:
19363 *
19364 * ```
19365 * typeof ngI18nClosureMode !== undefined && ngI18nClosureMode
19366 * ```
19367 */
19368 function createClosureModeGuard() {
19369 return typeofExpr(variable(NG_I18N_CLOSURE_MODE))
19370 .notIdentical(literal('undefined', STRING_TYPE))
19371 .and(variable(NG_I18N_CLOSURE_MODE));
19372 }
19373
19374 /**
19375 * @license
19376 * Copyright Google LLC All Rights Reserved.
19377 *
19378 * Use of this source code is governed by an MIT-style license that can be
19379 * found in the LICENSE file at https://angular.io/license
19380 */
19381 // This regex matches any binding names that contain the "attr." prefix, e.g. "attr.required"
19382 // If there is a match, the first matching group will contain the attribute name to bind.
19383 const ATTR_REGEX = /attr\.([^\]]+)/;
19384 function baseDirectiveFields(meta, constantPool, bindingParser) {
19385 const definitionMap = new DefinitionMap();
19386 const selectors = parseSelectorToR3Selector(meta.selector);
19387 // e.g. `type: MyDirective`
19388 definitionMap.set('type', meta.internalType);
19389 // e.g. `selectors: [['', 'someDir', '']]`
19390 if (selectors.length > 0) {
19391 definitionMap.set('selectors', asLiteral(selectors));
19392 }
19393 if (meta.queries.length > 0) {
19394 // e.g. `contentQueries: (rf, ctx, dirIndex) => { ... }
19395 definitionMap.set('contentQueries', createContentQueriesFunction(meta.queries, constantPool, meta.name));
19396 }
19397 if (meta.viewQueries.length) {
19398 definitionMap.set('viewQuery', createViewQueriesFunction(meta.viewQueries, constantPool, meta.name));
19399 }
19400 // e.g. `hostBindings: (rf, ctx) => { ... }
19401 definitionMap.set('hostBindings', createHostBindingsFunction(meta.host, meta.typeSourceSpan, bindingParser, constantPool, meta.selector || '', meta.name, definitionMap));
19402 // e.g 'inputs: {a: 'a'}`
19403 definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
19404 // e.g 'outputs: {a: 'a'}`
19405 definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
19406 if (meta.exportAs !== null) {
19407 definitionMap.set('exportAs', literalArr(meta.exportAs.map(e => literal(e))));
19408 }
19409 return definitionMap;
19410 }
19411 /**
19412 * Add features to the definition map.
19413 */
19414 function addFeatures(definitionMap, meta) {
19415 // e.g. `features: [NgOnChangesFeature]`
19416 const features = [];
19417 const providers = meta.providers;
19418 const viewProviders = meta.viewProviders;
19419 if (providers || viewProviders) {
19420 const args = [providers || new LiteralArrayExpr([])];
19421 if (viewProviders) {
19422 args.push(viewProviders);
19423 }
19424 features.push(importExpr(Identifiers$1.ProvidersFeature).callFn(args));
19425 }
19426 if (meta.usesInheritance) {
19427 features.push(importExpr(Identifiers$1.InheritDefinitionFeature));
19428 }
19429 if (meta.fullInheritance) {
19430 features.push(importExpr(Identifiers$1.CopyDefinitionFeature));
19431 }
19432 if (meta.lifecycle.usesOnChanges) {
19433 features.push(importExpr(Identifiers$1.NgOnChangesFeature));
19434 }
19435 if (features.length) {
19436 definitionMap.set('features', literalArr(features));
19437 }
19438 }
19439 /**
19440 * Compile a directive for the render3 runtime as defined by the `R3DirectiveMetadata`.
19441 */
19442 function compileDirectiveFromMetadata(meta, constantPool, bindingParser) {
19443 const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
19444 addFeatures(definitionMap, meta);
19445 const expression = importExpr(Identifiers$1.defineDirective).callFn([definitionMap.toLiteralMap()]);
19446 const type = createDirectiveType(meta);
19447 return { expression, type };
19448 }
19449 /**
19450 * Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`.
19451 */
19452 function compileComponentFromMetadata(meta, constantPool, bindingParser) {
19453 const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
19454 addFeatures(definitionMap, meta);
19455 const selector = meta.selector && CssSelector.parse(meta.selector);
19456 const firstSelector = selector && selector[0];
19457 // e.g. `attr: ["class", ".my.app"]`
19458 // This is optional an only included if the first selector of a component specifies attributes.
19459 if (firstSelector) {
19460 const selectorAttributes = firstSelector.getAttrs();
19461 if (selectorAttributes.length) {
19462 definitionMap.set('attrs', constantPool.getConstLiteral(literalArr(selectorAttributes.map(value => value != null ? literal(value) : literal(undefined))),
19463 /* forceShared */ true));
19464 }
19465 }
19466 // Generate the CSS matcher that recognize directive
19467 let directiveMatcher = null;
19468 if (meta.directives.length > 0) {
19469 const matcher = new SelectorMatcher();
19470 for (const { selector, type } of meta.directives) {
19471 matcher.addSelectables(CssSelector.parse(selector), type);
19472 }
19473 directiveMatcher = matcher;
19474 }
19475 // e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
19476 const templateTypeName = meta.name;
19477 const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
19478 const directivesUsed = new Set();
19479 const pipesUsed = new Set();
19480 const changeDetection = meta.changeDetection;
19481 const template = meta.template;
19482 const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, directiveMatcher, directivesUsed, meta.pipes, pipesUsed, Identifiers$1.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds);
19483 const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
19484 // We need to provide this so that dynamically generated components know what
19485 // projected content blocks to pass through to the component when it is instantiated.
19486 const ngContentSelectors = templateBuilder.getNgContentSelectors();
19487 if (ngContentSelectors) {
19488 definitionMap.set('ngContentSelectors', ngContentSelectors);
19489 }
19490 // e.g. `decls: 2`
19491 definitionMap.set('decls', literal(templateBuilder.getConstCount()));
19492 // e.g. `vars: 2`
19493 definitionMap.set('vars', literal(templateBuilder.getVarCount()));
19494 // Generate `consts` section of ComponentDef:
19495 // - either as an array:
19496 // `consts: [['one', 'two'], ['three', 'four']]`
19497 // - or as a factory function in case additional statements are present (to support i18n):
19498 // `consts: function() { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0]; }`
19499 const { constExpressions, prepareStatements } = templateBuilder.getConsts();
19500 if (constExpressions.length > 0) {
19501 let constsExpr = literalArr(constExpressions);
19502 // Prepare statements are present - turn `consts` into a function.
19503 if (prepareStatements.length > 0) {
19504 constsExpr = fn([], [...prepareStatements, new ReturnStatement(constsExpr)]);
19505 }
19506 definitionMap.set('consts', constsExpr);
19507 }
19508 definitionMap.set('template', templateFunctionExpression);
19509 // e.g. `directives: [MyDirective]`
19510 if (directivesUsed.size) {
19511 const directivesList = literalArr(Array.from(directivesUsed));
19512 const directivesExpr = compileDeclarationList(directivesList, meta.declarationListEmitMode);
19513 definitionMap.set('directives', directivesExpr);
19514 }
19515 // e.g. `pipes: [MyPipe]`
19516 if (pipesUsed.size) {
19517 const pipesList = literalArr(Array.from(pipesUsed));
19518 const pipesExpr = compileDeclarationList(pipesList, meta.declarationListEmitMode);
19519 definitionMap.set('pipes', pipesExpr);
19520 }
19521 if (meta.encapsulation === null) {
19522 meta.encapsulation = ViewEncapsulation.Emulated;
19523 }
19524 // e.g. `styles: [str1, str2]`
19525 if (meta.styles && meta.styles.length) {
19526 const styleValues = meta.encapsulation == ViewEncapsulation.Emulated ?
19527 compileStyles(meta.styles, CONTENT_ATTR, HOST_ATTR) :
19528 meta.styles;
19529 const strings = styleValues.map(str => constantPool.getConstLiteral(literal(str)));
19530 definitionMap.set('styles', literalArr(strings));
19531 }
19532 else if (meta.encapsulation === ViewEncapsulation.Emulated) {
19533 // If there is no style, don't generate css selectors on elements
19534 meta.encapsulation = ViewEncapsulation.None;
19535 }
19536 // Only set view encapsulation if it's not the default value
19537 if (meta.encapsulation !== ViewEncapsulation.Emulated) {
19538 definitionMap.set('encapsulation', literal(meta.encapsulation));
19539 }
19540 // e.g. `animation: [trigger('123', [])]`
19541 if (meta.animations !== null) {
19542 definitionMap.set('data', literalMap([{ key: 'animation', value: meta.animations, quoted: false }]));
19543 }
19544 // Only set the change detection flag if it's defined and it's not the default.
19545 if (changeDetection != null && changeDetection !== ChangeDetectionStrategy.Default) {
19546 definitionMap.set('changeDetection', literal(changeDetection));
19547 }
19548 const expression = importExpr(Identifiers$1.defineComponent).callFn([definitionMap.toLiteralMap()]);
19549 const type = createComponentType(meta);
19550 return { expression, type };
19551 }
19552 /**
19553 * Creates the type specification from the component meta. This type is inserted into .d.ts files
19554 * to be consumed by upstream compilations.
19555 */
19556 function createComponentType(meta) {
19557 const typeParams = createDirectiveTypeParams(meta);
19558 typeParams.push(stringArrayAsType(meta.template.ngContentSelectors));
19559 return expressionType(importExpr(Identifiers$1.ComponentDefWithMeta, typeParams));
19560 }
19561 /**
19562 * Compiles the array literal of declarations into an expression according to the provided emit
19563 * mode.
19564 */
19565 function compileDeclarationList(list, mode) {
19566 switch (mode) {
19567 case 0 /* Direct */:
19568 // directives: [MyDir],
19569 return list;
19570 case 1 /* Closure */:
19571 // directives: function () { return [MyDir]; }
19572 return fn([], [new ReturnStatement(list)]);
19573 case 2 /* ClosureResolved */:
19574 // directives: function () { return [MyDir].map(ng.resolveForwardRef); }
19575 const resolvedList = list.callMethod('map', [importExpr(Identifiers$1.resolveForwardRef)]);
19576 return fn([], [new ReturnStatement(resolvedList)]);
19577 }
19578 }
19579 function prepareQueryParams(query, constantPool) {
19580 const parameters = [getQueryPredicate(query, constantPool), literal(toQueryFlags(query))];
19581 if (query.read) {
19582 parameters.push(query.read);
19583 }
19584 return parameters;
19585 }
19586 /**
19587 * Translates query flags into `TQueryFlags` type in packages/core/src/render3/interfaces/query.ts
19588 * @param query
19589 */
19590 function toQueryFlags(query) {
19591 return (query.descendants ? 1 /* descendants */ : 0 /* none */) |
19592 (query.static ? 2 /* isStatic */ : 0 /* none */) |
19593 (query.emitDistinctChangesOnly ? 4 /* emitDistinctChangesOnly */ : 0 /* none */);
19594 }
19595 function convertAttributesToExpressions(attributes) {
19596 const values = [];
19597 for (let key of Object.getOwnPropertyNames(attributes)) {
19598 const value = attributes[key];
19599 values.push(literal(key), value);
19600 }
19601 return values;
19602 }
19603 // Define and update any content queries
19604 function createContentQueriesFunction(queries, constantPool, name) {
19605 const createStatements = [];
19606 const updateStatements = [];
19607 const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
19608 for (const query of queries) {
19609 // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null);
19610 createStatements.push(importExpr(Identifiers$1.contentQuery)
19611 .callFn([variable('dirIndex'), ...prepareQueryParams(query, constantPool)])
19612 .toStmt());
19613 // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
19614 const temporary = tempAllocator();
19615 const getQueryList = importExpr(Identifiers$1.loadQuery).callFn([]);
19616 const refresh = importExpr(Identifiers$1.queryRefresh).callFn([temporary.set(getQueryList)]);
19617 const updateDirective = variable(CONTEXT_NAME)
19618 .prop(query.propertyName)
19619 .set(query.first ? temporary.prop('first') : temporary);
19620 updateStatements.push(refresh.and(updateDirective).toStmt());
19621 }
19622 const contentQueriesFnName = name ? `${name}_ContentQueries` : null;
19623 return fn([
19624 new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null),
19625 new FnParam('dirIndex', null)
19626 ], [
19627 renderFlagCheckIfStmt(1 /* Create */, createStatements),
19628 renderFlagCheckIfStmt(2 /* Update */, updateStatements)
19629 ], INFERRED_TYPE, null, contentQueriesFnName);
19630 }
19631 function stringAsType(str) {
19632 return expressionType(literal(str));
19633 }
19634 function stringMapAsType(map) {
19635 const mapValues = Object.keys(map).map(key => {
19636 const value = Array.isArray(map[key]) ? map[key][0] : map[key];
19637 return {
19638 key,
19639 value: literal(value),
19640 quoted: true,
19641 };
19642 });
19643 return expressionType(literalMap(mapValues));
19644 }
19645 function stringArrayAsType(arr) {
19646 return arr.length > 0 ? expressionType(literalArr(arr.map(value => literal(value)))) :
19647 NONE_TYPE;
19648 }
19649 function createDirectiveTypeParams(meta) {
19650 // On the type side, remove newlines from the selector as it will need to fit into a TypeScript
19651 // string literal, which must be on one line.
19652 const selectorForType = meta.selector !== null ? meta.selector.replace(/\n/g, '') : null;
19653 return [
19654 typeWithParameters(meta.type.type, meta.typeArgumentCount),
19655 selectorForType !== null ? stringAsType(selectorForType) : NONE_TYPE,
19656 meta.exportAs !== null ? stringArrayAsType(meta.exportAs) : NONE_TYPE,
19657 stringMapAsType(meta.inputs),
19658 stringMapAsType(meta.outputs),
19659 stringArrayAsType(meta.queries.map(q => q.propertyName)),
19660 ];
19661 }
19662 /**
19663 * Creates the type specification from the directive meta. This type is inserted into .d.ts files
19664 * to be consumed by upstream compilations.
19665 */
19666 function createDirectiveType(meta) {
19667 const typeParams = createDirectiveTypeParams(meta);
19668 return expressionType(importExpr(Identifiers$1.DirectiveDefWithMeta, typeParams));
19669 }
19670 // Define and update any view queries
19671 function createViewQueriesFunction(viewQueries, constantPool, name) {
19672 const createStatements = [];
19673 const updateStatements = [];
19674 const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
19675 viewQueries.forEach((query) => {
19676 // creation, e.g. r3.viewQuery(somePredicate, true);
19677 const queryDefinition = importExpr(Identifiers$1.viewQuery).callFn(prepareQueryParams(query, constantPool));
19678 createStatements.push(queryDefinition.toStmt());
19679 // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
19680 const temporary = tempAllocator();
19681 const getQueryList = importExpr(Identifiers$1.loadQuery).callFn([]);
19682 const refresh = importExpr(Identifiers$1.queryRefresh).callFn([temporary.set(getQueryList)]);
19683 const updateDirective = variable(CONTEXT_NAME)
19684 .prop(query.propertyName)
19685 .set(query.first ? temporary.prop('first') : temporary);
19686 updateStatements.push(refresh.and(updateDirective).toStmt());
19687 });
19688 const viewQueryFnName = name ? `${name}_Query` : null;
19689 return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
19690 renderFlagCheckIfStmt(1 /* Create */, createStatements),
19691 renderFlagCheckIfStmt(2 /* Update */, updateStatements)
19692 ], INFERRED_TYPE, null, viewQueryFnName);
19693 }
19694 // Return a host binding function or null if one is not necessary.
19695 function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindingParser, constantPool, selector, name, definitionMap) {
19696 const bindingContext = variable(CONTEXT_NAME);
19697 const styleBuilder = new StylingBuilder(bindingContext);
19698 const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
19699 if (styleAttr !== undefined) {
19700 styleBuilder.registerStyleAttr(styleAttr);
19701 }
19702 if (classAttr !== undefined) {
19703 styleBuilder.registerClassAttr(classAttr);
19704 }
19705 const createStatements = [];
19706 const updateStatements = [];
19707 const hostBindingSourceSpan = typeSourceSpan;
19708 const directiveSummary = metadataAsSummary(hostBindingsMetadata);
19709 // Calculate host event bindings
19710 const eventBindings = bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
19711 if (eventBindings && eventBindings.length) {
19712 const listeners = createHostListeners(eventBindings, name);
19713 createStatements.push(...listeners);
19714 }
19715 // Calculate the host property bindings
19716 const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
19717 const allOtherBindings = [];
19718 // We need to calculate the total amount of binding slots required by
19719 // all the instructions together before any value conversions happen.
19720 // Value conversions may require additional slots for interpolation and
19721 // bindings with pipes. These calculates happen after this block.
19722 let totalHostVarsCount = 0;
19723 bindings && bindings.forEach((binding) => {
19724 const stylingInputWasSet = styleBuilder.registerInputBasedOnName(binding.name, binding.expression, hostBindingSourceSpan);
19725 if (stylingInputWasSet) {
19726 totalHostVarsCount += MIN_STYLING_BINDING_SLOTS_REQUIRED;
19727 }
19728 else {
19729 allOtherBindings.push(binding);
19730 totalHostVarsCount++;
19731 }
19732 });
19733 let valueConverter;
19734 const getValueConverter = () => {
19735 if (!valueConverter) {
19736 const hostVarsCountFn = (numSlots) => {
19737 const originalVarsCount = totalHostVarsCount;
19738 totalHostVarsCount += numSlots;
19739 return originalVarsCount;
19740 };
19741 valueConverter = new ValueConverter(constantPool, () => error('Unexpected node'), // new nodes are illegal here
19742 hostVarsCountFn, () => error('Unexpected pipe')); // pipes are illegal here
19743 }
19744 return valueConverter;
19745 };
19746 const propertyBindings = [];
19747 const attributeBindings = [];
19748 const syntheticHostBindings = [];
19749 allOtherBindings.forEach((binding) => {
19750 // resolve literal arrays and literal objects
19751 const value = binding.expression.visit(getValueConverter());
19752 const bindingExpr = bindingFn(bindingContext, value);
19753 const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
19754 const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
19755 .filter(context => context !== SecurityContext.NONE);
19756 let sanitizerFn = null;
19757 if (securityContexts.length) {
19758 if (securityContexts.length === 2 &&
19759 securityContexts.indexOf(SecurityContext.URL) > -1 &&
19760 securityContexts.indexOf(SecurityContext.RESOURCE_URL) > -1) {
19761 // Special case for some URL attributes (such as "src" and "href") that may be a part
19762 // of different security contexts. In this case we use special santitization function and
19763 // select the actual sanitizer at runtime based on a tag name that is provided while
19764 // invoking sanitization function.
19765 sanitizerFn = importExpr(Identifiers$1.sanitizeUrlOrResourceUrl);
19766 }
19767 else {
19768 sanitizerFn = resolveSanitizationFn(securityContexts[0], isAttribute);
19769 }
19770 }
19771 const instructionParams = [literal(bindingName), bindingExpr.currValExpr];
19772 if (sanitizerFn) {
19773 instructionParams.push(sanitizerFn);
19774 }
19775 updateStatements.push(...bindingExpr.stmts);
19776 if (instruction === Identifiers$1.hostProperty) {
19777 propertyBindings.push(instructionParams);
19778 }
19779 else if (instruction === Identifiers$1.attribute) {
19780 attributeBindings.push(instructionParams);
19781 }
19782 else if (instruction === Identifiers$1.syntheticHostProperty) {
19783 syntheticHostBindings.push(instructionParams);
19784 }
19785 else {
19786 updateStatements.push(importExpr(instruction).callFn(instructionParams).toStmt());
19787 }
19788 });
19789 if (propertyBindings.length > 0) {
19790 updateStatements.push(chainedInstruction(Identifiers$1.hostProperty, propertyBindings).toStmt());
19791 }
19792 if (attributeBindings.length > 0) {
19793 updateStatements.push(chainedInstruction(Identifiers$1.attribute, attributeBindings).toStmt());
19794 }
19795 if (syntheticHostBindings.length > 0) {
19796 updateStatements.push(chainedInstruction(Identifiers$1.syntheticHostProperty, syntheticHostBindings).toStmt());
19797 }
19798 // since we're dealing with directives/components and both have hostBinding
19799 // functions, we need to generate a special hostAttrs instruction that deals
19800 // with both the assignment of styling as well as static attributes to the host
19801 // element. The instruction below will instruct all initial styling (styling
19802 // that is inside of a host binding within a directive/component) to be attached
19803 // to the host element alongside any of the provided host attributes that were
19804 // collected earlier.
19805 const hostAttrs = convertAttributesToExpressions(hostBindingsMetadata.attributes);
19806 styleBuilder.assignHostAttrs(hostAttrs, definitionMap);
19807 if (styleBuilder.hasBindings) {
19808 // finally each binding that was registered in the statement above will need to be added to
19809 // the update block of a component/directive templateFn/hostBindingsFn so that the bindings
19810 // are evaluated and updated for the element.
19811 styleBuilder.buildUpdateLevelInstructions(getValueConverter()).forEach(instruction => {
19812 if (instruction.calls.length > 0) {
19813 const calls = [];
19814 instruction.calls.forEach(call => {
19815 // we subtract a value of `1` here because the binding slot was already allocated
19816 // at the top of this method when all the input bindings were counted.
19817 totalHostVarsCount +=
19818 Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
19819 calls.push(convertStylingCall(call, bindingContext, bindingFn));
19820 });
19821 updateStatements.push(chainedInstruction(instruction.reference, calls).toStmt());
19822 }
19823 });
19824 }
19825 if (totalHostVarsCount) {
19826 definitionMap.set('hostVars', literal(totalHostVarsCount));
19827 }
19828 if (createStatements.length > 0 || updateStatements.length > 0) {
19829 const hostBindingsFnName = name ? `${name}_HostBindings` : null;
19830 const statements = [];
19831 if (createStatements.length > 0) {
19832 statements.push(renderFlagCheckIfStmt(1 /* Create */, createStatements));
19833 }
19834 if (updateStatements.length > 0) {
19835 statements.push(renderFlagCheckIfStmt(2 /* Update */, updateStatements));
19836 }
19837 return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], statements, INFERRED_TYPE, null, hostBindingsFnName);
19838 }
19839 return null;
19840 }
19841 function bindingFn(implicit, value) {
19842 return convertPropertyBinding(null, implicit, value, 'b', BindingForm.Expression, () => error('Unexpected interpolation'));
19843 }
19844 function convertStylingCall(call, bindingContext, bindingFn) {
19845 return call.params(value => bindingFn(bindingContext, value).currValExpr);
19846 }
19847 function getBindingNameAndInstruction(binding) {
19848 let bindingName = binding.name;
19849 let instruction;
19850 // Check to see if this is an attr binding or a property binding
19851 const attrMatches = bindingName.match(ATTR_REGEX);
19852 if (attrMatches) {
19853 bindingName = attrMatches[1];
19854 instruction = Identifiers$1.attribute;
19855 }
19856 else {
19857 if (binding.isAnimation) {
19858 bindingName = prepareSyntheticPropertyName(bindingName);
19859 // host bindings that have a synthetic property (e.g. @foo) should always be rendered
19860 // in the context of the component and not the parent. Therefore there is a special
19861 // compatibility instruction available for this purpose.
19862 instruction = Identifiers$1.syntheticHostProperty;
19863 }
19864 else {
19865 instruction = Identifiers$1.hostProperty;
19866 }
19867 }
19868 return { bindingName, instruction, isAttribute: !!attrMatches };
19869 }
19870 function createHostListeners(eventBindings, name) {
19871 const listeners = [];
19872 const syntheticListeners = [];
19873 const instructions = [];
19874 eventBindings.forEach(binding => {
19875 let bindingName = binding.name && sanitizeIdentifier(binding.name);
19876 const bindingFnName = binding.type === 1 /* Animation */ ?
19877 prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase) :
19878 bindingName;
19879 const handlerName = name && bindingName ? `${name}_${bindingFnName}_HostBindingHandler` : null;
19880 const params = prepareEventListenerParameters(BoundEvent.fromParsedEvent(binding), handlerName);
19881 if (binding.type == 1 /* Animation */) {
19882 syntheticListeners.push(params);
19883 }
19884 else {
19885 listeners.push(params);
19886 }
19887 });
19888 if (syntheticListeners.length > 0) {
19889 instructions.push(chainedInstruction(Identifiers$1.syntheticHostListener, syntheticListeners).toStmt());
19890 }
19891 if (listeners.length > 0) {
19892 instructions.push(chainedInstruction(Identifiers$1.listener, listeners).toStmt());
19893 }
19894 return instructions;
19895 }
19896 function metadataAsSummary(meta) {
19897 // clang-format off
19898 return {
19899 // This is used by the BindingParser, which only deals with listeners and properties. There's no
19900 // need to pass attributes to it.
19901 hostAttributes: {},
19902 hostListeners: meta.listeners,
19903 hostProperties: meta.properties,
19904 };
19905 // clang-format on
19906 }
19907 const HOST_REG_EXP = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/;
19908 function parseHostBindings(host) {
19909 const attributes = {};
19910 const listeners = {};
19911 const properties = {};
19912 const specialAttributes = {};
19913 for (const key of Object.keys(host)) {
19914 const value = host[key];
19915 const matches = key.match(HOST_REG_EXP);
19916 if (matches === null) {
19917 switch (key) {
19918 case 'class':
19919 if (typeof value !== 'string') {
19920 // TODO(alxhub): make this a diagnostic.
19921 throw new Error(`Class binding must be string`);
19922 }
19923 specialAttributes.classAttr = value;
19924 break;
19925 case 'style':
19926 if (typeof value !== 'string') {
19927 // TODO(alxhub): make this a diagnostic.
19928 throw new Error(`Style binding must be string`);
19929 }
19930 specialAttributes.styleAttr = value;
19931 break;
19932 default:
19933 if (typeof value === 'string') {
19934 attributes[key] = literal(value);
19935 }
19936 else {
19937 attributes[key] = value;
19938 }
19939 }
19940 }
19941 else if (matches[1 /* Binding */] != null) {
19942 if (typeof value !== 'string') {
19943 // TODO(alxhub): make this a diagnostic.
19944 throw new Error(`Property binding must be string`);
19945 }
19946 // synthetic properties (the ones that have a `@` as a prefix)
19947 // are still treated the same as regular properties. Therefore
19948 // there is no point in storing them in a separate map.
19949 properties[matches[1 /* Binding */]] = value;
19950 }
19951 else if (matches[2 /* Event */] != null) {
19952 if (typeof value !== 'string') {
19953 // TODO(alxhub): make this a diagnostic.
19954 throw new Error(`Event binding must be string`);
19955 }
19956 listeners[matches[2 /* Event */]] = value;
19957 }
19958 }
19959 return { attributes, listeners, properties, specialAttributes };
19960 }
19961 /**
19962 * Verifies host bindings and returns the list of errors (if any). Empty array indicates that a
19963 * given set of host bindings has no errors.
19964 *
19965 * @param bindings set of host bindings to verify.
19966 * @param sourceSpan source span where host bindings were defined.
19967 * @returns array of errors associated with a given set of host bindings.
19968 */
19969 function verifyHostBindings(bindings, sourceSpan) {
19970 const summary = metadataAsSummary(bindings);
19971 // TODO: abstract out host bindings verification logic and use it instead of
19972 // creating events and properties ASTs to detect errors (FW-996)
19973 const bindingParser = makeBindingParser();
19974 bindingParser.createDirectiveHostEventAsts(summary, sourceSpan);
19975 bindingParser.createBoundHostProperties(summary, sourceSpan);
19976 return bindingParser.errors;
19977 }
19978 function compileStyles(styles, selector, hostSelector) {
19979 const shadowCss = new ShadowCss();
19980 return styles.map(style => {
19981 return shadowCss.shimCssText(style, selector, hostSelector);
19982 });
19983 }
19984
19985 /**
19986 * @license
19987 * Copyright Google LLC All Rights Reserved.
19988 *
19989 * Use of this source code is governed by an MIT-style license that can be
19990 * found in the LICENSE file at https://angular.io/license
19991 */
19992 /**
19993 * An interface for retrieving documents by URL that the compiler uses
19994 * to load templates.
19995 */
19996 class ResourceLoader {
19997 get(url) {
19998 return '';
19999 }
20000 }
20001
20002 /**
20003 * @license
20004 * Copyright Google LLC All Rights Reserved.
20005 *
20006 * Use of this source code is governed by an MIT-style license that can be
20007 * found in the LICENSE file at https://angular.io/license
20008 */
20009 class CompilerFacadeImpl {
20010 constructor(jitEvaluator = new JitEvaluator()) {
20011 this.jitEvaluator = jitEvaluator;
20012 this.R3ResolvedDependencyType = R3ResolvedDependencyType;
20013 this.R3FactoryTarget = R3FactoryTarget;
20014 this.ResourceLoader = ResourceLoader;
20015 this.elementSchemaRegistry = new DomElementSchemaRegistry();
20016 }
20017 compilePipe(angularCoreEnv, sourceMapUrl, facade) {
20018 const metadata = {
20019 name: facade.name,
20020 type: wrapReference(facade.type),
20021 internalType: new WrappedNodeExpr(facade.type),
20022 typeArgumentCount: facade.typeArgumentCount,
20023 deps: convertR3DependencyMetadataArray(facade.deps),
20024 pipeName: facade.pipeName,
20025 pure: facade.pure,
20026 };
20027 const res = compilePipeFromMetadata(metadata);
20028 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
20029 }
20030 compilePipeDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
20031 const meta = convertDeclarePipeFacadeToMetadata(declaration);
20032 const res = compilePipeFromMetadata(meta);
20033 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
20034 }
20035 compileInjectable(angularCoreEnv, sourceMapUrl, facade) {
20036 const { expression, statements } = compileInjectable({
20037 name: facade.name,
20038 type: wrapReference(facade.type),
20039 internalType: new WrappedNodeExpr(facade.type),
20040 typeArgumentCount: facade.typeArgumentCount,
20041 providedIn: computeProvidedIn(facade.providedIn),
20042 useClass: wrapExpression(facade, USE_CLASS),
20043 useFactory: wrapExpression(facade, USE_FACTORY),
20044 useValue: wrapExpression(facade, USE_VALUE),
20045 useExisting: wrapExpression(facade, USE_EXISTING),
20046 userDeps: convertR3DependencyMetadataArray(facade.userDeps) || undefined,
20047 });
20048 return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements);
20049 }
20050 compileInjector(angularCoreEnv, sourceMapUrl, facade) {
20051 const meta = {
20052 name: facade.name,
20053 type: wrapReference(facade.type),
20054 internalType: new WrappedNodeExpr(facade.type),
20055 deps: convertR3DependencyMetadataArray(facade.deps),
20056 providers: new WrappedNodeExpr(facade.providers),
20057 imports: facade.imports.map(i => new WrappedNodeExpr(i)),
20058 };
20059 const res = compileInjector(meta);
20060 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
20061 }
20062 compileNgModule(angularCoreEnv, sourceMapUrl, facade) {
20063 const meta = {
20064 type: wrapReference(facade.type),
20065 internalType: new WrappedNodeExpr(facade.type),
20066 adjacentType: new WrappedNodeExpr(facade.type),
20067 bootstrap: facade.bootstrap.map(wrapReference),
20068 declarations: facade.declarations.map(wrapReference),
20069 imports: facade.imports.map(wrapReference),
20070 exports: facade.exports.map(wrapReference),
20071 emitInline: true,
20072 containsForwardDecls: false,
20073 schemas: facade.schemas ? facade.schemas.map(wrapReference) : null,
20074 id: facade.id ? new WrappedNodeExpr(facade.id) : null,
20075 };
20076 const res = compileNgModule(meta);
20077 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
20078 }
20079 compileDirective(angularCoreEnv, sourceMapUrl, facade) {
20080 const meta = convertDirectiveFacadeToMetadata(facade);
20081 return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
20082 }
20083 compileDirectiveDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
20084 const typeSourceSpan = this.createParseSourceSpan('Directive', declaration.type.name, sourceMapUrl);
20085 const meta = convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan);
20086 return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
20087 }
20088 compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta) {
20089 const constantPool = new ConstantPool();
20090 const bindingParser = makeBindingParser();
20091 const res = compileDirectiveFromMetadata(meta, constantPool, bindingParser);
20092 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
20093 }
20094 compileComponent(angularCoreEnv, sourceMapUrl, facade) {
20095 // Parse the template and check for errors.
20096 const { template, interpolation } = parseJitTemplate(facade.template, facade.name, sourceMapUrl, facade.preserveWhitespaces, facade.interpolation);
20097 // Compile the component metadata, including template, into an expression.
20098 const meta = Object.assign(Object.assign(Object.assign({}, facade), convertDirectiveFacadeToMetadata(facade)), { selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(), template, declarationListEmitMode: 0 /* Direct */, styles: [...facade.styles, ...template.styles], encapsulation: facade.encapsulation, interpolation, changeDetection: facade.changeDetection, animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null, viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) :
20099 null, relativeContextFilePath: '', i18nUseExternalIds: true });
20100 const jitExpressionSourceMap = `ng:///${facade.name}.js`;
20101 return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
20102 }
20103 compileComponentDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
20104 const typeSourceSpan = this.createParseSourceSpan('Component', declaration.type.name, sourceMapUrl);
20105 const meta = convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl);
20106 return this.compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta);
20107 }
20108 compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta) {
20109 const constantPool = new ConstantPool();
20110 const bindingParser = makeBindingParser(meta.interpolation);
20111 const res = compileComponentFromMetadata(meta, constantPool, bindingParser);
20112 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
20113 }
20114 compileFactory(angularCoreEnv, sourceMapUrl, meta) {
20115 const factoryRes = compileFactoryFunction({
20116 name: meta.name,
20117 type: wrapReference(meta.type),
20118 internalType: new WrappedNodeExpr(meta.type),
20119 typeArgumentCount: meta.typeArgumentCount,
20120 deps: convertR3DependencyMetadataArray(meta.deps),
20121 injectFn: meta.injectFn === 'directiveInject' ? Identifiers.directiveInject :
20122 Identifiers.inject,
20123 target: meta.target,
20124 });
20125 return this.jitExpression(factoryRes.factory, angularCoreEnv, sourceMapUrl, factoryRes.statements);
20126 }
20127 createParseSourceSpan(kind, typeName, sourceUrl) {
20128 return r3JitTypeSourceSpan(kind, typeName, sourceUrl);
20129 }
20130 /**
20131 * JIT compiles an expression and returns the result of executing that expression.
20132 *
20133 * @param def the definition which will be compiled and executed to get the value to patch
20134 * @param context an object map of @angular/core symbol names to symbols which will be available
20135 * in the context of the compiled expression
20136 * @param sourceUrl a URL to use for the source map of the compiled expression
20137 * @param preStatements a collection of statements that should be evaluated before the expression.
20138 */
20139 jitExpression(def, context, sourceUrl, preStatements) {
20140 // The ConstantPool may contain Statements which declare variables used in the final expression.
20141 // Therefore, its statements need to precede the actual JIT operation. The final statement is a
20142 // declaration of $def which is set to the expression being compiled.
20143 const statements = [
20144 ...preStatements,
20145 new DeclareVarStmt('$def', def, undefined, [StmtModifier.Exported]),
20146 ];
20147 const res = this.jitEvaluator.evaluateStatements(sourceUrl, statements, new R3JitReflector(context), /* enableSourceMaps */ true);
20148 return res['$def'];
20149 }
20150 }
20151 const USE_CLASS = Object.keys({ useClass: null })[0];
20152 const USE_FACTORY = Object.keys({ useFactory: null })[0];
20153 const USE_VALUE = Object.keys({ useValue: null })[0];
20154 const USE_EXISTING = Object.keys({ useExisting: null })[0];
20155 const wrapReference = function (value) {
20156 const wrapped = new WrappedNodeExpr(value);
20157 return { value: wrapped, type: wrapped };
20158 };
20159 function convertToR3QueryMetadata(facade) {
20160 return Object.assign(Object.assign({}, facade), { predicate: Array.isArray(facade.predicate) ? facade.predicate :
20161 new WrappedNodeExpr(facade.predicate), read: facade.read ? new WrappedNodeExpr(facade.read) : null, static: facade.static, emitDistinctChangesOnly: facade.emitDistinctChangesOnly });
20162 }
20163 function convertQueryDeclarationToMetadata(declaration) {
20164 var _a, _b, _c, _d;
20165 return {
20166 propertyName: declaration.propertyName,
20167 first: (_a = declaration.first) !== null && _a !== void 0 ? _a : false,
20168 predicate: Array.isArray(declaration.predicate) ? declaration.predicate :
20169 new WrappedNodeExpr(declaration.predicate),
20170 descendants: (_b = declaration.descendants) !== null && _b !== void 0 ? _b : false,
20171 read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
20172 static: (_c = declaration.static) !== null && _c !== void 0 ? _c : false,
20173 emitDistinctChangesOnly: (_d = declaration.emitDistinctChangesOnly) !== null && _d !== void 0 ? _d : true,
20174 };
20175 }
20176 function convertDirectiveFacadeToMetadata(facade) {
20177 const inputsFromMetadata = parseInputOutputs(facade.inputs || []);
20178 const outputsFromMetadata = parseInputOutputs(facade.outputs || []);
20179 const propMetadata = facade.propMetadata;
20180 const inputsFromType = {};
20181 const outputsFromType = {};
20182 for (const field in propMetadata) {
20183 if (propMetadata.hasOwnProperty(field)) {
20184 propMetadata[field].forEach(ann => {
20185 if (isInput(ann)) {
20186 inputsFromType[field] =
20187 ann.bindingPropertyName ? [ann.bindingPropertyName, field] : field;
20188 }
20189 else if (isOutput(ann)) {
20190 outputsFromType[field] = ann.bindingPropertyName || field;
20191 }
20192 });
20193 }
20194 }
20195 return Object.assign(Object.assign({}, facade), { typeSourceSpan: facade.typeSourceSpan, type: wrapReference(facade.type), internalType: new WrappedNodeExpr(facade.type), deps: convertR3DependencyMetadataArray(facade.deps), host: extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host), inputs: Object.assign(Object.assign({}, inputsFromMetadata), inputsFromType), outputs: Object.assign(Object.assign({}, outputsFromMetadata), outputsFromType), queries: facade.queries.map(convertToR3QueryMetadata), providers: facade.providers != null ? new WrappedNodeExpr(facade.providers) : null, viewQueries: facade.viewQueries.map(convertToR3QueryMetadata), fullInheritance: false });
20196 }
20197 function convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan) {
20198 var _a, _b, _c, _d, _e, _f, _g, _h;
20199 return {
20200 name: declaration.type.name,
20201 type: wrapReference(declaration.type),
20202 typeSourceSpan,
20203 internalType: new WrappedNodeExpr(declaration.type),
20204 selector: (_a = declaration.selector) !== null && _a !== void 0 ? _a : null,
20205 inputs: (_b = declaration.inputs) !== null && _b !== void 0 ? _b : {},
20206 outputs: (_c = declaration.outputs) !== null && _c !== void 0 ? _c : {},
20207 host: convertHostDeclarationToMetadata(declaration.host),
20208 queries: ((_d = declaration.queries) !== null && _d !== void 0 ? _d : []).map(convertQueryDeclarationToMetadata),
20209 viewQueries: ((_e = declaration.viewQueries) !== null && _e !== void 0 ? _e : []).map(convertQueryDeclarationToMetadata),
20210 providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) :
20211 null,
20212 exportAs: (_f = declaration.exportAs) !== null && _f !== void 0 ? _f : null,
20213 usesInheritance: (_g = declaration.usesInheritance) !== null && _g !== void 0 ? _g : false,
20214 lifecycle: { usesOnChanges: (_h = declaration.usesOnChanges) !== null && _h !== void 0 ? _h : false },
20215 deps: null,
20216 typeArgumentCount: 0,
20217 fullInheritance: false,
20218 };
20219 }
20220 function convertHostDeclarationToMetadata(host = {}) {
20221 var _a, _b, _c;
20222 return {
20223 attributes: convertOpaqueValuesToExpressions((_a = host.attributes) !== null && _a !== void 0 ? _a : {}),
20224 listeners: (_b = host.listeners) !== null && _b !== void 0 ? _b : {},
20225 properties: (_c = host.properties) !== null && _c !== void 0 ? _c : {},
20226 specialAttributes: {
20227 classAttr: host.classAttribute,
20228 styleAttr: host.styleAttribute,
20229 },
20230 };
20231 }
20232 function convertOpaqueValuesToExpressions(obj) {
20233 const result = {};
20234 for (const key of Object.keys(obj)) {
20235 result[key] = new WrappedNodeExpr(obj[key]);
20236 }
20237 return result;
20238 }
20239 function convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl) {
20240 var _a, _b, _c, _d, _e;
20241 const { template, interpolation } = parseJitTemplate(declaration.template, declaration.type.name, sourceMapUrl, (_a = declaration.preserveWhitespaces) !== null && _a !== void 0 ? _a : false, declaration.interpolation);
20242 return Object.assign(Object.assign({}, convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan)), { template, styles: (_b = declaration.styles) !== null && _b !== void 0 ? _b : [], directives: ((_c = declaration.directives) !== null && _c !== void 0 ? _c : []).map(convertUsedDirectiveDeclarationToMetadata), pipes: convertUsedPipesToMetadata(declaration.pipes), viewProviders: declaration.viewProviders !== undefined ?
20243 new WrappedNodeExpr(declaration.viewProviders) :
20244 null, animations: declaration.animations !== undefined ? new WrappedNodeExpr(declaration.animations) :
20245 null, changeDetection: (_d = declaration.changeDetection) !== null && _d !== void 0 ? _d : ChangeDetectionStrategy.Default, encapsulation: (_e = declaration.encapsulation) !== null && _e !== void 0 ? _e : ViewEncapsulation.Emulated, interpolation, declarationListEmitMode: 2 /* ClosureResolved */, relativeContextFilePath: '', i18nUseExternalIds: true });
20246 }
20247 function convertUsedDirectiveDeclarationToMetadata(declaration) {
20248 var _a, _b, _c;
20249 return {
20250 selector: declaration.selector,
20251 type: new WrappedNodeExpr(declaration.type),
20252 inputs: (_a = declaration.inputs) !== null && _a !== void 0 ? _a : [],
20253 outputs: (_b = declaration.outputs) !== null && _b !== void 0 ? _b : [],
20254 exportAs: (_c = declaration.exportAs) !== null && _c !== void 0 ? _c : null,
20255 };
20256 }
20257 function convertUsedPipesToMetadata(declaredPipes) {
20258 const pipes = new Map();
20259 if (declaredPipes === undefined) {
20260 return pipes;
20261 }
20262 for (const pipeName of Object.keys(declaredPipes)) {
20263 const pipeType = declaredPipes[pipeName];
20264 pipes.set(pipeName, new WrappedNodeExpr(pipeType));
20265 }
20266 return pipes;
20267 }
20268 function parseJitTemplate(template, typeName, sourceMapUrl, preserveWhitespaces, interpolation) {
20269 const interpolationConfig = interpolation ? InterpolationConfig.fromArray(interpolation) : DEFAULT_INTERPOLATION_CONFIG;
20270 // Parse the template and check for errors.
20271 const parsed = parseTemplate(template, sourceMapUrl, { preserveWhitespaces: preserveWhitespaces, interpolationConfig });
20272 if (parsed.errors !== null) {
20273 const errors = parsed.errors.map(err => err.toString()).join(', ');
20274 throw new Error(`Errors during JIT compilation of template for ${typeName}: ${errors}`);
20275 }
20276 return { template: parsed, interpolation: interpolationConfig };
20277 }
20278 function wrapExpression(obj, property) {
20279 if (obj.hasOwnProperty(property)) {
20280 return new WrappedNodeExpr(obj[property]);
20281 }
20282 else {
20283 return undefined;
20284 }
20285 }
20286 function computeProvidedIn(providedIn) {
20287 if (providedIn == null || typeof providedIn === 'string') {
20288 return new LiteralExpr(providedIn);
20289 }
20290 else {
20291 return new WrappedNodeExpr(providedIn);
20292 }
20293 }
20294 function convertR3DependencyMetadata(facade) {
20295 let tokenExpr;
20296 if (facade.token === null) {
20297 tokenExpr = new LiteralExpr(null);
20298 }
20299 else if (facade.resolved === R3ResolvedDependencyType.Attribute) {
20300 tokenExpr = new LiteralExpr(facade.token);
20301 }
20302 else {
20303 tokenExpr = new WrappedNodeExpr(facade.token);
20304 }
20305 return {
20306 token: tokenExpr,
20307 attribute: null,
20308 resolved: facade.resolved,
20309 host: facade.host,
20310 optional: facade.optional,
20311 self: facade.self,
20312 skipSelf: facade.skipSelf,
20313 };
20314 }
20315 function convertR3DependencyMetadataArray(facades) {
20316 return facades == null ? null : facades.map(convertR3DependencyMetadata);
20317 }
20318 function extractHostBindings(propMetadata, sourceSpan, host) {
20319 // First parse the declarations from the metadata.
20320 const bindings = parseHostBindings(host || {});
20321 // After that check host bindings for errors
20322 const errors = verifyHostBindings(bindings, sourceSpan);
20323 if (errors.length) {
20324 throw new Error(errors.map((error) => error.msg).join('\n'));
20325 }
20326 // Next, loop over the properties of the object, looking for @HostBinding and @HostListener.
20327 for (const field in propMetadata) {
20328 if (propMetadata.hasOwnProperty(field)) {
20329 propMetadata[field].forEach(ann => {
20330 if (isHostBinding(ann)) {
20331 // Since this is a decorator, we know that the value is a class member. Always access it
20332 // through `this` so that further down the line it can't be confused for a literal value
20333 // (e.g. if there's a property called `true`).
20334 bindings.properties[ann.hostPropertyName || field] =
20335 getSafePropertyAccessString('this', field);
20336 }
20337 else if (isHostListener(ann)) {
20338 bindings.listeners[ann.eventName || field] = `${field}(${(ann.args || []).join(',')})`;
20339 }
20340 });
20341 }
20342 }
20343 return bindings;
20344 }
20345 function isHostBinding(value) {
20346 return value.ngMetadataName === 'HostBinding';
20347 }
20348 function isHostListener(value) {
20349 return value.ngMetadataName === 'HostListener';
20350 }
20351 function isInput(value) {
20352 return value.ngMetadataName === 'Input';
20353 }
20354 function isOutput(value) {
20355 return value.ngMetadataName === 'Output';
20356 }
20357 function parseInputOutputs(values) {
20358 return values.reduce((map, value) => {
20359 const [field, property] = value.split(',').map(piece => piece.trim());
20360 map[field] = property || field;
20361 return map;
20362 }, {});
20363 }
20364 function convertDeclarePipeFacadeToMetadata(declaration) {
20365 var _a;
20366 return {
20367 name: declaration.type.name,
20368 type: wrapReference(declaration.type),
20369 internalType: new WrappedNodeExpr(declaration.type),
20370 typeArgumentCount: 0,
20371 pipeName: declaration.name,
20372 deps: null,
20373 pure: (_a = declaration.pure) !== null && _a !== void 0 ? _a : true,
20374 };
20375 }
20376 function publishFacade(global) {
20377 const ng = global.ng || (global.ng = {});
20378 ng.ɵcompilerFacade = new CompilerFacadeImpl();
20379 }
20380
20381 /**
20382 * @license
20383 * Copyright Google LLC All Rights Reserved.
20384 *
20385 * Use of this source code is governed by an MIT-style license that can be
20386 * found in the LICENSE file at https://angular.io/license
20387 */
20388 const VERSION$1 = new Version('11.2.4');
20389
20390 /**
20391 * @license
20392 * Copyright Google LLC All Rights Reserved.
20393 *
20394 * Use of this source code is governed by an MIT-style license that can be
20395 * found in the LICENSE file at https://angular.io/license
20396 */
20397 var _VisitorMode;
20398 (function (_VisitorMode) {
20399 _VisitorMode[_VisitorMode["Extract"] = 0] = "Extract";
20400 _VisitorMode[_VisitorMode["Merge"] = 1] = "Merge";
20401 })(_VisitorMode || (_VisitorMode = {}));
20402
20403 /**
20404 * @license
20405 * Copyright Google LLC All Rights Reserved.
20406 *
20407 * Use of this source code is governed by an MIT-style license that can be
20408 * found in the LICENSE file at https://angular.io/license
20409 */
20410 var LifecycleHooks;
20411 (function (LifecycleHooks) {
20412 LifecycleHooks[LifecycleHooks["OnInit"] = 0] = "OnInit";
20413 LifecycleHooks[LifecycleHooks["OnDestroy"] = 1] = "OnDestroy";
20414 LifecycleHooks[LifecycleHooks["DoCheck"] = 2] = "DoCheck";
20415 LifecycleHooks[LifecycleHooks["OnChanges"] = 3] = "OnChanges";
20416 LifecycleHooks[LifecycleHooks["AfterContentInit"] = 4] = "AfterContentInit";
20417 LifecycleHooks[LifecycleHooks["AfterContentChecked"] = 5] = "AfterContentChecked";
20418 LifecycleHooks[LifecycleHooks["AfterViewInit"] = 6] = "AfterViewInit";
20419 LifecycleHooks[LifecycleHooks["AfterViewChecked"] = 7] = "AfterViewChecked";
20420 })(LifecycleHooks || (LifecycleHooks = {}));
20421 const LIFECYCLE_HOOKS_VALUES = [
20422 LifecycleHooks.OnInit, LifecycleHooks.OnDestroy, LifecycleHooks.DoCheck, LifecycleHooks.OnChanges,
20423 LifecycleHooks.AfterContentInit, LifecycleHooks.AfterContentChecked, LifecycleHooks.AfterViewInit,
20424 LifecycleHooks.AfterViewChecked
20425 ];
20426
20427 /**
20428 * @license
20429 * Copyright Google LLC All Rights Reserved.
20430 *
20431 * Use of this source code is governed by an MIT-style license that can be
20432 * found in the LICENSE file at https://angular.io/license
20433 */
20434 const LOG_VAR = variable('_l');
20435
20436 /**
20437 * @license
20438 * Copyright Google LLC All Rights Reserved.
20439 *
20440 * Use of this source code is governed by an MIT-style license that can be
20441 * found in the LICENSE file at https://angular.io/license
20442 */
20443 const LOG_VAR$1 = variable('_l');
20444 const VIEW_VAR = variable('_v');
20445 const CHECK_VAR = variable('_ck');
20446 const COMP_VAR = variable('_co');
20447 const EVENT_NAME_VAR = variable('en');
20448 const ALLOW_DEFAULT_VAR = variable(`ad`);
20449
20450 /**
20451 * @license
20452 * Copyright Google LLC All Rights Reserved.
20453 *
20454 * Use of this source code is governed by an MIT-style license that can be
20455 * found in the LICENSE file at https://angular.io/license
20456 */
20457 /**
20458 * The index of each URI component in the return value of goog.uri.utils.split.
20459 * @enum {number}
20460 */
20461 var _ComponentIndex;
20462 (function (_ComponentIndex) {
20463 _ComponentIndex[_ComponentIndex["Scheme"] = 1] = "Scheme";
20464 _ComponentIndex[_ComponentIndex["UserInfo"] = 2] = "UserInfo";
20465 _ComponentIndex[_ComponentIndex["Domain"] = 3] = "Domain";
20466 _ComponentIndex[_ComponentIndex["Port"] = 4] = "Port";
20467 _ComponentIndex[_ComponentIndex["Path"] = 5] = "Path";
20468 _ComponentIndex[_ComponentIndex["QueryData"] = 6] = "QueryData";
20469 _ComponentIndex[_ComponentIndex["Fragment"] = 7] = "Fragment";
20470 })(_ComponentIndex || (_ComponentIndex = {}));
20471
20472 /**
20473 * @license
20474 * Copyright Google LLC All Rights Reserved.
20475 *
20476 * Use of this source code is governed by an MIT-style license that can be
20477 * found in the LICENSE file at https://angular.io/license
20478 */
20479 /**
20480 * Processes `Target`s with a given set of directives and performs a binding operation, which
20481 * returns an object similar to TypeScript's `ts.TypeChecker` that contains knowledge about the
20482 * target.
20483 */
20484 class R3TargetBinder {
20485 constructor(directiveMatcher) {
20486 this.directiveMatcher = directiveMatcher;
20487 }
20488 /**
20489 * Perform a binding operation on the given `Target` and return a `BoundTarget` which contains
20490 * metadata about the types referenced in the template.
20491 */
20492 bind(target) {
20493 if (!target.template) {
20494 // TODO(alxhub): handle targets which contain things like HostBindings, etc.
20495 throw new Error('Binding without a template not yet supported');
20496 }
20497 // First, parse the template into a `Scope` structure. This operation captures the syntactic
20498 // scopes in the template and makes them available for later use.
20499 const scope = Scope.apply(target.template);
20500 // Use the `Scope` to extract the entities present at every level of the template.
20501 const templateEntities = extractTemplateEntities(scope);
20502 // Next, perform directive matching on the template using the `DirectiveBinder`. This returns:
20503 // - directives: Map of nodes (elements & ng-templates) to the directives on them.
20504 // - bindings: Map of inputs, outputs, and attributes to the directive/element that claims
20505 // them. TODO(alxhub): handle multiple directives claiming an input/output/etc.
20506 // - references: Map of #references to their targets.
20507 const { directives, bindings, references } = DirectiveBinder.apply(target.template, this.directiveMatcher);
20508 // Finally, run the TemplateBinder to bind references, variables, and other entities within the
20509 // template. This extracts all the metadata that doesn't depend on directive matching.
20510 const { expressions, symbols, nestingLevel, usedPipes } = TemplateBinder.apply(target.template, scope);
20511 return new R3BoundTarget(target, directives, bindings, references, expressions, symbols, nestingLevel, templateEntities, usedPipes);
20512 }
20513 }
20514 /**
20515 * Represents a binding scope within a template.
20516 *
20517 * Any variables, references, or other named entities declared within the template will
20518 * be captured and available by name in `namedEntities`. Additionally, child templates will
20519 * be analyzed and have their child `Scope`s available in `childScopes`.
20520 */
20521 class Scope {
20522 constructor(parentScope, template) {
20523 this.parentScope = parentScope;
20524 this.template = template;
20525 /**
20526 * Named members of the `Scope`, such as `Reference`s or `Variable`s.
20527 */
20528 this.namedEntities = new Map();
20529 /**
20530 * Child `Scope`s for immediately nested `Template`s.
20531 */
20532 this.childScopes = new Map();
20533 }
20534 static newRootScope() {
20535 return new Scope(null, null);
20536 }
20537 /**
20538 * Process a template (either as a `Template` sub-template with variables, or a plain array of
20539 * template `Node`s) and construct its `Scope`.
20540 */
20541 static apply(template) {
20542 const scope = Scope.newRootScope();
20543 scope.ingest(template);
20544 return scope;
20545 }
20546 /**
20547 * Internal method to process the template and populate the `Scope`.
20548 */
20549 ingest(template) {
20550 if (template instanceof Template) {
20551 // Variables on an <ng-template> are defined in the inner scope.
20552 template.variables.forEach(node => this.visitVariable(node));
20553 // Process the nodes of the template.
20554 template.children.forEach(node => node.visit(this));
20555 }
20556 else {
20557 // No overarching `Template` instance, so process the nodes directly.
20558 template.forEach(node => node.visit(this));
20559 }
20560 }
20561 visitElement(element) {
20562 // `Element`s in the template may have `Reference`s which are captured in the scope.
20563 element.references.forEach(node => this.visitReference(node));
20564 // Recurse into the `Element`'s children.
20565 element.children.forEach(node => node.visit(this));
20566 }
20567 visitTemplate(template) {
20568 // References on a <ng-template> are defined in the outer scope, so capture them before
20569 // processing the template's child scope.
20570 template.references.forEach(node => this.visitReference(node));
20571 // Next, create an inner scope and process the template within it.
20572 const scope = new Scope(this, template);
20573 scope.ingest(template);
20574 this.childScopes.set(template, scope);
20575 }
20576 visitVariable(variable) {
20577 // Declare the variable if it's not already.
20578 this.maybeDeclare(variable);
20579 }
20580 visitReference(reference) {
20581 // Declare the variable if it's not already.
20582 this.maybeDeclare(reference);
20583 }
20584 // Unused visitors.
20585 visitContent(content) { }
20586 visitBoundAttribute(attr) { }
20587 visitBoundEvent(event) { }
20588 visitBoundText(text) { }
20589 visitText(text) { }
20590 visitTextAttribute(attr) { }
20591 visitIcu(icu) { }
20592 maybeDeclare(thing) {
20593 // Declare something with a name, as long as that name isn't taken.
20594 if (!this.namedEntities.has(thing.name)) {
20595 this.namedEntities.set(thing.name, thing);
20596 }
20597 }
20598 /**
20599 * Look up a variable within this `Scope`.
20600 *
20601 * This can recurse into a parent `Scope` if it's available.
20602 */
20603 lookup(name) {
20604 if (this.namedEntities.has(name)) {
20605 // Found in the local scope.
20606 return this.namedEntities.get(name);
20607 }
20608 else if (this.parentScope !== null) {
20609 // Not in the local scope, but there's a parent scope so check there.
20610 return this.parentScope.lookup(name);
20611 }
20612 else {
20613 // At the top level and it wasn't found.
20614 return null;
20615 }
20616 }
20617 /**
20618 * Get the child scope for a `Template`.
20619 *
20620 * This should always be defined.
20621 */
20622 getChildScope(template) {
20623 const res = this.childScopes.get(template);
20624 if (res === undefined) {
20625 throw new Error(`Assertion error: child scope for ${template} not found`);
20626 }
20627 return res;
20628 }
20629 }
20630 /**
20631 * Processes a template and matches directives on nodes (elements and templates).
20632 *
20633 * Usually used via the static `apply()` method.
20634 */
20635 class DirectiveBinder {
20636 constructor(matcher, directives, bindings, references) {
20637 this.matcher = matcher;
20638 this.directives = directives;
20639 this.bindings = bindings;
20640 this.references = references;
20641 }
20642 /**
20643 * Process a template (list of `Node`s) and perform directive matching against each node.
20644 *
20645 * @param template the list of template `Node`s to match (recursively).
20646 * @param selectorMatcher a `SelectorMatcher` containing the directives that are in scope for
20647 * this template.
20648 * @returns three maps which contain information about directives in the template: the
20649 * `directives` map which lists directives matched on each node, the `bindings` map which
20650 * indicates which directives claimed which bindings (inputs, outputs, etc), and the `references`
20651 * map which resolves #references (`Reference`s) within the template to the named directive or
20652 * template node.
20653 */
20654 static apply(template, selectorMatcher) {
20655 const directives = new Map();
20656 const bindings = new Map();
20657 const references = new Map();
20658 const matcher = new DirectiveBinder(selectorMatcher, directives, bindings, references);
20659 matcher.ingest(template);
20660 return { directives, bindings, references };
20661 }
20662 ingest(template) {
20663 template.forEach(node => node.visit(this));
20664 }
20665 visitElement(element) {
20666 this.visitElementOrTemplate(element.name, element);
20667 }
20668 visitTemplate(template) {
20669 this.visitElementOrTemplate('ng-template', template);
20670 }
20671 visitElementOrTemplate(elementName, node) {
20672 // First, determine the HTML shape of the node for the purpose of directive matching.
20673 // Do this by building up a `CssSelector` for the node.
20674 const cssSelector = createCssSelector(elementName, getAttrsForDirectiveMatching(node));
20675 // Next, use the `SelectorMatcher` to get the list of directives on the node.
20676 const directives = [];
20677 this.matcher.match(cssSelector, (_, directive) => directives.push(directive));
20678 if (directives.length > 0) {
20679 this.directives.set(node, directives);
20680 }
20681 // Resolve any references that are created on this node.
20682 node.references.forEach(ref => {
20683 let dirTarget = null;
20684 // If the reference expression is empty, then it matches the "primary" directive on the node
20685 // (if there is one). Otherwise it matches the host node itself (either an element or
20686 // <ng-template> node).
20687 if (ref.value.trim() === '') {
20688 // This could be a reference to a component if there is one.
20689 dirTarget = directives.find(dir => dir.isComponent) || null;
20690 }
20691 else {
20692 // This should be a reference to a directive exported via exportAs.
20693 dirTarget =
20694 directives.find(dir => dir.exportAs !== null && dir.exportAs.some(value => value === ref.value)) ||
20695 null;
20696 // Check if a matching directive was found.
20697 if (dirTarget === null) {
20698 // No matching directive was found - this reference points to an unknown target. Leave it
20699 // unmapped.
20700 return;
20701 }
20702 }
20703 if (dirTarget !== null) {
20704 // This reference points to a directive.
20705 this.references.set(ref, { directive: dirTarget, node });
20706 }
20707 else {
20708 // This reference points to the node itself.
20709 this.references.set(ref, node);
20710 }
20711 });
20712 const setAttributeBinding = (attribute, ioType) => {
20713 const dir = directives.find(dir => dir[ioType].hasBindingPropertyName(attribute.name));
20714 const binding = dir !== undefined ? dir : node;
20715 this.bindings.set(attribute, binding);
20716 };
20717 // Node inputs (bound attributes) and text attributes can be bound to an
20718 // input on a directive.
20719 node.inputs.forEach(input => setAttributeBinding(input, 'inputs'));
20720 node.attributes.forEach(attr => setAttributeBinding(attr, 'inputs'));
20721 if (node instanceof Template) {
20722 node.templateAttrs.forEach(attr => setAttributeBinding(attr, 'inputs'));
20723 }
20724 // Node outputs (bound events) can be bound to an output on a directive.
20725 node.outputs.forEach(output => setAttributeBinding(output, 'outputs'));
20726 // Recurse into the node's children.
20727 node.children.forEach(child => child.visit(this));
20728 }
20729 // Unused visitors.
20730 visitContent(content) { }
20731 visitVariable(variable) { }
20732 visitReference(reference) { }
20733 visitTextAttribute(attribute) { }
20734 visitBoundAttribute(attribute) { }
20735 visitBoundEvent(attribute) { }
20736 visitBoundAttributeOrEvent(node) { }
20737 visitText(text) { }
20738 visitBoundText(text) { }
20739 visitIcu(icu) { }
20740 }
20741 /**
20742 * Processes a template and extract metadata about expressions and symbols within.
20743 *
20744 * This is a companion to the `DirectiveBinder` that doesn't require knowledge of directives matched
20745 * within the template in order to operate.
20746 *
20747 * Expressions are visited by the superclass `RecursiveAstVisitor`, with custom logic provided
20748 * by overridden methods from that visitor.
20749 */
20750 class TemplateBinder extends RecursiveAstVisitor {
20751 constructor(bindings, symbols, usedPipes, nestingLevel, scope, template, level) {
20752 super();
20753 this.bindings = bindings;
20754 this.symbols = symbols;
20755 this.usedPipes = usedPipes;
20756 this.nestingLevel = nestingLevel;
20757 this.scope = scope;
20758 this.template = template;
20759 this.level = level;
20760 this.pipesUsed = [];
20761 // Save a bit of processing time by constructing this closure in advance.
20762 this.visitNode = (node) => node.visit(this);
20763 }
20764 // This method is defined to reconcile the type of TemplateBinder since both
20765 // RecursiveAstVisitor and Visitor define the visit() method in their
20766 // interfaces.
20767 visit(node, context) {
20768 if (node instanceof AST) {
20769 node.visit(this, context);
20770 }
20771 else {
20772 node.visit(this);
20773 }
20774 }
20775 /**
20776 * Process a template and extract metadata about expressions and symbols within.
20777 *
20778 * @param template the nodes of the template to process
20779 * @param scope the `Scope` of the template being processed.
20780 * @returns three maps which contain metadata about the template: `expressions` which interprets
20781 * special `AST` nodes in expressions as pointing to references or variables declared within the
20782 * template, `symbols` which maps those variables and references to the nested `Template` which
20783 * declares them, if any, and `nestingLevel` which associates each `Template` with a integer
20784 * nesting level (how many levels deep within the template structure the `Template` is), starting
20785 * at 1.
20786 */
20787 static apply(template, scope) {
20788 const expressions = new Map();
20789 const symbols = new Map();
20790 const nestingLevel = new Map();
20791 const usedPipes = new Set();
20792 // The top-level template has nesting level 0.
20793 const binder = new TemplateBinder(expressions, symbols, usedPipes, nestingLevel, scope, template instanceof Template ? template : null, 0);
20794 binder.ingest(template);
20795 return { expressions, symbols, nestingLevel, usedPipes };
20796 }
20797 ingest(template) {
20798 if (template instanceof Template) {
20799 // For <ng-template>s, process only variables and child nodes. Inputs, outputs, templateAttrs,
20800 // and references were all processed in the scope of the containing template.
20801 template.variables.forEach(this.visitNode);
20802 template.children.forEach(this.visitNode);
20803 // Set the nesting level.
20804 this.nestingLevel.set(template, this.level);
20805 }
20806 else {
20807 // Visit each node from the top-level template.
20808 template.forEach(this.visitNode);
20809 }
20810 }
20811 visitElement(element) {
20812 // Visit the inputs, outputs, and children of the element.
20813 element.inputs.forEach(this.visitNode);
20814 element.outputs.forEach(this.visitNode);
20815 element.children.forEach(this.visitNode);
20816 }
20817 visitTemplate(template) {
20818 // First, visit inputs, outputs and template attributes of the template node.
20819 template.inputs.forEach(this.visitNode);
20820 template.outputs.forEach(this.visitNode);
20821 template.templateAttrs.forEach(this.visitNode);
20822 // References are also evaluated in the outer context.
20823 template.references.forEach(this.visitNode);
20824 // Next, recurse into the template using its scope, and bumping the nesting level up by one.
20825 const childScope = this.scope.getChildScope(template);
20826 const binder = new TemplateBinder(this.bindings, this.symbols, this.usedPipes, this.nestingLevel, childScope, template, this.level + 1);
20827 binder.ingest(template);
20828 }
20829 visitVariable(variable) {
20830 // Register the `Variable` as a symbol in the current `Template`.
20831 if (this.template !== null) {
20832 this.symbols.set(variable, this.template);
20833 }
20834 }
20835 visitReference(reference) {
20836 // Register the `Reference` as a symbol in the current `Template`.
20837 if (this.template !== null) {
20838 this.symbols.set(reference, this.template);
20839 }
20840 }
20841 // Unused template visitors
20842 visitText(text) { }
20843 visitContent(content) { }
20844 visitTextAttribute(attribute) { }
20845 visitIcu(icu) {
20846 Object.keys(icu.vars).forEach(key => icu.vars[key].visit(this));
20847 Object.keys(icu.placeholders).forEach(key => icu.placeholders[key].visit(this));
20848 }
20849 // The remaining visitors are concerned with processing AST expressions within template bindings
20850 visitBoundAttribute(attribute) {
20851 attribute.value.visit(this);
20852 }
20853 visitBoundEvent(event) {
20854 event.handler.visit(this);
20855 }
20856 visitBoundText(text) {
20857 text.value.visit(this);
20858 }
20859 visitPipe(ast, context) {
20860 this.usedPipes.add(ast.name);
20861 return super.visitPipe(ast, context);
20862 }
20863 // These five types of AST expressions can refer to expression roots, which could be variables
20864 // or references in the current scope.
20865 visitPropertyRead(ast, context) {
20866 this.maybeMap(context, ast, ast.name);
20867 return super.visitPropertyRead(ast, context);
20868 }
20869 visitSafePropertyRead(ast, context) {
20870 this.maybeMap(context, ast, ast.name);
20871 return super.visitSafePropertyRead(ast, context);
20872 }
20873 visitPropertyWrite(ast, context) {
20874 this.maybeMap(context, ast, ast.name);
20875 return super.visitPropertyWrite(ast, context);
20876 }
20877 visitMethodCall(ast, context) {
20878 this.maybeMap(context, ast, ast.name);
20879 return super.visitMethodCall(ast, context);
20880 }
20881 visitSafeMethodCall(ast, context) {
20882 this.maybeMap(context, ast, ast.name);
20883 return super.visitSafeMethodCall(ast, context);
20884 }
20885 maybeMap(scope, ast, name) {
20886 // If the receiver of the expression isn't the `ImplicitReceiver`, this isn't the root of an
20887 // `AST` expression that maps to a `Variable` or `Reference`.
20888 if (!(ast.receiver instanceof ImplicitReceiver)) {
20889 return;
20890 }
20891 // Check whether the name exists in the current scope. If so, map it. Otherwise, the name is
20892 // probably a property on the top-level component context.
20893 let target = this.scope.lookup(name);
20894 if (target !== null) {
20895 this.bindings.set(ast, target);
20896 }
20897 }
20898 }
20899 /**
20900 * Metadata container for a `Target` that allows queries for specific bits of metadata.
20901 *
20902 * See `BoundTarget` for documentation on the individual methods.
20903 */
20904 class R3BoundTarget {
20905 constructor(target, directives, bindings, references, exprTargets, symbols, nestingLevel, templateEntities, usedPipes) {
20906 this.target = target;
20907 this.directives = directives;
20908 this.bindings = bindings;
20909 this.references = references;
20910 this.exprTargets = exprTargets;
20911 this.symbols = symbols;
20912 this.nestingLevel = nestingLevel;
20913 this.templateEntities = templateEntities;
20914 this.usedPipes = usedPipes;
20915 }
20916 getEntitiesInTemplateScope(template) {
20917 var _a;
20918 return (_a = this.templateEntities.get(template)) !== null && _a !== void 0 ? _a : new Set();
20919 }
20920 getDirectivesOfNode(node) {
20921 return this.directives.get(node) || null;
20922 }
20923 getReferenceTarget(ref) {
20924 return this.references.get(ref) || null;
20925 }
20926 getConsumerOfBinding(binding) {
20927 return this.bindings.get(binding) || null;
20928 }
20929 getExpressionTarget(expr) {
20930 return this.exprTargets.get(expr) || null;
20931 }
20932 getTemplateOfSymbol(symbol) {
20933 return this.symbols.get(symbol) || null;
20934 }
20935 getNestingLevel(template) {
20936 return this.nestingLevel.get(template) || 0;
20937 }
20938 getUsedDirectives() {
20939 const set = new Set();
20940 this.directives.forEach(dirs => dirs.forEach(dir => set.add(dir)));
20941 return Array.from(set.values());
20942 }
20943 getUsedPipes() {
20944 return Array.from(this.usedPipes);
20945 }
20946 }
20947 function extractTemplateEntities(rootScope) {
20948 const entityMap = new Map();
20949 function extractScopeEntities(scope) {
20950 if (entityMap.has(scope.template)) {
20951 return entityMap.get(scope.template);
20952 }
20953 const currentEntities = scope.namedEntities;
20954 let templateEntities;
20955 if (scope.parentScope !== null) {
20956 templateEntities = new Map([...extractScopeEntities(scope.parentScope), ...currentEntities]);
20957 }
20958 else {
20959 templateEntities = new Map(currentEntities);
20960 }
20961 entityMap.set(scope.template, templateEntities);
20962 return templateEntities;
20963 }
20964 const scopesToProcess = [rootScope];
20965 while (scopesToProcess.length > 0) {
20966 const scope = scopesToProcess.pop();
20967 for (const childScope of scope.childScopes.values()) {
20968 scopesToProcess.push(childScope);
20969 }
20970 extractScopeEntities(scope);
20971 }
20972 const templateEntities = new Map();
20973 for (const [template, entities] of entityMap) {
20974 templateEntities.set(template, new Set(entities.values()));
20975 }
20976 return templateEntities;
20977 }
20978
20979 /**
20980 * @license
20981 * Copyright Google LLC All Rights Reserved.
20982 *
20983 * Use of this source code is governed by an MIT-style license that can be
20984 * found in the LICENSE file at https://angular.io/license
20985 */
20986 /**
20987 * Creates an array literal expression from the given array, mapping all values to an expression
20988 * using the provided mapping function. If the array is empty or null, then null is returned.
20989 *
20990 * @param values The array to transfer into literal array expression.
20991 * @param mapper The logic to use for creating an expression for the array's values.
20992 * @returns An array literal expression representing `values`, or null if `values` is empty or
20993 * is itself null.
20994 */
20995 function toOptionalLiteralArray(values, mapper) {
20996 if (values === null || values.length === 0) {
20997 return null;
20998 }
20999 return literalArr(values.map(value => mapper(value)));
21000 }
21001 /**
21002 * Creates an object literal expression from the given object, mapping all values to an expression
21003 * using the provided mapping function. If the object has no keys, then null is returned.
21004 *
21005 * @param object The object to transfer into an object literal expression.
21006 * @param mapper The logic to use for creating an expression for the object's values.
21007 * @returns An object literal expression representing `object`, or null if `object` does not have
21008 * any keys.
21009 */
21010 function toOptionalLiteralMap(object, mapper) {
21011 const entries = Object.keys(object).map(key => {
21012 const value = object[key];
21013 return { key, value: mapper(value), quoted: true };
21014 });
21015 if (entries.length > 0) {
21016 return literalMap(entries);
21017 }
21018 else {
21019 return null;
21020 }
21021 }
21022
21023 /**
21024 * @license
21025 * Copyright Google LLC All Rights Reserved.
21026 *
21027 * Use of this source code is governed by an MIT-style license that can be
21028 * found in the LICENSE file at https://angular.io/license
21029 */
21030 /**
21031 * Compile a directive declaration defined by the `R3DirectiveMetadata`.
21032 */
21033 function compileDeclareDirectiveFromMetadata(meta) {
21034 const definitionMap = createDirectiveDefinitionMap(meta);
21035 const expression = importExpr(Identifiers$1.declareDirective).callFn([definitionMap.toLiteralMap()]);
21036 const type = createDirectiveType(meta);
21037 return { expression, type };
21038 }
21039 /**
21040 * Gathers the declaration fields for a directive into a `DefinitionMap`. This allows for reusing
21041 * this logic for components, as they extend the directive metadata.
21042 */
21043 function createDirectiveDefinitionMap(meta) {
21044 const definitionMap = new DefinitionMap();
21045 definitionMap.set('version', literal('11.2.4'));
21046 // e.g. `type: MyDirective`
21047 definitionMap.set('type', meta.internalType);
21048 // e.g. `selector: 'some-dir'`
21049 if (meta.selector !== null) {
21050 definitionMap.set('selector', literal(meta.selector));
21051 }
21052 definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
21053 definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
21054 definitionMap.set('host', compileHostMetadata(meta.host));
21055 definitionMap.set('providers', meta.providers);
21056 if (meta.queries.length > 0) {
21057 definitionMap.set('queries', literalArr(meta.queries.map(compileQuery)));
21058 }
21059 if (meta.viewQueries.length > 0) {
21060 definitionMap.set('viewQueries', literalArr(meta.viewQueries.map(compileQuery)));
21061 }
21062 if (meta.exportAs !== null) {
21063 definitionMap.set('exportAs', asLiteral(meta.exportAs));
21064 }
21065 if (meta.usesInheritance) {
21066 definitionMap.set('usesInheritance', literal(true));
21067 }
21068 if (meta.lifecycle.usesOnChanges) {
21069 definitionMap.set('usesOnChanges', literal(true));
21070 }
21071 definitionMap.set('ngImport', importExpr(Identifiers$1.core));
21072 return definitionMap;
21073 }
21074 /**
21075 * Compiles the metadata of a single query into its partial declaration form as declared
21076 * by `R3DeclareQueryMetadata`.
21077 */
21078 function compileQuery(query) {
21079 const meta = new DefinitionMap();
21080 meta.set('propertyName', literal(query.propertyName));
21081 if (query.first) {
21082 meta.set('first', literal(true));
21083 }
21084 meta.set('predicate', Array.isArray(query.predicate) ? asLiteral(query.predicate) : query.predicate);
21085 if (!query.emitDistinctChangesOnly) {
21086 // `emitDistinctChangesOnly` is special because in future we expect it to be `true`. For this
21087 // reason the absence should be interpreted as `true`.
21088 meta.set('emitDistinctChangesOnly', literal(false));
21089 }
21090 if (query.descendants) {
21091 meta.set('descendants', literal(true));
21092 }
21093 meta.set('read', query.read);
21094 if (query.static) {
21095 meta.set('static', literal(true));
21096 }
21097 return meta.toLiteralMap();
21098 }
21099 /**
21100 * Compiles the host metadata into its partial declaration form as declared
21101 * in `R3DeclareDirectiveMetadata['host']`
21102 */
21103 function compileHostMetadata(meta) {
21104 const hostMetadata = new DefinitionMap();
21105 hostMetadata.set('attributes', toOptionalLiteralMap(meta.attributes, expression => expression));
21106 hostMetadata.set('listeners', toOptionalLiteralMap(meta.listeners, literal));
21107 hostMetadata.set('properties', toOptionalLiteralMap(meta.properties, literal));
21108 if (meta.specialAttributes.styleAttr) {
21109 hostMetadata.set('styleAttribute', literal(meta.specialAttributes.styleAttr));
21110 }
21111 if (meta.specialAttributes.classAttr) {
21112 hostMetadata.set('classAttribute', literal(meta.specialAttributes.classAttr));
21113 }
21114 if (hostMetadata.values.length > 0) {
21115 return hostMetadata.toLiteralMap();
21116 }
21117 else {
21118 return null;
21119 }
21120 }
21121
21122 /**
21123 * @license
21124 * Copyright Google LLC All Rights Reserved.
21125 *
21126 * Use of this source code is governed by an MIT-style license that can be
21127 * found in the LICENSE file at https://angular.io/license
21128 */
21129 /**
21130 * Compile a component declaration defined by the `R3ComponentMetadata`.
21131 */
21132 function compileDeclareComponentFromMetadata(meta, template) {
21133 const definitionMap = createComponentDefinitionMap(meta, template);
21134 const expression = importExpr(Identifiers$1.declareComponent).callFn([definitionMap.toLiteralMap()]);
21135 const type = createComponentType(meta);
21136 return { expression, type };
21137 }
21138 /**
21139 * Gathers the declaration fields for a component into a `DefinitionMap`.
21140 */
21141 function createComponentDefinitionMap(meta, template) {
21142 const definitionMap = createDirectiveDefinitionMap(meta);
21143 definitionMap.set('template', getTemplateExpression(template));
21144 if (template.isInline) {
21145 definitionMap.set('isInline', literal(true));
21146 }
21147 definitionMap.set('styles', toOptionalLiteralArray(meta.styles, literal));
21148 definitionMap.set('directives', compileUsedDirectiveMetadata(meta));
21149 definitionMap.set('pipes', compileUsedPipeMetadata(meta));
21150 definitionMap.set('viewProviders', meta.viewProviders);
21151 definitionMap.set('animations', meta.animations);
21152 if (meta.changeDetection !== undefined) {
21153 definitionMap.set('changeDetection', importExpr(Identifiers$1.ChangeDetectionStrategy)
21154 .prop(ChangeDetectionStrategy[meta.changeDetection]));
21155 }
21156 if (meta.encapsulation !== ViewEncapsulation.Emulated) {
21157 definitionMap.set('encapsulation', importExpr(Identifiers$1.ViewEncapsulation).prop(ViewEncapsulation[meta.encapsulation]));
21158 }
21159 if (meta.interpolation !== DEFAULT_INTERPOLATION_CONFIG) {
21160 definitionMap.set('interpolation', literalArr([literal(meta.interpolation.start), literal(meta.interpolation.end)]));
21161 }
21162 if (template.preserveWhitespaces === true) {
21163 definitionMap.set('preserveWhitespaces', literal(true));
21164 }
21165 return definitionMap;
21166 }
21167 function getTemplateExpression(template) {
21168 if (typeof template.template === 'string') {
21169 if (template.isInline) {
21170 // The template is inline but not a simple literal string, so give up with trying to
21171 // source-map it and just return a simple literal here.
21172 return literal(template.template);
21173 }
21174 else {
21175 // The template is external so we must synthesize an expression node with the appropriate
21176 // source-span.
21177 const contents = template.template;
21178 const file = new ParseSourceFile(contents, template.templateUrl);
21179 const start = new ParseLocation(file, 0, 0, 0);
21180 const end = computeEndLocation(file, contents);
21181 const span = new ParseSourceSpan(start, end);
21182 return literal(contents, null, span);
21183 }
21184 }
21185 else {
21186 // The template is inline so we can just reuse the current expression node.
21187 return template.template;
21188 }
21189 }
21190 function computeEndLocation(file, contents) {
21191 const length = contents.length;
21192 let lineStart = 0;
21193 let lastLineStart = 0;
21194 let line = 0;
21195 do {
21196 lineStart = contents.indexOf('\n', lastLineStart);
21197 if (lineStart !== -1) {
21198 lastLineStart = lineStart + 1;
21199 line++;
21200 }
21201 } while (lineStart !== -1);
21202 return new ParseLocation(file, length, line, length - lastLineStart);
21203 }
21204 /**
21205 * Compiles the directives as registered in the component metadata into an array literal of the
21206 * individual directives. If the component does not use any directives, then null is returned.
21207 */
21208 function compileUsedDirectiveMetadata(meta) {
21209 const wrapType = meta.declarationListEmitMode !== 0 /* Direct */ ?
21210 generateForwardRef :
21211 (expr) => expr;
21212 return toOptionalLiteralArray(meta.directives, directive => {
21213 const dirMeta = new DefinitionMap();
21214 dirMeta.set('type', wrapType(directive.type));
21215 dirMeta.set('selector', literal(directive.selector));
21216 dirMeta.set('inputs', toOptionalLiteralArray(directive.inputs, literal));
21217 dirMeta.set('outputs', toOptionalLiteralArray(directive.outputs, literal));
21218 dirMeta.set('exportAs', toOptionalLiteralArray(directive.exportAs, literal));
21219 return dirMeta.toLiteralMap();
21220 });
21221 }
21222 /**
21223 * Compiles the pipes as registered in the component metadata into an object literal, where the
21224 * pipe's name is used as key and a reference to its type as value. If the component does not use
21225 * any pipes, then null is returned.
21226 */
21227 function compileUsedPipeMetadata(meta) {
21228 if (meta.pipes.size === 0) {
21229 return null;
21230 }
21231 const wrapType = meta.declarationListEmitMode !== 0 /* Direct */ ?
21232 generateForwardRef :
21233 (expr) => expr;
21234 const entries = [];
21235 for (const [name, pipe] of meta.pipes) {
21236 entries.push({ key: name, value: wrapType(pipe), quoted: true });
21237 }
21238 return literalMap(entries);
21239 }
21240 function generateForwardRef(expr) {
21241 return importExpr(Identifiers$1.forwardRef).callFn([fn([], [new ReturnStatement(expr)])]);
21242 }
21243
21244 /**
21245 * @license
21246 * Copyright Google LLC All Rights Reserved.
21247 *
21248 * Use of this source code is governed by an MIT-style license that can be
21249 * found in the LICENSE file at https://angular.io/license
21250 */
21251 /**
21252 * Compile a Pipe declaration defined by the `R3PipeMetadata`.
21253 */
21254 function compileDeclarePipeFromMetadata(meta) {
21255 const definitionMap = createPipeDefinitionMap(meta);
21256 const expression = importExpr(Identifiers$1.declarePipe).callFn([definitionMap.toLiteralMap()]);
21257 const type = createPipeType(meta);
21258 return { expression, type };
21259 }
21260 /**
21261 * Gathers the declaration fields for a Pipe into a `DefinitionMap`. This allows for reusing
21262 * this logic for components, as they extend the Pipe metadata.
21263 */
21264 function createPipeDefinitionMap(meta) {
21265 const definitionMap = new DefinitionMap();
21266 definitionMap.set('version', literal('11.2.4'));
21267 definitionMap.set('ngImport', importExpr(Identifiers$1.core));
21268 // e.g. `type: MyPipe`
21269 definitionMap.set('type', meta.internalType);
21270 // e.g. `name: "myPipe"`
21271 definitionMap.set('name', literal(meta.pipeName));
21272 if (meta.pure === false) {
21273 // e.g. `pure: false`
21274 definitionMap.set('pure', literal(meta.pure));
21275 }
21276 return definitionMap;
21277 }
21278
21279 /**
21280 * @license
21281 * Copyright Google LLC All Rights Reserved.
21282 *
21283 * Use of this source code is governed by an MIT-style license that can be
21284 * found in the LICENSE file at https://angular.io/license
21285 */
21286 // This file only reexports content of the `src` folder. Keep it that way.
21287 // This function call has a global side effects and publishes the compiler into global namespace for
21288 // the late binding of the Compiler to the @angular/core for jit compilation.
21289 publishFacade(_global);
21290
21291 /**
21292 * @license
21293 * Copyright Google LLC All Rights Reserved.
21294 *
21295 * Use of this source code is governed by an MIT-style license that can be
21296 * found in the LICENSE file at https://angular.io/license
21297 */
21298 const VERSION$2 = new Version('11.2.4');
21299
21300 /**
21301 * @license
21302 * Copyright Google LLC All Rights Reserved.
21303 *
21304 * Use of this source code is governed by an MIT-style license that can be
21305 * found in the LICENSE file at https://angular.io/license
21306 */
21307 // In TypeScript 2.1 the spread element kind was renamed.
21308 const spreadElementSyntaxKind = ts$1.SyntaxKind.SpreadElement || ts$1.SyntaxKind.SpreadElementExpression;
21309 const empty$1 = ts$1.createNodeArray();
21310
21311 /**
21312 * @license
21313 * Copyright Google LLC All Rights Reserved.
21314 *
21315 * Use of this source code is governed by an MIT-style license that can be
21316 * found in the LICENSE file at https://angular.io/license
21317 */
21318 const UNKNOWN_ERROR_CODE = 500;
21319 var EmitFlags;
21320 (function (EmitFlags) {
21321 EmitFlags[EmitFlags["DTS"] = 1] = "DTS";
21322 EmitFlags[EmitFlags["JS"] = 2] = "JS";
21323 EmitFlags[EmitFlags["Metadata"] = 4] = "Metadata";
21324 EmitFlags[EmitFlags["I18nBundle"] = 8] = "I18nBundle";
21325 EmitFlags[EmitFlags["Codegen"] = 16] = "Codegen";
21326 EmitFlags[EmitFlags["Default"] = 19] = "Default";
21327 EmitFlags[EmitFlags["All"] = 31] = "All";
21328 })(EmitFlags || (EmitFlags = {}));
21329
21330 /*! *****************************************************************************
21331 Copyright (c) Microsoft Corporation.
21332
21333 Permission to use, copy, modify, and/or distribute this software for any
21334 purpose with or without fee is hereby granted.
21335
21336 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
21337 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
21338 AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
21339 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21340 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21341 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21342 PERFORMANCE OF THIS SOFTWARE.
21343 ***************************************************************************** */
21344
21345 function __awaiter(thisArg, _arguments, P, generator) {
21346 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
21347 return new (P || (P = Promise))(function (resolve, reject) {
21348 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21349 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
21350 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
21351 step((generator = generator.apply(thisArg, _arguments || [])).next());
21352 });
21353 }
21354
21355 /**
21356 * @license
21357 * Copyright Google LLC All Rights Reserved.
21358 *
21359 * Use of this source code is governed by an MIT-style license that can be
21360 * found in the LICENSE file at https://angular.io/license
21361 */
21362 /**
21363 * @publicApi
21364 */
21365 var ErrorCode;
21366 (function (ErrorCode) {
21367 ErrorCode[ErrorCode["DECORATOR_ARG_NOT_LITERAL"] = 1001] = "DECORATOR_ARG_NOT_LITERAL";
21368 ErrorCode[ErrorCode["DECORATOR_ARITY_WRONG"] = 1002] = "DECORATOR_ARITY_WRONG";
21369 ErrorCode[ErrorCode["DECORATOR_NOT_CALLED"] = 1003] = "DECORATOR_NOT_CALLED";
21370 ErrorCode[ErrorCode["DECORATOR_ON_ANONYMOUS_CLASS"] = 1004] = "DECORATOR_ON_ANONYMOUS_CLASS";
21371 ErrorCode[ErrorCode["DECORATOR_UNEXPECTED"] = 1005] = "DECORATOR_UNEXPECTED";
21372 /**
21373 * This error code indicates that there are incompatible decorators on a type or a class field.
21374 */
21375 ErrorCode[ErrorCode["DECORATOR_COLLISION"] = 1006] = "DECORATOR_COLLISION";
21376 ErrorCode[ErrorCode["VALUE_HAS_WRONG_TYPE"] = 1010] = "VALUE_HAS_WRONG_TYPE";
21377 ErrorCode[ErrorCode["VALUE_NOT_LITERAL"] = 1011] = "VALUE_NOT_LITERAL";
21378 ErrorCode[ErrorCode["COMPONENT_MISSING_TEMPLATE"] = 2001] = "COMPONENT_MISSING_TEMPLATE";
21379 ErrorCode[ErrorCode["PIPE_MISSING_NAME"] = 2002] = "PIPE_MISSING_NAME";
21380 ErrorCode[ErrorCode["PARAM_MISSING_TOKEN"] = 2003] = "PARAM_MISSING_TOKEN";
21381 ErrorCode[ErrorCode["DIRECTIVE_MISSING_SELECTOR"] = 2004] = "DIRECTIVE_MISSING_SELECTOR";
21382 /** Raised when an undecorated class is passed in as a provider to a module or a directive. */
21383 ErrorCode[ErrorCode["UNDECORATED_PROVIDER"] = 2005] = "UNDECORATED_PROVIDER";
21384 /**
21385 * Raised when a Directive inherits its constructor from a base class without an Angular
21386 * decorator.
21387 */
21388 ErrorCode[ErrorCode["DIRECTIVE_INHERITS_UNDECORATED_CTOR"] = 2006] = "DIRECTIVE_INHERITS_UNDECORATED_CTOR";
21389 /**
21390 * Raised when an undecorated class that is using Angular features
21391 * has been discovered.
21392 */
21393 ErrorCode[ErrorCode["UNDECORATED_CLASS_USING_ANGULAR_FEATURES"] = 2007] = "UNDECORATED_CLASS_USING_ANGULAR_FEATURES";
21394 /**
21395 * Raised when an component cannot resolve an external resource, such as a template or a style
21396 * sheet.
21397 */
21398 ErrorCode[ErrorCode["COMPONENT_RESOURCE_NOT_FOUND"] = 2008] = "COMPONENT_RESOURCE_NOT_FOUND";
21399 ErrorCode[ErrorCode["SYMBOL_NOT_EXPORTED"] = 3001] = "SYMBOL_NOT_EXPORTED";
21400 ErrorCode[ErrorCode["SYMBOL_EXPORTED_UNDER_DIFFERENT_NAME"] = 3002] = "SYMBOL_EXPORTED_UNDER_DIFFERENT_NAME";
21401 /**
21402 * Raised when a relationship between directives and/or pipes would cause a cyclic import to be
21403 * created that cannot be handled, such as in partial compilation mode.
21404 */
21405 ErrorCode[ErrorCode["IMPORT_CYCLE_DETECTED"] = 3003] = "IMPORT_CYCLE_DETECTED";
21406 ErrorCode[ErrorCode["CONFIG_FLAT_MODULE_NO_INDEX"] = 4001] = "CONFIG_FLAT_MODULE_NO_INDEX";
21407 ErrorCode[ErrorCode["CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK"] = 4002] = "CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK";
21408 /**
21409 * Raised when a host expression has a parse error, such as a host listener or host binding
21410 * expression containing a pipe.
21411 */
21412 ErrorCode[ErrorCode["HOST_BINDING_PARSE_ERROR"] = 5001] = "HOST_BINDING_PARSE_ERROR";
21413 /**
21414 * Raised when the compiler cannot parse a component's template.
21415 */
21416 ErrorCode[ErrorCode["TEMPLATE_PARSE_ERROR"] = 5002] = "TEMPLATE_PARSE_ERROR";
21417 /**
21418 * Raised when an NgModule contains an invalid reference in `declarations`.
21419 */
21420 ErrorCode[ErrorCode["NGMODULE_INVALID_DECLARATION"] = 6001] = "NGMODULE_INVALID_DECLARATION";
21421 /**
21422 * Raised when an NgModule contains an invalid type in `imports`.
21423 */
21424 ErrorCode[ErrorCode["NGMODULE_INVALID_IMPORT"] = 6002] = "NGMODULE_INVALID_IMPORT";
21425 /**
21426 * Raised when an NgModule contains an invalid type in `exports`.
21427 */
21428 ErrorCode[ErrorCode["NGMODULE_INVALID_EXPORT"] = 6003] = "NGMODULE_INVALID_EXPORT";
21429 /**
21430 * Raised when an NgModule contains a type in `exports` which is neither in `declarations` nor
21431 * otherwise imported.
21432 */
21433 ErrorCode[ErrorCode["NGMODULE_INVALID_REEXPORT"] = 6004] = "NGMODULE_INVALID_REEXPORT";
21434 /**
21435 * Raised when a `ModuleWithProviders` with a missing
21436 * generic type argument is passed into an `NgModule`.
21437 */
21438 ErrorCode[ErrorCode["NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC"] = 6005] = "NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC";
21439 /**
21440 * Raised when an NgModule exports multiple directives/pipes of the same name and the compiler
21441 * attempts to generate private re-exports within the NgModule file.
21442 */
21443 ErrorCode[ErrorCode["NGMODULE_REEXPORT_NAME_COLLISION"] = 6006] = "NGMODULE_REEXPORT_NAME_COLLISION";
21444 /**
21445 * Raised when a directive/pipe is part of the declarations of two or more NgModules.
21446 */
21447 ErrorCode[ErrorCode["NGMODULE_DECLARATION_NOT_UNIQUE"] = 6007] = "NGMODULE_DECLARATION_NOT_UNIQUE";
21448 /**
21449 * An element name failed validation against the DOM schema.
21450 */
21451 ErrorCode[ErrorCode["SCHEMA_INVALID_ELEMENT"] = 8001] = "SCHEMA_INVALID_ELEMENT";
21452 /**
21453 * An element's attribute name failed validation against the DOM schema.
21454 */
21455 ErrorCode[ErrorCode["SCHEMA_INVALID_ATTRIBUTE"] = 8002] = "SCHEMA_INVALID_ATTRIBUTE";
21456 /**
21457 * No matching directive was found for a `#ref="target"` expression.
21458 */
21459 ErrorCode[ErrorCode["MISSING_REFERENCE_TARGET"] = 8003] = "MISSING_REFERENCE_TARGET";
21460 /**
21461 * No matching pipe was found for a
21462 */
21463 ErrorCode[ErrorCode["MISSING_PIPE"] = 8004] = "MISSING_PIPE";
21464 /**
21465 * The left-hand side of an assignment expression was a template variable. Effectively, the
21466 * template looked like:
21467 *
21468 * ```
21469 * <ng-template let-something>
21470 * <button (click)="something = ...">...</button>
21471 * </ng-template>
21472 * ```
21473 *
21474 * Template variables are read-only.
21475 */
21476 ErrorCode[ErrorCode["WRITE_TO_READ_ONLY_VARIABLE"] = 8005] = "WRITE_TO_READ_ONLY_VARIABLE";
21477 /**
21478 * A template variable was declared twice. For example:
21479 *
21480 * ```html
21481 * <div *ngFor="let i of items; let i = index">
21482 * </div>
21483 * ```
21484 */
21485 ErrorCode[ErrorCode["DUPLICATE_VARIABLE_DECLARATION"] = 8006] = "DUPLICATE_VARIABLE_DECLARATION";
21486 /**
21487 * The template type-checking engine would need to generate an inline type check block for a
21488 * component, but the current type-checking environment doesn't support it.
21489 */
21490 ErrorCode[ErrorCode["INLINE_TCB_REQUIRED"] = 8900] = "INLINE_TCB_REQUIRED";
21491 /**
21492 * The template type-checking engine would need to generate an inline type constructor for a
21493 * directive or component, but the current type-checking environment doesn't support it.
21494 */
21495 ErrorCode[ErrorCode["INLINE_TYPE_CTOR_REQUIRED"] = 8901] = "INLINE_TYPE_CTOR_REQUIRED";
21496 /**
21497 * An injectable already has a `ɵprov` property.
21498 */
21499 ErrorCode[ErrorCode["INJECTABLE_DUPLICATE_PROV"] = 9001] = "INJECTABLE_DUPLICATE_PROV";
21500 // 10XXX error codes are reserved for diagnostics with category
21501 // `ts.DiagnosticCategory.Suggestion`. These diagnostics are generated by
21502 // language service.
21503 /**
21504 * Suggest users to enable `strictTemplates` to make use of full capabilities
21505 * provided by Angular language service.
21506 */
21507 ErrorCode[ErrorCode["SUGGEST_STRICT_TEMPLATES"] = 10001] = "SUGGEST_STRICT_TEMPLATES";
21508 })(ErrorCode || (ErrorCode = {}));
21509 /**
21510 * @internal
21511 * Base URL for the error details page.
21512 * Keep this value in sync with a similar const in
21513 * `packages/core/src/render3/error_code.ts`.
21514 */
21515 const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
21516 /**
21517 * @internal
21518 * Contains a set of error messages that have detailed guides at angular.io.
21519 * Full list of available error guides can be found at https://angular.io/errors
21520 */
21521 const COMPILER_ERRORS_WITH_GUIDES = new Set([
21522 ErrorCode.DECORATOR_ARG_NOT_LITERAL,
21523 ErrorCode.IMPORT_CYCLE_DETECTED,
21524 ErrorCode.PARAM_MISSING_TOKEN,
21525 ErrorCode.SCHEMA_INVALID_ELEMENT,
21526 ErrorCode.SCHEMA_INVALID_ATTRIBUTE,
21527 ErrorCode.MISSING_REFERENCE_TARGET,
21528 ]);
21529 /**
21530 * @internal
21531 */
21532 function ngErrorCode(code) {
21533 return parseInt('-99' + code);
21534 }
21535
21536 /**
21537 * @license
21538 * Copyright Google LLC All Rights Reserved.
21539 *
21540 * Use of this source code is governed by an MIT-style license that can be
21541 * found in the LICENSE file at https://angular.io/license
21542 */
21543 class FatalDiagnosticError {
21544 constructor(code, node, message, relatedInformation) {
21545 this.code = code;
21546 this.node = node;
21547 this.message = message;
21548 this.relatedInformation = relatedInformation;
21549 /**
21550 * @internal
21551 */
21552 this._isFatalDiagnosticError = true;
21553 }
21554 toDiagnostic() {
21555 return makeDiagnostic(this.code, this.node, this.message, this.relatedInformation);
21556 }
21557 }
21558 function makeDiagnostic(code, node, messageText, relatedInformation) {
21559 node = ts$1.getOriginalNode(node);
21560 return {
21561 category: ts$1.DiagnosticCategory.Error,
21562 code: ngErrorCode(code),
21563 file: ts$1.getOriginalNode(node).getSourceFile(),
21564 start: node.getStart(undefined, false),
21565 length: node.getWidth(),
21566 messageText,
21567 relatedInformation,
21568 };
21569 }
21570 function makeRelatedInformation(node, messageText) {
21571 node = ts$1.getOriginalNode(node);
21572 return {
21573 category: ts$1.DiagnosticCategory.Message,
21574 code: 0,
21575 file: node.getSourceFile(),
21576 start: node.getStart(),
21577 length: node.getWidth(),
21578 messageText,
21579 };
21580 }
21581
21582 /**
21583 * @license
21584 * Copyright Google LLC All Rights Reserved.
21585 *
21586 * Use of this source code is governed by an MIT-style license that can be
21587 * found in the LICENSE file at https://angular.io/license
21588 */
21589 const D_TS = /\.d\.ts$/i;
21590 function isDtsPath(filePath) {
21591 return D_TS.test(filePath);
21592 }
21593 function nodeNameForError(node) {
21594 if (node.name !== undefined && ts$1.isIdentifier(node.name)) {
21595 return node.name.text;
21596 }
21597 else {
21598 const kind = ts$1.SyntaxKind[node.kind];
21599 const { line, character } = ts$1.getLineAndCharacterOfPosition(node.getSourceFile(), node.getStart());
21600 return `${kind}@${line}:${character}`;
21601 }
21602 }
21603 function getSourceFile(node) {
21604 // In certain transformation contexts, `ts.Node.getSourceFile()` can actually return `undefined`,
21605 // despite the type signature not allowing it. In that event, get the `ts.SourceFile` via the
21606 // original node instead (which works).
21607 const directSf = node.getSourceFile();
21608 return directSf !== undefined ? directSf : ts$1.getOriginalNode(node).getSourceFile();
21609 }
21610 function getSourceFileOrNull(program, fileName) {
21611 return program.getSourceFile(fileName) || null;
21612 }
21613 function getTokenAtPosition(sf, pos) {
21614 // getTokenAtPosition is part of TypeScript's private API.
21615 return ts$1.getTokenAtPosition(sf, pos);
21616 }
21617 function identifierOfNode(decl) {
21618 if (decl.name !== undefined && ts$1.isIdentifier(decl.name)) {
21619 return decl.name;
21620 }
21621 else {
21622 return null;
21623 }
21624 }
21625 function isDeclaration(node) {
21626 return isValueDeclaration(node) || isTypeDeclaration(node);
21627 }
21628 function isValueDeclaration(node) {
21629 return ts$1.isClassDeclaration(node) || ts$1.isFunctionDeclaration(node) ||
21630 ts$1.isVariableDeclaration(node);
21631 }
21632 function isTypeDeclaration(node) {
21633 return ts$1.isEnumDeclaration(node) || ts$1.isTypeAliasDeclaration(node) ||
21634 ts$1.isInterfaceDeclaration(node);
21635 }
21636 function isExported(node) {
21637 let topLevel = node;
21638 if (ts$1.isVariableDeclaration(node) && ts$1.isVariableDeclarationList(node.parent)) {
21639 topLevel = node.parent.parent;
21640 }
21641 return topLevel.modifiers !== undefined &&
21642 topLevel.modifiers.some(modifier => modifier.kind === ts$1.SyntaxKind.ExportKeyword);
21643 }
21644 function getRootDirs(host, options) {
21645 const rootDirs = [];
21646 if (options.rootDirs !== undefined) {
21647 rootDirs.push(...options.rootDirs);
21648 }
21649 else if (options.rootDir !== undefined) {
21650 rootDirs.push(options.rootDir);
21651 }
21652 else {
21653 rootDirs.push(host.getCurrentDirectory());
21654 }
21655 // In Windows the above might not always return posix separated paths
21656 // See:
21657 // https://github.com/Microsoft/TypeScript/blob/3f7357d37f66c842d70d835bc925ec2a873ecfec/src/compiler/sys.ts#L650
21658 // Also compiler options might be set via an API which doesn't normalize paths
21659 return rootDirs.map(rootDir => absoluteFrom(host.getCanonicalFileName(rootDir)));
21660 }
21661 function nodeDebugInfo(node) {
21662 const sf = getSourceFile(node);
21663 const { line, character } = ts$1.getLineAndCharacterOfPosition(sf, node.pos);
21664 return `[${sf.fileName}: ${ts$1.SyntaxKind[node.kind]} @ ${line}:${character}]`;
21665 }
21666 /**
21667 * Resolve the specified `moduleName` using the given `compilerOptions` and `compilerHost`.
21668 *
21669 * This helper will attempt to use the `CompilerHost.resolveModuleNames()` method if available.
21670 * Otherwise it will fallback on the `ts.ResolveModuleName()` function.
21671 */
21672 function resolveModuleName(moduleName, containingFile, compilerOptions, compilerHost, moduleResolutionCache) {
21673 if (compilerHost.resolveModuleNames) {
21674 return compilerHost.resolveModuleNames([moduleName], containingFile, undefined, // reusedNames
21675 undefined, // redirectedReference
21676 compilerOptions)[0];
21677 }
21678 else {
21679 return ts$1.resolveModuleName(moduleName, containingFile, compilerOptions, compilerHost, moduleResolutionCache !== null ? moduleResolutionCache : undefined)
21680 .resolvedModule;
21681 }
21682 }
21683 /** Returns true if the node is an assignment expression. */
21684 function isAssignment(node) {
21685 return ts$1.isBinaryExpression(node) && node.operatorToken.kind === ts$1.SyntaxKind.EqualsToken;
21686 }
21687
21688 /**
21689 * @license
21690 * Copyright Google LLC All Rights Reserved.
21691 *
21692 * Use of this source code is governed by an MIT-style license that can be
21693 * found in the LICENSE file at https://angular.io/license
21694 */
21695 /**
21696 * Find the name, if any, by which a node is exported from a given file.
21697 */
21698 function findExportedNameOfNode(target, file, reflector) {
21699 const exports = reflector.getExportsOfModule(file);
21700 if (exports === null) {
21701 return null;
21702 }
21703 // Look for the export which declares the node.
21704 const keys = Array.from(exports.keys());
21705 const name = keys.find(key => {
21706 const decl = exports.get(key);
21707 return decl !== undefined && decl.node === target;
21708 });
21709 if (name === undefined) {
21710 throw new Error(`Failed to find exported name of node (${target.getText()}) in '${file.fileName}'.`);
21711 }
21712 return name;
21713 }
21714
21715 /**
21716 * @license
21717 * Copyright Google LLC All Rights Reserved.
21718 *
21719 * Use of this source code is governed by an MIT-style license that can be
21720 * found in the LICENSE file at https://angular.io/license
21721 */
21722 /**
21723 * Flags which alter the imports generated by the `ReferenceEmitter`.
21724 */
21725 var ImportFlags;
21726 (function (ImportFlags) {
21727 ImportFlags[ImportFlags["None"] = 0] = "None";
21728 /**
21729 * Force the generation of a new import when generating a reference, even if an identifier already
21730 * exists in the target file which could be used instead.
21731 *
21732 * This is sometimes required if there's a risk TypeScript might remove imports during emit.
21733 */
21734 ImportFlags[ImportFlags["ForceNewImport"] = 1] = "ForceNewImport";
21735 /**
21736 * Don't make use of any aliasing information when emitting a reference.
21737 *
21738 * This is sometimes required if emitting into a context where generated references will be fed
21739 * into TypeScript and type-checked (such as in template type-checking).
21740 */
21741 ImportFlags[ImportFlags["NoAliasing"] = 2] = "NoAliasing";
21742 /**
21743 * Indicates that an import to a type-only declaration is allowed.
21744 *
21745 * For references that occur in type-positions, the referred declaration may be a type-only
21746 * declaration that is not retained during emit. Including this flag allows to emit references to
21747 * type-only declarations as used in e.g. template type-checking.
21748 */
21749 ImportFlags[ImportFlags["AllowTypeImports"] = 4] = "AllowTypeImports";
21750 })(ImportFlags || (ImportFlags = {}));
21751 /**
21752 * Generates `Expression`s which refer to `Reference`s in a given context.
21753 *
21754 * A `ReferenceEmitter` uses one or more `ReferenceEmitStrategy` implementations to produce an
21755 * `Expression` which refers to a `Reference` in the context of a particular file.
21756 */
21757 class ReferenceEmitter {
21758 constructor(strategies) {
21759 this.strategies = strategies;
21760 }
21761 emit(ref, context, importFlags = ImportFlags.None) {
21762 for (const strategy of this.strategies) {
21763 const emitted = strategy.emit(ref, context, importFlags);
21764 if (emitted !== null) {
21765 return emitted;
21766 }
21767 }
21768 throw new Error(`Unable to write a reference to ${nodeNameForError(ref.node)} in ${ref.node.getSourceFile().fileName} from ${context.fileName}`);
21769 }
21770 }
21771 /**
21772 * A `ReferenceEmitStrategy` which will refer to declarations by any local `ts.Identifier`s, if
21773 * such identifiers are available.
21774 */
21775 class LocalIdentifierStrategy {
21776 emit(ref, context, importFlags) {
21777 const refSf = getSourceFile(ref.node);
21778 // If the emitter has specified ForceNewImport, then LocalIdentifierStrategy should not use a
21779 // local identifier at all, *except* in the source file where the node is actually declared.
21780 if (importFlags & ImportFlags.ForceNewImport && refSf !== context) {
21781 return null;
21782 }
21783 // If referenced node is not an actual TS declaration (e.g. `class Foo` or `function foo() {}`,
21784 // etc) and it is in the current file then just use it directly.
21785 // This is important because the reference could be a property access (e.g. `exports.foo`). In
21786 // such a case, the reference's `identities` property would be `[foo]`, which would result in an
21787 // invalid emission of a free-standing `foo` identifier, rather than `exports.foo`.
21788 if (!isDeclaration(ref.node) && refSf === context) {
21789 return new WrappedNodeExpr(ref.node);
21790 }
21791 // A Reference can have multiple identities in different files, so it may already have an
21792 // Identifier in the requested context file.
21793 const identifier = ref.getIdentityIn(context);
21794 if (identifier !== null) {
21795 return new WrappedNodeExpr(identifier);
21796 }
21797 else {
21798 return null;
21799 }
21800 }
21801 }
21802 /**
21803 * A `ReferenceEmitStrategy` which will refer to declarations that come from `node_modules` using
21804 * an absolute import.
21805 *
21806 * Part of this strategy involves looking at the target entry point and identifying the exported
21807 * name of the targeted declaration, as it might be different from the declared name (e.g. a
21808 * directive might be declared as FooDirImpl, but exported as FooDir). If no export can be found
21809 * which maps back to the original directive, an error is thrown.
21810 */
21811 class AbsoluteModuleStrategy {
21812 constructor(program, checker, moduleResolver, reflectionHost) {
21813 this.program = program;
21814 this.checker = checker;
21815 this.moduleResolver = moduleResolver;
21816 this.reflectionHost = reflectionHost;
21817 /**
21818 * A cache of the exports of specific modules, because resolving a module to its exports is a
21819 * costly operation.
21820 */
21821 this.moduleExportsCache = new Map();
21822 }
21823 emit(ref, context, importFlags) {
21824 if (ref.bestGuessOwningModule === null) {
21825 // There is no module name available for this Reference, meaning it was arrived at via a
21826 // relative path.
21827 return null;
21828 }
21829 else if (!isDeclaration(ref.node)) {
21830 // It's not possible to import something which isn't a declaration.
21831 throw new Error(`Debug assert: unable to import a Reference to non-declaration of type ${ts$1.SyntaxKind[ref.node.kind]}.`);
21832 }
21833 else if ((importFlags & ImportFlags.AllowTypeImports) === 0 && isTypeDeclaration(ref.node)) {
21834 throw new Error(`Importing a type-only declaration of type ${ts$1.SyntaxKind[ref.node.kind]} in a value position is not allowed.`);
21835 }
21836 // Try to find the exported name of the declaration, if one is available.
21837 const { specifier, resolutionContext } = ref.bestGuessOwningModule;
21838 const symbolName = this.resolveImportName(specifier, ref.node, resolutionContext);
21839 if (symbolName === null) {
21840 // TODO(alxhub): make this error a ts.Diagnostic pointing at whatever caused this import to be
21841 // triggered.
21842 throw new Error(`Symbol ${ref.debugName} declared in ${getSourceFile(ref.node).fileName} is not exported from ${specifier} (import into ${context.fileName})`);
21843 }
21844 return new ExternalExpr(new ExternalReference(specifier, symbolName));
21845 }
21846 resolveImportName(moduleName, target, fromFile) {
21847 const exports = this.getExportsOfModule(moduleName, fromFile);
21848 if (exports !== null && exports.has(target)) {
21849 return exports.get(target);
21850 }
21851 else {
21852 return null;
21853 }
21854 }
21855 getExportsOfModule(moduleName, fromFile) {
21856 if (!this.moduleExportsCache.has(moduleName)) {
21857 this.moduleExportsCache.set(moduleName, this.enumerateExportsOfModule(moduleName, fromFile));
21858 }
21859 return this.moduleExportsCache.get(moduleName);
21860 }
21861 enumerateExportsOfModule(specifier, fromFile) {
21862 // First, resolve the module specifier to its entry point, and get the ts.Symbol for it.
21863 const entryPointFile = this.moduleResolver.resolveModule(specifier, fromFile);
21864 if (entryPointFile === null) {
21865 return null;
21866 }
21867 const exports = this.reflectionHost.getExportsOfModule(entryPointFile);
21868 if (exports === null) {
21869 return null;
21870 }
21871 const exportMap = new Map();
21872 exports.forEach((declaration, name) => {
21873 exportMap.set(declaration.node, name);
21874 });
21875 return exportMap;
21876 }
21877 }
21878 /**
21879 * A `ReferenceEmitStrategy` which will refer to declarations via relative paths, provided they're
21880 * both in the logical project "space" of paths.
21881 *
21882 * This is trickier than it sounds, as the two files may be in different root directories in the
21883 * project. Simply calculating a file system relative path between the two is not sufficient.
21884 * Instead, `LogicalProjectPath`s are used.
21885 */
21886 class LogicalProjectStrategy {
21887 constructor(reflector, logicalFs) {
21888 this.reflector = reflector;
21889 this.logicalFs = logicalFs;
21890 }
21891 emit(ref, context) {
21892 const destSf = getSourceFile(ref.node);
21893 // Compute the relative path from the importing file to the file being imported. This is done
21894 // as a logical path computation, because the two files might be in different rootDirs.
21895 const destPath = this.logicalFs.logicalPathOfSf(destSf);
21896 if (destPath === null) {
21897 // The imported file is not within the logical project filesystem.
21898 return null;
21899 }
21900 const originPath = this.logicalFs.logicalPathOfSf(context);
21901 if (originPath === null) {
21902 throw new Error(`Debug assert: attempt to import from ${context.fileName} but it's outside the program?`);
21903 }
21904 // There's no way to emit a relative reference from a file to itself.
21905 if (destPath === originPath) {
21906 return null;
21907 }
21908 const name = findExportedNameOfNode(ref.node, destSf, this.reflector);
21909 if (name === null) {
21910 // The target declaration isn't exported from the file it's declared in. This is an issue!
21911 return null;
21912 }
21913 // With both files expressed as LogicalProjectPaths, getting the module specifier as a relative
21914 // path is now straightforward.
21915 const moduleName = LogicalProjectPath.relativePathBetween(originPath, destPath);
21916 return new ExternalExpr({ moduleName, name });
21917 }
21918 }
21919 /**
21920 * A `ReferenceEmitStrategy` which constructs relatives paths between `ts.SourceFile`s.
21921 *
21922 * This strategy can be used if there is no `rootDir`/`rootDirs` structure for the project which
21923 * necessitates the stronger logic of `LogicalProjectStrategy`.
21924 */
21925 class RelativePathStrategy {
21926 constructor(reflector) {
21927 this.reflector = reflector;
21928 }
21929 emit(ref, context) {
21930 const destSf = getSourceFile(ref.node);
21931 const relativePath = relative(dirname(absoluteFromSourceFile(context)), absoluteFromSourceFile(destSf));
21932 const moduleName = toRelativeImport(stripExtension(relativePath));
21933 const name = findExportedNameOfNode(ref.node, destSf, this.reflector);
21934 return new ExternalExpr({ moduleName, name });
21935 }
21936 }
21937 /**
21938 * A `ReferenceEmitStrategy` which uses a `UnifiedModulesHost` to generate absolute import
21939 * references.
21940 */
21941 class UnifiedModulesStrategy {
21942 constructor(reflector, unifiedModulesHost) {
21943 this.reflector = reflector;
21944 this.unifiedModulesHost = unifiedModulesHost;
21945 }
21946 emit(ref, context) {
21947 const destSf = getSourceFile(ref.node);
21948 const name = findExportedNameOfNode(ref.node, destSf, this.reflector);
21949 if (name === null) {
21950 return null;
21951 }
21952 const moduleName = this.unifiedModulesHost.fileNameToModuleName(destSf.fileName, context.fileName);
21953 return new ExternalExpr({ moduleName, name });
21954 }
21955 }
21956
21957 /**
21958 * @license
21959 * Copyright Google LLC All Rights Reserved.
21960 *
21961 * Use of this source code is governed by an MIT-style license that can be
21962 * found in the LICENSE file at https://angular.io/license
21963 */
21964 // Escape anything that isn't alphanumeric, '/' or '_'.
21965 const CHARS_TO_ESCAPE = /[^a-zA-Z0-9/_]/g;
21966 /**
21967 * An `AliasingHost` which generates and consumes alias re-exports when module names for each file
21968 * are determined by a `UnifiedModulesHost`.
21969 *
21970 * When using a `UnifiedModulesHost`, aliasing prevents issues with transitive dependencies. See the
21971 * README.md for more details.
21972 */
21973 class UnifiedModulesAliasingHost {
21974 constructor(unifiedModulesHost) {
21975 this.unifiedModulesHost = unifiedModulesHost;
21976 /**
21977 * With a `UnifiedModulesHost`, aliases are chosen automatically without the need to look through
21978 * the exports present in a .d.ts file, so we can avoid cluttering the .d.ts files.
21979 */
21980 this.aliasExportsInDts = false;
21981 }
21982 maybeAliasSymbolAs(ref, context, ngModuleName, isReExport) {
21983 if (!isReExport) {
21984 // Aliasing is used with a UnifiedModulesHost to prevent transitive dependencies. Thus,
21985 // aliases
21986 // only need to be created for directives/pipes which are not direct declarations of an
21987 // NgModule which exports them.
21988 return null;
21989 }
21990 return this.aliasName(ref.node, context);
21991 }
21992 /**
21993 * Generates an `Expression` to import `decl` from `via`, assuming an export was added when `via`
21994 * was compiled per `maybeAliasSymbolAs` above.
21995 */
21996 getAliasIn(decl, via, isReExport) {
21997 if (!isReExport) {
21998 // Directly exported directives/pipes don't require an alias, per the logic in
21999 // `maybeAliasSymbolAs`.
22000 return null;
22001 }
22002 // viaModule is the module it'll actually be imported from.
22003 const moduleName = this.unifiedModulesHost.fileNameToModuleName(via.fileName, via.fileName);
22004 return new ExternalExpr({ moduleName, name: this.aliasName(decl, via) });
22005 }
22006 /**
22007 * Generates an alias name based on the full module name of the file which declares the aliased
22008 * directive/pipe.
22009 */
22010 aliasName(decl, context) {
22011 // The declared module is used to get the name of the alias.
22012 const declModule = this.unifiedModulesHost.fileNameToModuleName(decl.getSourceFile().fileName, context.fileName);
22013 const replaced = declModule.replace(CHARS_TO_ESCAPE, '_').replace(/\//g, '$');
22014 return 'ɵng$' + replaced + '$$' + decl.name.text;
22015 }
22016 }
22017 /**
22018 * An `AliasingHost` which exports directives from any file containing an NgModule in which they're
22019 * declared/exported, under a private symbol name.
22020 *
22021 * These exports support cases where an NgModule is imported deeply from an absolute module path
22022 * (that is, it's not part of an Angular Package Format entrypoint), and the compiler needs to
22023 * import any matched directives/pipes from the same path (to the NgModule file). See README.md for
22024 * more details.
22025 */
22026 class PrivateExportAliasingHost {
22027 constructor(host) {
22028 this.host = host;
22029 /**
22030 * Under private export aliasing, the `AbsoluteModuleStrategy` used for emitting references will
22031 * will select aliased exports that it finds in the .d.ts file for an NgModule's file. Thus,
22032 * emitting these exports in .d.ts is a requirement for the `PrivateExportAliasingHost` to
22033 * function correctly.
22034 */
22035 this.aliasExportsInDts = true;
22036 }
22037 maybeAliasSymbolAs(ref, context, ngModuleName) {
22038 if (ref.hasOwningModuleGuess) {
22039 // Skip nodes that already have an associated absolute module specifier, since they can be
22040 // safely imported from that specifier.
22041 return null;
22042 }
22043 // Look for a user-provided export of `decl` in `context`. If one exists, then an alias export
22044 // is not needed.
22045 // TODO(alxhub): maybe add a host method to check for the existence of an export without going
22046 // through the entire list of exports.
22047 const exports = this.host.getExportsOfModule(context);
22048 if (exports === null) {
22049 // Something went wrong, and no exports were available at all. Bail rather than risk creating
22050 // re-exports when they're not needed.
22051 throw new Error(`Could not determine the exports of: ${context.fileName}`);
22052 }
22053 let found = false;
22054 exports.forEach(value => {
22055 if (value.node === ref.node) {
22056 found = true;
22057 }
22058 });
22059 if (found) {
22060 // The module exports the declared class directly, no alias is necessary.
22061 return null;
22062 }
22063 return `ɵngExportɵ${ngModuleName}ɵ${ref.node.name.text}`;
22064 }
22065 /**
22066 * A `PrivateExportAliasingHost` only generates re-exports and does not direct the compiler to
22067 * directly consume the aliases it creates.
22068 *
22069 * Instead, they're consumed indirectly: `AbsoluteModuleStrategy` `ReferenceEmitterStrategy` will
22070 * select these alias exports automatically when looking for an export of the directive/pipe from
22071 * the same path as the NgModule was imported.
22072 *
22073 * Thus, `getAliasIn` always returns `null`.
22074 */
22075 getAliasIn() {
22076 return null;
22077 }
22078 }
22079 /**
22080 * A `ReferenceEmitStrategy` which will consume the alias attached to a particular `Reference` to a
22081 * directive or pipe, if it exists.
22082 */
22083 class AliasStrategy {
22084 emit(ref, context, importMode) {
22085 if (importMode & ImportFlags.NoAliasing) {
22086 return null;
22087 }
22088 return ref.alias;
22089 }
22090 }
22091
22092 /**
22093 * @license
22094 * Copyright Google LLC All Rights Reserved.
22095 *
22096 * Use of this source code is governed by an MIT-style license that can be
22097 * found in the LICENSE file at https://angular.io/license
22098 */
22099 function relativePathBetween(from, to) {
22100 const relativePath = stripExtension(relative(dirname(resolve(from)), resolve(to)));
22101 return relativePath !== '' ? toRelativeImport(relativePath) : null;
22102 }
22103
22104 /**
22105 * @license
22106 * Copyright Google LLC All Rights Reserved.
22107 *
22108 * Use of this source code is governed by an MIT-style license that can be
22109 * found in the LICENSE file at https://angular.io/license
22110 */
22111 /**
22112 * `ImportRewriter` that does no rewriting.
22113 */
22114 class NoopImportRewriter {
22115 shouldImportSymbol(symbol, specifier) {
22116 return true;
22117 }
22118 rewriteSymbol(symbol, specifier) {
22119 return symbol;
22120 }
22121 rewriteSpecifier(specifier, inContextOfFile) {
22122 return specifier;
22123 }
22124 }
22125 /**
22126 * A mapping of supported symbols that can be imported from within @angular/core, and the names by
22127 * which they're exported from r3_symbols.
22128 */
22129 const CORE_SUPPORTED_SYMBOLS = new Map([
22130 ['ɵɵdefineInjectable', 'ɵɵdefineInjectable'],
22131 ['ɵɵdefineInjector', 'ɵɵdefineInjector'],
22132 ['ɵɵdefineNgModule', 'ɵɵdefineNgModule'],
22133 ['ɵɵsetNgModuleScope', 'ɵɵsetNgModuleScope'],
22134 ['ɵɵinject', 'ɵɵinject'],
22135 ['ɵɵFactoryDef', 'ɵɵFactoryDef'],
22136 ['ɵsetClassMetadata', 'setClassMetadata'],
22137 ['ɵɵInjectableDef', 'ɵɵInjectableDef'],
22138 ['ɵɵInjectorDef', 'ɵɵInjectorDef'],
22139 ['ɵɵNgModuleDefWithMeta', 'ɵɵNgModuleDefWithMeta'],
22140 ['ɵNgModuleFactory', 'NgModuleFactory'],
22141 ['ɵnoSideEffects', 'ɵnoSideEffects'],
22142 ]);
22143 const CORE_MODULE = '@angular/core';
22144 /**
22145 * `ImportRewriter` that rewrites imports from '@angular/core' to be imported from the r3_symbols.ts
22146 * file instead.
22147 */
22148 class R3SymbolsImportRewriter {
22149 constructor(r3SymbolsPath) {
22150 this.r3SymbolsPath = r3SymbolsPath;
22151 }
22152 shouldImportSymbol(symbol, specifier) {
22153 return true;
22154 }
22155 rewriteSymbol(symbol, specifier) {
22156 if (specifier !== CORE_MODULE) {
22157 // This import isn't from core, so ignore it.
22158 return symbol;
22159 }
22160 return validateAndRewriteCoreSymbol(symbol);
22161 }
22162 rewriteSpecifier(specifier, inContextOfFile) {
22163 if (specifier !== CORE_MODULE) {
22164 // This module isn't core, so ignore it.
22165 return specifier;
22166 }
22167 const relativePathToR3Symbols = relativePathBetween(inContextOfFile, this.r3SymbolsPath);
22168 if (relativePathToR3Symbols === null) {
22169 throw new Error(`Failed to rewrite import inside ${CORE_MODULE}: ${inContextOfFile} -> ${this.r3SymbolsPath}`);
22170 }
22171 return relativePathToR3Symbols;
22172 }
22173 }
22174 function validateAndRewriteCoreSymbol(name) {
22175 if (!CORE_SUPPORTED_SYMBOLS.has(name)) {
22176 throw new Error(`Importing unexpected symbol ${name} while compiling ${CORE_MODULE}`);
22177 }
22178 return CORE_SUPPORTED_SYMBOLS.get(name);
22179 }
22180
22181 /**
22182 * @license
22183 * Copyright Google LLC All Rights Reserved.
22184 *
22185 * Use of this source code is governed by an MIT-style license that can be
22186 * found in the LICENSE file at https://angular.io/license
22187 */
22188 /**
22189 * TypeScript has trouble with generating default imports inside of transformers for some module
22190 * formats. The issue is that for the statement:
22191 *
22192 * import X from 'some/module';
22193 * console.log(X);
22194 *
22195 * TypeScript will not use the "X" name in generated code. For normal user code, this is fine
22196 * because references to X will also be renamed. However, if both the import and any references are
22197 * added in a transformer, TypeScript does not associate the two, and will leave the "X" references
22198 * dangling while renaming the import variable. The generated code looks something like:
22199 *
22200 * const module_1 = require('some/module');
22201 * console.log(X); // now X is a dangling reference.
22202 *
22203 * Therefore, we cannot synthetically add default imports, and must reuse the imports that users
22204 * include. Doing this poses a challenge for imports that are only consumed in the type position in
22205 * the user's code. If Angular reuses the imported symbol in a value position (for example, we
22206 * see a constructor parameter of type Foo and try to write "inject(Foo)") we will also end up with
22207 * a dangling reference, as TS will elide the import because it was only used in the type position
22208 * originally.
22209 *
22210 * To avoid this, the compiler must "touch" the imports with `ts.getMutableClone`, and should
22211 * only do this for imports which are actually consumed. The `DefaultImportTracker` keeps track of
22212 * these imports as they're encountered and emitted, and implements a transform which can correctly
22213 * flag the imports as required.
22214 *
22215 * This problem does not exist for non-default imports as the compiler can easily insert
22216 * "import * as X" style imports for those, and the "X" identifier survives transformation.
22217 */
22218 class DefaultImportTracker {
22219 constructor() {
22220 /**
22221 * A `Map` which tracks the `Map` of default import `ts.Identifier`s to their
22222 * `ts.ImportDeclaration`s. These declarations are not guaranteed to be used.
22223 */
22224 this.sourceFileToImportMap = new Map();
22225 /**
22226 * A `Map` which tracks the `Set` of `ts.ImportDeclaration`s for default imports that were used in
22227 * a given `ts.SourceFile` and need to be preserved.
22228 */
22229 this.sourceFileToUsedImports = new Map();
22230 }
22231 recordImportedIdentifier(id, decl) {
22232 const sf = getSourceFile(id);
22233 if (!this.sourceFileToImportMap.has(sf)) {
22234 this.sourceFileToImportMap.set(sf, new Map());
22235 }
22236 this.sourceFileToImportMap.get(sf).set(id, decl);
22237 }
22238 recordUsedIdentifier(id) {
22239 const sf = getSourceFile(id);
22240 if (!this.sourceFileToImportMap.has(sf)) {
22241 // The identifier's source file has no registered default imports at all.
22242 return;
22243 }
22244 const identiferToDeclaration = this.sourceFileToImportMap.get(sf);
22245 if (!identiferToDeclaration.has(id)) {
22246 // The identifier isn't from a registered default import.
22247 return;
22248 }
22249 const decl = identiferToDeclaration.get(id);
22250 // Add the default import declaration to the set of used import declarations for the file.
22251 if (!this.sourceFileToUsedImports.has(sf)) {
22252 this.sourceFileToUsedImports.set(sf, new Set());
22253 }
22254 this.sourceFileToUsedImports.get(sf).add(decl);
22255 }
22256 /**
22257 * Get a `ts.TransformerFactory` which will preserve default imports that were previously marked
22258 * as used.
22259 *
22260 * This transformer must run after any other transformers which call `recordUsedIdentifier`.
22261 */
22262 importPreservingTransformer() {
22263 return (context) => {
22264 return (sf) => {
22265 return this.transformSourceFile(sf);
22266 };
22267 };
22268 }
22269 /**
22270 * Process a `ts.SourceFile` and replace any `ts.ImportDeclaration`s.
22271 */
22272 transformSourceFile(sf) {
22273 const originalSf = ts$1.getOriginalNode(sf);
22274 // Take a fast path if no import declarations need to be preserved in the file.
22275 if (!this.sourceFileToUsedImports.has(originalSf)) {
22276 return sf;
22277 }
22278 // There are declarations that need to be preserved.
22279 const importsToPreserve = this.sourceFileToUsedImports.get(originalSf);
22280 // Generate a new statement list which preserves any imports present in `importsToPreserve`.
22281 const statements = sf.statements.map(stmt => {
22282 if (ts$1.isImportDeclaration(stmt) && importsToPreserve.has(stmt)) {
22283 // Preserving an import that's marked as unreferenced (type-only) is tricky in TypeScript.
22284 //
22285 // Various approaches have been tried, with mixed success:
22286 //
22287 // 1. Using `ts.updateImportDeclaration` does not cause the import to be retained.
22288 //
22289 // 2. Using `ts.createImportDeclaration` with the same `ts.ImportClause` causes the import
22290 // to correctly be retained, but when emitting CommonJS module format code, references
22291 // to the imported value will not match the import variable.
22292 //
22293 // 3. Emitting "import * as" imports instead generates the correct import variable, but
22294 // references are missing the ".default" access. This happens to work for tsickle code
22295 // with goog.module transformations as tsickle strips the ".default" anyway.
22296 //
22297 // 4. It's possible to trick TypeScript by setting `ts.NodeFlag.Synthesized` on the import
22298 // declaration. This causes the import to be correctly retained and generated, but can
22299 // violate invariants elsewhere in the compiler and cause crashes.
22300 //
22301 // 5. Using `ts.getMutableClone` seems to correctly preserve the import and correctly
22302 // generate references to the import variable across all module types.
22303 //
22304 // Therefore, option 5 is the one used here. It seems to be implemented as the correct way
22305 // to perform option 4, which preserves all the compiler's invariants.
22306 //
22307 // TODO(alxhub): discuss with the TypeScript team and determine if there's a better way to
22308 // deal with this issue.
22309 stmt = ts$1.getMutableClone(stmt);
22310 }
22311 return stmt;
22312 });
22313 // Save memory - there's no need to keep these around once the transform has run for the given
22314 // file.
22315 this.sourceFileToImportMap.delete(originalSf);
22316 this.sourceFileToUsedImports.delete(originalSf);
22317 return ts$1.updateSourceFileNode(sf, statements);
22318 }
22319 }
22320
22321 /**
22322 * @license
22323 * Copyright Google LLC All Rights Reserved.
22324 *
22325 * Use of this source code is governed by an MIT-style license that can be
22326 * found in the LICENSE file at https://angular.io/license
22327 */
22328 /**
22329 * A `ts.Node` plus the context in which it was discovered.
22330 *
22331 * A `Reference` is a pointer to a `ts.Node` that was extracted from the program somehow. It
22332 * contains not only the node itself, but the information regarding how the node was located. In
22333 * particular, it might track different identifiers by which the node is exposed, as well as
22334 * potentially a module specifier which might expose the node.
22335 *
22336 * The Angular compiler uses `Reference`s instead of `ts.Node`s when tracking classes or generating
22337 * imports.
22338 */
22339 class Reference$1 {
22340 constructor(node, bestGuessOwningModule = null) {
22341 this.node = node;
22342 this.identifiers = [];
22343 /**
22344 * Indicates that the Reference was created synthetically, not as a result of natural value
22345 * resolution.
22346 *
22347 * This is used to avoid misinterpreting the Reference in certain contexts.
22348 */
22349 this.synthetic = false;
22350 this._alias = null;
22351 this.bestGuessOwningModule = bestGuessOwningModule;
22352 const id = identifierOfNode(node);
22353 if (id !== null) {
22354 this.identifiers.push(id);
22355 }
22356 }
22357 /**
22358 * The best guess at which module specifier owns this particular reference, or `null` if there
22359 * isn't one.
22360 */
22361 get ownedByModuleGuess() {
22362 if (this.bestGuessOwningModule !== null) {
22363 return this.bestGuessOwningModule.specifier;
22364 }
22365 else {
22366 return null;
22367 }
22368 }
22369 /**
22370 * Whether this reference has a potential owning module or not.
22371 *
22372 * See `bestGuessOwningModule`.
22373 */
22374 get hasOwningModuleGuess() {
22375 return this.bestGuessOwningModule !== null;
22376 }
22377 /**
22378 * A name for the node, if one is available.
22379 *
22380 * This is only suited for debugging. Any actual references to this node should be made with
22381 * `ts.Identifier`s (see `getIdentityIn`).
22382 */
22383 get debugName() {
22384 const id = identifierOfNode(this.node);
22385 return id !== null ? id.text : null;
22386 }
22387 get alias() {
22388 return this._alias;
22389 }
22390 /**
22391 * Record a `ts.Identifier` by which it's valid to refer to this node, within the context of this
22392 * `Reference`.
22393 */
22394 addIdentifier(identifier) {
22395 this.identifiers.push(identifier);
22396 }
22397 /**
22398 * Get a `ts.Identifier` within this `Reference` that can be used to refer within the context of a
22399 * given `ts.SourceFile`, if any.
22400 */
22401 getIdentityIn(context) {
22402 return this.identifiers.find(id => id.getSourceFile() === context) || null;
22403 }
22404 /**
22405 * Get a `ts.Identifier` for this `Reference` that exists within the given expression.
22406 *
22407 * This is very useful for producing `ts.Diagnostic`s that reference `Reference`s that were
22408 * extracted from some larger expression, as it can be used to pinpoint the `ts.Identifier` within
22409 * the expression from which the `Reference` originated.
22410 */
22411 getIdentityInExpression(expr) {
22412 const sf = expr.getSourceFile();
22413 return this.identifiers.find(id => {
22414 if (id.getSourceFile() !== sf) {
22415 return false;
22416 }
22417 // This identifier is a match if its position lies within the given expression.
22418 return id.pos >= expr.pos && id.end <= expr.end;
22419 }) ||
22420 null;
22421 }
22422 /**
22423 * Given the 'container' expression from which this `Reference` was extracted, produce a
22424 * `ts.Expression` to use in a diagnostic which best indicates the position within the container
22425 * expression that generated the `Reference`.
22426 *
22427 * For example, given a `Reference` to the class 'Bar' and the containing expression:
22428 * `[Foo, Bar, Baz]`, this function would attempt to return the `ts.Identifier` for `Bar` within
22429 * the array. This could be used to produce a nice diagnostic context:
22430 *
22431 * ```text
22432 * [Foo, Bar, Baz]
22433 * ~~~
22434 * ```
22435 *
22436 * If no specific node can be found, then the `fallback` expression is used, which defaults to the
22437 * entire containing expression.
22438 */
22439 getOriginForDiagnostics(container, fallback = container) {
22440 const id = this.getIdentityInExpression(container);
22441 return id !== null ? id : fallback;
22442 }
22443 cloneWithAlias(alias) {
22444 const ref = new Reference$1(this.node, this.bestGuessOwningModule);
22445 ref.identifiers = [...this.identifiers];
22446 ref._alias = alias;
22447 return ref;
22448 }
22449 cloneWithNoIdentifiers() {
22450 const ref = new Reference$1(this.node, this.bestGuessOwningModule);
22451 ref._alias = this._alias;
22452 ref.identifiers = [];
22453 return ref;
22454 }
22455 }
22456
22457 /**
22458 * Used by `RouterEntryPointManager` and `NgModuleRouteAnalyzer` (which is in turn is used by
22459 * `NgModuleDecoratorHandler`) for resolving the module source-files references in lazy-loaded
22460 * routes (relative to the source-file containing the `NgModule` that provides the route
22461 * definitions).
22462 */
22463 class ModuleResolver {
22464 constructor(program, compilerOptions, host, moduleResolutionCache) {
22465 this.program = program;
22466 this.compilerOptions = compilerOptions;
22467 this.host = host;
22468 this.moduleResolutionCache = moduleResolutionCache;
22469 }
22470 resolveModule(moduleName, containingFile) {
22471 const resolved = resolveModuleName(moduleName, containingFile, this.compilerOptions, this.host, this.moduleResolutionCache);
22472 if (resolved === undefined) {
22473 return null;
22474 }
22475 return getSourceFileOrNull(this.program, absoluteFrom(resolved.resolvedFileName));
22476 }
22477 }
22478
22479 /**
22480 * @license
22481 * Copyright Google LLC All Rights Reserved.
22482 *
22483 * Use of this source code is governed by an MIT-style license that can be
22484 * found in the LICENSE file at https://angular.io/license
22485 */
22486 const Decorator = {
22487 nodeForError: (decorator) => {
22488 if (decorator.node !== null) {
22489 return decorator.node;
22490 }
22491 else {
22492 // TODO(alxhub): we can't rely on narrowing until TS 3.6 is in g3.
22493 return decorator.synthesizedFor;
22494 }
22495 },
22496 };
22497 function isDecoratorIdentifier(exp) {
22498 return ts$1.isIdentifier(exp) ||
22499 ts$1.isPropertyAccessExpression(exp) && ts$1.isIdentifier(exp.expression) &&
22500 ts$1.isIdentifier(exp.name);
22501 }
22502 /**
22503 * An enumeration of possible kinds of class members.
22504 */
22505 var ClassMemberKind;
22506 (function (ClassMemberKind) {
22507 ClassMemberKind[ClassMemberKind["Constructor"] = 0] = "Constructor";
22508 ClassMemberKind[ClassMemberKind["Getter"] = 1] = "Getter";
22509 ClassMemberKind[ClassMemberKind["Setter"] = 2] = "Setter";
22510 ClassMemberKind[ClassMemberKind["Property"] = 3] = "Property";
22511 ClassMemberKind[ClassMemberKind["Method"] = 4] = "Method";
22512 })(ClassMemberKind || (ClassMemberKind = {}));
22513 /**
22514 * Possible declarations of known values, such as built-in objects/functions or TypeScript helpers.
22515 */
22516 var KnownDeclaration;
22517 (function (KnownDeclaration) {
22518 /**
22519 * Indicates the JavaScript global `Object` class.
22520 */
22521 KnownDeclaration[KnownDeclaration["JsGlobalObject"] = 0] = "JsGlobalObject";
22522 /**
22523 * Indicates the `__assign` TypeScript helper function.
22524 */
22525 KnownDeclaration[KnownDeclaration["TsHelperAssign"] = 1] = "TsHelperAssign";
22526 /**
22527 * Indicates the `__spread` TypeScript helper function.
22528 */
22529 KnownDeclaration[KnownDeclaration["TsHelperSpread"] = 2] = "TsHelperSpread";
22530 /**
22531 * Indicates the `__spreadArrays` TypeScript helper function.
22532 */
22533 KnownDeclaration[KnownDeclaration["TsHelperSpreadArrays"] = 3] = "TsHelperSpreadArrays";
22534 })(KnownDeclaration || (KnownDeclaration = {}));
22535 /**
22536 * Returns true if the `decl` is a `ConcreteDeclaration` (ie. that its `node` property is a
22537 * `ts.Declaration`).
22538 */
22539 function isConcreteDeclaration(decl) {
22540 return decl.kind === 0 /* Concrete */;
22541 }
22542
22543 /**
22544 * @license
22545 * Copyright Google LLC All Rights Reserved.
22546 *
22547 * Use of this source code is governed by an MIT-style license that can be
22548 * found in the LICENSE file at https://angular.io/license
22549 */
22550 /**
22551 * Potentially convert a `ts.TypeNode` to a `TypeValueReference`, which indicates how to use the
22552 * type given in the `ts.TypeNode` in a value position.
22553 *
22554 * This can return `null` if the `typeNode` is `null`, if it does not refer to a symbol with a value
22555 * declaration, or if it is not possible to statically understand.
22556 */
22557 function typeToValue(typeNode, checker) {
22558 // It's not possible to get a value expression if the parameter doesn't even have a type.
22559 if (typeNode === null) {
22560 return missingType();
22561 }
22562 if (!ts$1.isTypeReferenceNode(typeNode)) {
22563 return unsupportedType(typeNode);
22564 }
22565 const symbols = resolveTypeSymbols(typeNode, checker);
22566 if (symbols === null) {
22567 return unknownReference(typeNode);
22568 }
22569 const { local, decl } = symbols;
22570 // It's only valid to convert a type reference to a value reference if the type actually
22571 // has a value declaration associated with it. Note that const enums are an exception,
22572 // because while they do have a value declaration, they don't exist at runtime.
22573 if (decl.valueDeclaration === undefined || decl.flags & ts$1.SymbolFlags.ConstEnum) {
22574 let typeOnlyDecl = null;
22575 if (decl.declarations !== undefined && decl.declarations.length > 0) {
22576 typeOnlyDecl = decl.declarations[0];
22577 }
22578 return noValueDeclaration(typeNode, typeOnlyDecl);
22579 }
22580 // The type points to a valid value declaration. Rewrite the TypeReference into an
22581 // Expression which references the value pointed to by the TypeReference, if possible.
22582 // Look at the local `ts.Symbol`'s declarations and see if it comes from an import
22583 // statement. If so, extract the module specifier and the name of the imported type.
22584 const firstDecl = local.declarations && local.declarations[0];
22585 if (firstDecl !== undefined) {
22586 if (ts$1.isImportClause(firstDecl) && firstDecl.name !== undefined) {
22587 // This is a default import.
22588 // import Foo from 'foo';
22589 if (firstDecl.isTypeOnly) {
22590 // Type-only imports cannot be represented as value.
22591 return typeOnlyImport(typeNode, firstDecl);
22592 }
22593 return {
22594 kind: 0 /* LOCAL */,
22595 expression: firstDecl.name,
22596 defaultImportStatement: firstDecl.parent,
22597 };
22598 }
22599 else if (ts$1.isImportSpecifier(firstDecl)) {
22600 // The symbol was imported by name
22601 // import {Foo} from 'foo';
22602 // or
22603 // import {Foo as Bar} from 'foo';
22604 if (firstDecl.parent.parent.isTypeOnly) {
22605 // Type-only imports cannot be represented as value.
22606 return typeOnlyImport(typeNode, firstDecl.parent.parent);
22607 }
22608 // Determine the name to import (`Foo`) from the import specifier, as the symbol names of
22609 // the imported type could refer to a local alias (like `Bar` in the example above).
22610 const importedName = (firstDecl.propertyName || firstDecl.name).text;
22611 // The first symbol name refers to the local name, which is replaced by `importedName` above.
22612 // Any remaining symbol names make up the complete path to the value.
22613 const [_localName, ...nestedPath] = symbols.symbolNames;
22614 const moduleName = extractModuleName(firstDecl.parent.parent.parent);
22615 return {
22616 kind: 1 /* IMPORTED */,
22617 valueDeclaration: decl.valueDeclaration,
22618 moduleName,
22619 importedName,
22620 nestedPath
22621 };
22622 }
22623 else if (ts$1.isNamespaceImport(firstDecl)) {
22624 // The import is a namespace import
22625 // import * as Foo from 'foo';
22626 if (firstDecl.parent.isTypeOnly) {
22627 // Type-only imports cannot be represented as value.
22628 return typeOnlyImport(typeNode, firstDecl.parent);
22629 }
22630 if (symbols.symbolNames.length === 1) {
22631 // The type refers to the namespace itself, which cannot be represented as a value.
22632 return namespaceImport(typeNode, firstDecl.parent);
22633 }
22634 // The first symbol name refers to the local name of the namespace, which is is discarded
22635 // as a new namespace import will be generated. This is followed by the symbol name that needs
22636 // to be imported and any remaining names that constitute the complete path to the value.
22637 const [_ns, importedName, ...nestedPath] = symbols.symbolNames;
22638 const moduleName = extractModuleName(firstDecl.parent.parent);
22639 return {
22640 kind: 1 /* IMPORTED */,
22641 valueDeclaration: decl.valueDeclaration,
22642 moduleName,
22643 importedName,
22644 nestedPath
22645 };
22646 }
22647 }
22648 // If the type is not imported, the type reference can be converted into an expression as is.
22649 const expression = typeNodeToValueExpr(typeNode);
22650 if (expression !== null) {
22651 return {
22652 kind: 0 /* LOCAL */,
22653 expression,
22654 defaultImportStatement: null,
22655 };
22656 }
22657 else {
22658 return unsupportedType(typeNode);
22659 }
22660 }
22661 function unsupportedType(typeNode) {
22662 return {
22663 kind: 2 /* UNAVAILABLE */,
22664 reason: { kind: 5 /* UNSUPPORTED */, typeNode },
22665 };
22666 }
22667 function noValueDeclaration(typeNode, decl) {
22668 return {
22669 kind: 2 /* UNAVAILABLE */,
22670 reason: { kind: 1 /* NO_VALUE_DECLARATION */, typeNode, decl },
22671 };
22672 }
22673 function typeOnlyImport(typeNode, importClause) {
22674 return {
22675 kind: 2 /* UNAVAILABLE */,
22676 reason: { kind: 2 /* TYPE_ONLY_IMPORT */, typeNode, importClause },
22677 };
22678 }
22679 function unknownReference(typeNode) {
22680 return {
22681 kind: 2 /* UNAVAILABLE */,
22682 reason: { kind: 3 /* UNKNOWN_REFERENCE */, typeNode },
22683 };
22684 }
22685 function namespaceImport(typeNode, importClause) {
22686 return {
22687 kind: 2 /* UNAVAILABLE */,
22688 reason: { kind: 4 /* NAMESPACE */, typeNode, importClause },
22689 };
22690 }
22691 function missingType() {
22692 return {
22693 kind: 2 /* UNAVAILABLE */,
22694 reason: { kind: 0 /* MISSING_TYPE */ },
22695 };
22696 }
22697 /**
22698 * Attempt to extract a `ts.Expression` that's equivalent to a `ts.TypeNode`, as the two have
22699 * different AST shapes but can reference the same symbols.
22700 *
22701 * This will return `null` if an equivalent expression cannot be constructed.
22702 */
22703 function typeNodeToValueExpr(node) {
22704 if (ts$1.isTypeReferenceNode(node)) {
22705 return entityNameToValue(node.typeName);
22706 }
22707 else {
22708 return null;
22709 }
22710 }
22711 /**
22712 * Resolve a `TypeReference` node to the `ts.Symbol`s for both its declaration and its local source.
22713 *
22714 * In the event that the `TypeReference` refers to a locally declared symbol, these will be the
22715 * same. If the `TypeReference` refers to an imported symbol, then `decl` will be the fully resolved
22716 * `ts.Symbol` of the referenced symbol. `local` will be the `ts.Symbol` of the `ts.Identifier`
22717 * which points to the import statement by which the symbol was imported.
22718 *
22719 * All symbol names that make up the type reference are returned left-to-right into the
22720 * `symbolNames` array, which is guaranteed to include at least one entry.
22721 */
22722 function resolveTypeSymbols(typeRef, checker) {
22723 const typeName = typeRef.typeName;
22724 // typeRefSymbol is the ts.Symbol of the entire type reference.
22725 const typeRefSymbol = checker.getSymbolAtLocation(typeName);
22726 if (typeRefSymbol === undefined) {
22727 return null;
22728 }
22729 // `local` is the `ts.Symbol` for the local `ts.Identifier` for the type.
22730 // If the type is actually locally declared or is imported by name, for example:
22731 // import {Foo} from './foo';
22732 // then it'll be the same as `typeRefSymbol`.
22733 //
22734 // If the type is imported via a namespace import, for example:
22735 // import * as foo from './foo';
22736 // and then referenced as:
22737 // constructor(f: foo.Foo)
22738 // then `local` will be the `ts.Symbol` of `foo`, whereas `typeRefSymbol` will be the `ts.Symbol`
22739 // of `foo.Foo`. This allows tracking of the import behind whatever type reference exists.
22740 let local = typeRefSymbol;
22741 // Destructure a name like `foo.X.Y.Z` as follows:
22742 // - in `leftMost`, the `ts.Identifier` of the left-most name (`foo`) in the qualified name.
22743 // This identifier is used to resolve the `ts.Symbol` for `local`.
22744 // - in `symbolNames`, all names involved in the qualified path, or a single symbol name if the
22745 // type is not qualified.
22746 let leftMost = typeName;
22747 const symbolNames = [];
22748 while (ts$1.isQualifiedName(leftMost)) {
22749 symbolNames.unshift(leftMost.right.text);
22750 leftMost = leftMost.left;
22751 }
22752 symbolNames.unshift(leftMost.text);
22753 if (leftMost !== typeName) {
22754 const localTmp = checker.getSymbolAtLocation(leftMost);
22755 if (localTmp !== undefined) {
22756 local = localTmp;
22757 }
22758 }
22759 // De-alias the top-level type reference symbol to get the symbol of the actual declaration.
22760 let decl = typeRefSymbol;
22761 if (typeRefSymbol.flags & ts$1.SymbolFlags.Alias) {
22762 decl = checker.getAliasedSymbol(typeRefSymbol);
22763 }
22764 return { local, decl, symbolNames };
22765 }
22766 function entityNameToValue(node) {
22767 if (ts$1.isQualifiedName(node)) {
22768 const left = entityNameToValue(node.left);
22769 return left !== null ? ts$1.createPropertyAccess(left, node.right) : null;
22770 }
22771 else if (ts$1.isIdentifier(node)) {
22772 return ts$1.getMutableClone(node);
22773 }
22774 else {
22775 return null;
22776 }
22777 }
22778 function extractModuleName(node) {
22779 if (!ts$1.isStringLiteral(node.moduleSpecifier)) {
22780 throw new Error('not a module specifier');
22781 }
22782 return node.moduleSpecifier.text;
22783 }
22784
22785 /**
22786 * @license
22787 * Copyright Google LLC All Rights Reserved.
22788 *
22789 * Use of this source code is governed by an MIT-style license that can be
22790 * found in the LICENSE file at https://angular.io/license
22791 */
22792 function isNamedClassDeclaration(node) {
22793 return ts$1.isClassDeclaration(node) && isIdentifier$1(node.name);
22794 }
22795 function isIdentifier$1(node) {
22796 return node !== undefined && ts$1.isIdentifier(node);
22797 }
22798
22799 /**
22800 * @license
22801 * Copyright Google LLC All Rights Reserved.
22802 *
22803 * Use of this source code is governed by an MIT-style license that can be
22804 * found in the LICENSE file at https://angular.io/license
22805 */
22806 /**
22807 * reflector.ts implements static reflection of declarations using the TypeScript `ts.TypeChecker`.
22808 */
22809 class TypeScriptReflectionHost {
22810 constructor(checker) {
22811 this.checker = checker;
22812 }
22813 getDecoratorsOfDeclaration(declaration) {
22814 if (declaration.decorators === undefined || declaration.decorators.length === 0) {
22815 return null;
22816 }
22817 return declaration.decorators.map(decorator => this._reflectDecorator(decorator))
22818 .filter((dec) => dec !== null);
22819 }
22820 getMembersOfClass(clazz) {
22821 const tsClazz = castDeclarationToClassOrDie(clazz);
22822 return tsClazz.members.map(member => this._reflectMember(member))
22823 .filter((member) => member !== null);
22824 }
22825 getConstructorParameters(clazz) {
22826 const tsClazz = castDeclarationToClassOrDie(clazz);
22827 const isDeclaration = tsClazz.getSourceFile().isDeclarationFile;
22828 // For non-declaration files, we want to find the constructor with a `body`. The constructors
22829 // without a `body` are overloads whereas we want the implementation since it's the one that'll
22830 // be executed and which can have decorators. For declaration files, we take the first one that
22831 // we get.
22832 const ctor = tsClazz.members.find((member) => ts$1.isConstructorDeclaration(member) && (isDeclaration || member.body !== undefined));
22833 if (ctor === undefined) {
22834 return null;
22835 }
22836 return ctor.parameters.map(node => {
22837 // The name of the parameter is easy.
22838 const name = parameterName(node.name);
22839 const decorators = this.getDecoratorsOfDeclaration(node);
22840 // It may or may not be possible to write an expression that refers to the value side of the
22841 // type named for the parameter.
22842 let originalTypeNode = node.type || null;
22843 let typeNode = originalTypeNode;
22844 // Check if we are dealing with a simple nullable union type e.g. `foo: Foo|null`
22845 // and extract the type. More complex union types e.g. `foo: Foo|Bar` are not supported.
22846 // We also don't need to support `foo: Foo|undefined` because Angular's DI injects `null` for
22847 // optional tokes that don't have providers.
22848 if (typeNode && ts$1.isUnionTypeNode(typeNode)) {
22849 let childTypeNodes = typeNode.types.filter(childTypeNode => !(ts$1.isLiteralTypeNode(childTypeNode) &&
22850 childTypeNode.literal.kind === ts$1.SyntaxKind.NullKeyword));
22851 if (childTypeNodes.length === 1) {
22852 typeNode = childTypeNodes[0];
22853 }
22854 }
22855 const typeValueReference = typeToValue(typeNode, this.checker);
22856 return {
22857 name,
22858 nameNode: node.name,
22859 typeValueReference,
22860 typeNode: originalTypeNode,
22861 decorators,
22862 };
22863 });
22864 }
22865 getImportOfIdentifier(id) {
22866 const directImport = this.getDirectImportOfIdentifier(id);
22867 if (directImport !== null) {
22868 return directImport;
22869 }
22870 else if (ts$1.isQualifiedName(id.parent) && id.parent.right === id) {
22871 return this.getImportOfNamespacedIdentifier(id, getQualifiedNameRoot(id.parent));
22872 }
22873 else if (ts$1.isPropertyAccessExpression(id.parent) && id.parent.name === id) {
22874 return this.getImportOfNamespacedIdentifier(id, getFarLeftIdentifier(id.parent));
22875 }
22876 else {
22877 return null;
22878 }
22879 }
22880 getExportsOfModule(node) {
22881 // In TypeScript code, modules are only ts.SourceFiles. Throw if the node isn't a module.
22882 if (!ts$1.isSourceFile(node)) {
22883 throw new Error(`getExportsOfModule() called on non-SourceFile in TS code`);
22884 }
22885 // Reflect the module to a Symbol, and use getExportsOfModule() to get a list of exported
22886 // Symbols.
22887 const symbol = this.checker.getSymbolAtLocation(node);
22888 if (symbol === undefined) {
22889 return null;
22890 }
22891 const map = new Map();
22892 this.checker.getExportsOfModule(symbol).forEach(exportSymbol => {
22893 // Map each exported Symbol to a Declaration and add it to the map.
22894 const decl = this.getDeclarationOfSymbol(exportSymbol, null);
22895 if (decl !== null) {
22896 map.set(exportSymbol.name, decl);
22897 }
22898 });
22899 return map;
22900 }
22901 isClass(node) {
22902 // For our purposes, classes are "named" ts.ClassDeclarations;
22903 // (`node.name` can be undefined in unnamed default exports: `default export class { ... }`).
22904 return isNamedClassDeclaration(node);
22905 }
22906 hasBaseClass(clazz) {
22907 return this.getBaseClassExpression(clazz) !== null;
22908 }
22909 getBaseClassExpression(clazz) {
22910 if (!(ts$1.isClassDeclaration(clazz) || ts$1.isClassExpression(clazz)) ||
22911 clazz.heritageClauses === undefined) {
22912 return null;
22913 }
22914 const extendsClause = clazz.heritageClauses.find(clause => clause.token === ts$1.SyntaxKind.ExtendsKeyword);
22915 if (extendsClause === undefined) {
22916 return null;
22917 }
22918 const extendsType = extendsClause.types[0];
22919 if (extendsType === undefined) {
22920 return null;
22921 }
22922 return extendsType.expression;
22923 }
22924 getDeclarationOfIdentifier(id) {
22925 // Resolve the identifier to a Symbol, and return the declaration of that.
22926 let symbol = this.checker.getSymbolAtLocation(id);
22927 if (symbol === undefined) {
22928 return null;
22929 }
22930 return this.getDeclarationOfSymbol(symbol, id);
22931 }
22932 getDefinitionOfFunction(node) {
22933 if (!ts$1.isFunctionDeclaration(node) && !ts$1.isMethodDeclaration(node) &&
22934 !ts$1.isFunctionExpression(node)) {
22935 return null;
22936 }
22937 return {
22938 node,
22939 body: node.body !== undefined ? Array.from(node.body.statements) : null,
22940 parameters: node.parameters.map(param => {
22941 const name = parameterName(param.name);
22942 const initializer = param.initializer || null;
22943 return { name, node: param, initializer };
22944 }),
22945 };
22946 }
22947 getGenericArityOfClass(clazz) {
22948 if (!ts$1.isClassDeclaration(clazz)) {
22949 return null;
22950 }
22951 return clazz.typeParameters !== undefined ? clazz.typeParameters.length : 0;
22952 }
22953 getVariableValue(declaration) {
22954 return declaration.initializer || null;
22955 }
22956 getDtsDeclaration(_) {
22957 return null;
22958 }
22959 getInternalNameOfClass(clazz) {
22960 return clazz.name;
22961 }
22962 getAdjacentNameOfClass(clazz) {
22963 return clazz.name;
22964 }
22965 getDirectImportOfIdentifier(id) {
22966 const symbol = this.checker.getSymbolAtLocation(id);
22967 if (symbol === undefined || symbol.declarations === undefined ||
22968 symbol.declarations.length !== 1) {
22969 return null;
22970 }
22971 const decl = symbol.declarations[0];
22972 const importDecl = getContainingImportDeclaration(decl);
22973 // Ignore declarations that are defined locally (not imported).
22974 if (importDecl === null) {
22975 return null;
22976 }
22977 // The module specifier is guaranteed to be a string literal, so this should always pass.
22978 if (!ts$1.isStringLiteral(importDecl.moduleSpecifier)) {
22979 // Not allowed to happen in TypeScript ASTs.
22980 return null;
22981 }
22982 return { from: importDecl.moduleSpecifier.text, name: getExportedName(decl, id) };
22983 }
22984 /**
22985 * Try to get the import info for this identifier as though it is a namespaced import.
22986 *
22987 * For example, if the identifier is the `Directive` part of a qualified type chain like:
22988 *
22989 * ```
22990 * core.Directive
22991 * ```
22992 *
22993 * then it might be that `core` is a namespace import such as:
22994 *
22995 * ```
22996 * import * as core from 'tslib';
22997 * ```
22998 *
22999 * @param id the TypeScript identifier to find the import info for.
23000 * @returns The import info if this is a namespaced import or `null`.
23001 */
23002 getImportOfNamespacedIdentifier(id, namespaceIdentifier) {
23003 if (namespaceIdentifier === null) {
23004 return null;
23005 }
23006 const namespaceSymbol = this.checker.getSymbolAtLocation(namespaceIdentifier);
23007 if (!namespaceSymbol) {
23008 return null;
23009 }
23010 const declaration = namespaceSymbol.declarations.length === 1 ? namespaceSymbol.declarations[0] : null;
23011 if (!declaration) {
23012 return null;
23013 }
23014 const namespaceDeclaration = ts$1.isNamespaceImport(declaration) ? declaration : null;
23015 if (!namespaceDeclaration) {
23016 return null;
23017 }
23018 const importDeclaration = namespaceDeclaration.parent.parent;
23019 if (!ts$1.isStringLiteral(importDeclaration.moduleSpecifier)) {
23020 // Should not happen as this would be invalid TypesScript
23021 return null;
23022 }
23023 return {
23024 from: importDeclaration.moduleSpecifier.text,
23025 name: id.text,
23026 };
23027 }
23028 /**
23029 * Resolve a `ts.Symbol` to its declaration, keeping track of the `viaModule` along the way.
23030 */
23031 getDeclarationOfSymbol(symbol, originalId) {
23032 // If the symbol points to a ShorthandPropertyAssignment, resolve it.
23033 let valueDeclaration = undefined;
23034 if (symbol.valueDeclaration !== undefined) {
23035 valueDeclaration = symbol.valueDeclaration;
23036 }
23037 else if (symbol.declarations !== undefined && symbol.declarations.length > 0) {
23038 valueDeclaration = symbol.declarations[0];
23039 }
23040 if (valueDeclaration !== undefined && ts$1.isShorthandPropertyAssignment(valueDeclaration)) {
23041 const shorthandSymbol = this.checker.getShorthandAssignmentValueSymbol(valueDeclaration);
23042 if (shorthandSymbol === undefined) {
23043 return null;
23044 }
23045 return this.getDeclarationOfSymbol(shorthandSymbol, originalId);
23046 }
23047 else if (valueDeclaration !== undefined && ts$1.isExportSpecifier(valueDeclaration)) {
23048 const targetSymbol = this.checker.getExportSpecifierLocalTargetSymbol(valueDeclaration);
23049 if (targetSymbol === undefined) {
23050 return null;
23051 }
23052 return this.getDeclarationOfSymbol(targetSymbol, originalId);
23053 }
23054 const importInfo = originalId && this.getImportOfIdentifier(originalId);
23055 const viaModule = importInfo !== null && importInfo.from !== null && !importInfo.from.startsWith('.') ?
23056 importInfo.from :
23057 null;
23058 // Now, resolve the Symbol to its declaration by following any and all aliases.
23059 while (symbol.flags & ts$1.SymbolFlags.Alias) {
23060 symbol = this.checker.getAliasedSymbol(symbol);
23061 }
23062 // Look at the resolved Symbol's declarations and pick one of them to return. Value declarations
23063 // are given precedence over type declarations.
23064 if (symbol.valueDeclaration !== undefined) {
23065 return {
23066 node: symbol.valueDeclaration,
23067 known: null,
23068 viaModule,
23069 identity: null,
23070 kind: 0 /* Concrete */,
23071 };
23072 }
23073 else if (symbol.declarations !== undefined && symbol.declarations.length > 0) {
23074 return {
23075 node: symbol.declarations[0],
23076 known: null,
23077 viaModule,
23078 identity: null,
23079 kind: 0 /* Concrete */,
23080 };
23081 }
23082 else {
23083 return null;
23084 }
23085 }
23086 _reflectDecorator(node) {
23087 // Attempt to resolve the decorator expression into a reference to a concrete Identifier. The
23088 // expression may contain a call to a function which returns the decorator function, in which
23089 // case we want to return the arguments.
23090 let decoratorExpr = node.expression;
23091 let args = null;
23092 // Check for call expressions.
23093 if (ts$1.isCallExpression(decoratorExpr)) {
23094 args = Array.from(decoratorExpr.arguments);
23095 decoratorExpr = decoratorExpr.expression;
23096 }
23097 // The final resolved decorator should be a `ts.Identifier` - if it's not, then something is
23098 // wrong and the decorator can't be resolved statically.
23099 if (!isDecoratorIdentifier(decoratorExpr)) {
23100 return null;
23101 }
23102 const decoratorIdentifier = ts$1.isIdentifier(decoratorExpr) ? decoratorExpr : decoratorExpr.name;
23103 const importDecl = this.getImportOfIdentifier(decoratorIdentifier);
23104 return {
23105 name: decoratorIdentifier.text,
23106 identifier: decoratorExpr,
23107 import: importDecl,
23108 node,
23109 args,
23110 };
23111 }
23112 _reflectMember(node) {
23113 let kind = null;
23114 let value = null;
23115 let name = null;
23116 let nameNode = null;
23117 if (ts$1.isPropertyDeclaration(node)) {
23118 kind = ClassMemberKind.Property;
23119 value = node.initializer || null;
23120 }
23121 else if (ts$1.isGetAccessorDeclaration(node)) {
23122 kind = ClassMemberKind.Getter;
23123 }
23124 else if (ts$1.isSetAccessorDeclaration(node)) {
23125 kind = ClassMemberKind.Setter;
23126 }
23127 else if (ts$1.isMethodDeclaration(node)) {
23128 kind = ClassMemberKind.Method;
23129 }
23130 else if (ts$1.isConstructorDeclaration(node)) {
23131 kind = ClassMemberKind.Constructor;
23132 }
23133 else {
23134 return null;
23135 }
23136 if (ts$1.isConstructorDeclaration(node)) {
23137 name = 'constructor';
23138 }
23139 else if (ts$1.isIdentifier(node.name)) {
23140 name = node.name.text;
23141 nameNode = node.name;
23142 }
23143 else if (ts$1.isStringLiteral(node.name)) {
23144 name = node.name.text;
23145 nameNode = node.name;
23146 }
23147 else {
23148 return null;
23149 }
23150 const decorators = this.getDecoratorsOfDeclaration(node);
23151 const isStatic = node.modifiers !== undefined &&
23152 node.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.StaticKeyword);
23153 return {
23154 node,
23155 implementation: node,
23156 kind,
23157 type: node.type || null,
23158 name,
23159 nameNode,
23160 decorators,
23161 value,
23162 isStatic,
23163 };
23164 }
23165 }
23166 function reflectTypeEntityToDeclaration(type, checker) {
23167 let realSymbol = checker.getSymbolAtLocation(type);
23168 if (realSymbol === undefined) {
23169 throw new Error(`Cannot resolve type entity ${type.getText()} to symbol`);
23170 }
23171 while (realSymbol.flags & ts$1.SymbolFlags.Alias) {
23172 realSymbol = checker.getAliasedSymbol(realSymbol);
23173 }
23174 let node = null;
23175 if (realSymbol.valueDeclaration !== undefined) {
23176 node = realSymbol.valueDeclaration;
23177 }
23178 else if (realSymbol.declarations !== undefined && realSymbol.declarations.length === 1) {
23179 node = realSymbol.declarations[0];
23180 }
23181 else {
23182 throw new Error(`Cannot resolve type entity symbol to declaration`);
23183 }
23184 if (ts$1.isQualifiedName(type)) {
23185 if (!ts$1.isIdentifier(type.left)) {
23186 throw new Error(`Cannot handle qualified name with non-identifier lhs`);
23187 }
23188 const symbol = checker.getSymbolAtLocation(type.left);
23189 if (symbol === undefined || symbol.declarations === undefined ||
23190 symbol.declarations.length !== 1) {
23191 throw new Error(`Cannot resolve qualified type entity lhs to symbol`);
23192 }
23193 const decl = symbol.declarations[0];
23194 if (ts$1.isNamespaceImport(decl)) {
23195 const clause = decl.parent;
23196 const importDecl = clause.parent;
23197 if (!ts$1.isStringLiteral(importDecl.moduleSpecifier)) {
23198 throw new Error(`Module specifier is not a string`);
23199 }
23200 return { node, from: importDecl.moduleSpecifier.text };
23201 }
23202 else {
23203 throw new Error(`Unknown import type?`);
23204 }
23205 }
23206 else {
23207 return { node, from: null };
23208 }
23209 }
23210 function filterToMembersWithDecorator(members, name, module) {
23211 return members.filter(member => !member.isStatic)
23212 .map(member => {
23213 if (member.decorators === null) {
23214 return null;
23215 }
23216 const decorators = member.decorators.filter(dec => {
23217 if (dec.import !== null) {
23218 return dec.import.name === name && (module === undefined || dec.import.from === module);
23219 }
23220 else {
23221 return dec.name === name && module === undefined;
23222 }
23223 });
23224 if (decorators.length === 0) {
23225 return null;
23226 }
23227 return { member, decorators };
23228 })
23229 .filter((value) => value !== null);
23230 }
23231 function reflectObjectLiteral(node) {
23232 const map = new Map();
23233 node.properties.forEach(prop => {
23234 if (ts$1.isPropertyAssignment(prop)) {
23235 const name = propertyNameToString(prop.name);
23236 if (name === null) {
23237 return;
23238 }
23239 map.set(name, prop.initializer);
23240 }
23241 else if (ts$1.isShorthandPropertyAssignment(prop)) {
23242 map.set(prop.name.text, prop.name);
23243 }
23244 else {
23245 return;
23246 }
23247 });
23248 return map;
23249 }
23250 function castDeclarationToClassOrDie(declaration) {
23251 if (!ts$1.isClassDeclaration(declaration)) {
23252 throw new Error(`Reflecting on a ${ts$1.SyntaxKind[declaration.kind]} instead of a ClassDeclaration.`);
23253 }
23254 return declaration;
23255 }
23256 function parameterName(name) {
23257 if (ts$1.isIdentifier(name)) {
23258 return name.text;
23259 }
23260 else {
23261 return null;
23262 }
23263 }
23264 function propertyNameToString(node) {
23265 if (ts$1.isIdentifier(node) || ts$1.isStringLiteral(node) || ts$1.isNumericLiteral(node)) {
23266 return node.text;
23267 }
23268 else {
23269 return null;
23270 }
23271 }
23272 /**
23273 * Compute the left most identifier in a qualified type chain. E.g. the `a` of `a.b.c.SomeType`.
23274 * @param qualifiedName The starting property access expression from which we want to compute
23275 * the left most identifier.
23276 * @returns the left most identifier in the chain or `null` if it is not an identifier.
23277 */
23278 function getQualifiedNameRoot(qualifiedName) {
23279 while (ts$1.isQualifiedName(qualifiedName.left)) {
23280 qualifiedName = qualifiedName.left;
23281 }
23282 return ts$1.isIdentifier(qualifiedName.left) ? qualifiedName.left : null;
23283 }
23284 /**
23285 * Compute the left most identifier in a property access chain. E.g. the `a` of `a.b.c.d`.
23286 * @param propertyAccess The starting property access expression from which we want to compute
23287 * the left most identifier.
23288 * @returns the left most identifier in the chain or `null` if it is not an identifier.
23289 */
23290 function getFarLeftIdentifier(propertyAccess) {
23291 while (ts$1.isPropertyAccessExpression(propertyAccess.expression)) {
23292 propertyAccess = propertyAccess.expression;
23293 }
23294 return ts$1.isIdentifier(propertyAccess.expression) ? propertyAccess.expression : null;
23295 }
23296 /**
23297 * Return the ImportDeclaration for the given `node` if it is either an `ImportSpecifier` or a
23298 * `NamespaceImport`. If not return `null`.
23299 */
23300 function getContainingImportDeclaration(node) {
23301 return ts$1.isImportSpecifier(node) ? node.parent.parent.parent :
23302 ts$1.isNamespaceImport(node) ? node.parent.parent : null;
23303 }
23304 /**
23305 * Compute the name by which the `decl` was exported, not imported.
23306 * If no such declaration can be found (e.g. it is a namespace import)
23307 * then fallback to the `originalId`.
23308 */
23309 function getExportedName(decl, originalId) {
23310 return ts$1.isImportSpecifier(decl) ?
23311 (decl.propertyName !== undefined ? decl.propertyName : decl.name).text :
23312 originalId.text;
23313 }
23314
23315 /**
23316 * @license
23317 * Copyright Google LLC All Rights Reserved.
23318 *
23319 * Use of this source code is governed by an MIT-style license that can be
23320 * found in the LICENSE file at https://angular.io/license
23321 */
23322 /**
23323 * A mapping of component property and template binding property names, for example containing the
23324 * inputs of a particular directive or component.
23325 *
23326 * A single component property has exactly one input/output annotation (and therefore one binding
23327 * property name) associated with it, but the same binding property name may be shared across many
23328 * component property names.
23329 *
23330 * Allows bidirectional querying of the mapping - looking up all inputs/outputs with a given
23331 * property name, or mapping from a specific class property to its binding property name.
23332 */
23333 class ClassPropertyMapping {
23334 constructor(forwardMap) {
23335 this.forwardMap = forwardMap;
23336 this.reverseMap = reverseMapFromForwardMap(forwardMap);
23337 }
23338 /**
23339 * Construct a `ClassPropertyMapping` with no entries.
23340 */
23341 static empty() {
23342 return new ClassPropertyMapping(new Map());
23343 }
23344 /**
23345 * Construct a `ClassPropertyMapping` from a primitive JS object which maps class property names
23346 * to either binding property names or an array that contains both names, which is used in on-disk
23347 * metadata formats (e.g. in .d.ts files).
23348 */
23349 static fromMappedObject(obj) {
23350 const forwardMap = new Map();
23351 for (const classPropertyName of Object.keys(obj)) {
23352 const value = obj[classPropertyName];
23353 const bindingPropertyName = Array.isArray(value) ? value[0] : value;
23354 const inputOrOutput = { classPropertyName, bindingPropertyName };
23355 forwardMap.set(classPropertyName, inputOrOutput);
23356 }
23357 return new ClassPropertyMapping(forwardMap);
23358 }
23359 /**
23360 * Merge two mappings into one, with class properties from `b` taking precedence over class
23361 * properties from `a`.
23362 */
23363 static merge(a, b) {
23364 const forwardMap = new Map(a.forwardMap.entries());
23365 for (const [classPropertyName, inputOrOutput] of b.forwardMap) {
23366 forwardMap.set(classPropertyName, inputOrOutput);
23367 }
23368 return new ClassPropertyMapping(forwardMap);
23369 }
23370 /**
23371 * All class property names mapped in this mapping.
23372 */
23373 get classPropertyNames() {
23374 return Array.from(this.forwardMap.keys());
23375 }
23376 /**
23377 * All binding property names mapped in this mapping.
23378 */
23379 get propertyNames() {
23380 return Array.from(this.reverseMap.keys());
23381 }
23382 /**
23383 * Check whether a mapping for the given property name exists.
23384 */
23385 hasBindingPropertyName(propertyName) {
23386 return this.reverseMap.has(propertyName);
23387 }
23388 /**
23389 * Lookup all `InputOrOutput`s that use this `propertyName`.
23390 */
23391 getByBindingPropertyName(propertyName) {
23392 return this.reverseMap.has(propertyName) ? this.reverseMap.get(propertyName) : null;
23393 }
23394 /**
23395 * Lookup the `InputOrOutput` associated with a `classPropertyName`.
23396 */
23397 getByClassPropertyName(classPropertyName) {
23398 return this.forwardMap.has(classPropertyName) ? this.forwardMap.get(classPropertyName) : null;
23399 }
23400 /**
23401 * Convert this mapping to a primitive JS object which maps each class property directly to the
23402 * binding property name associated with it.
23403 */
23404 toDirectMappedObject() {
23405 const obj = {};
23406 for (const [classPropertyName, inputOrOutput] of this.forwardMap) {
23407 obj[classPropertyName] = inputOrOutput.bindingPropertyName;
23408 }
23409 return obj;
23410 }
23411 /**
23412 * Convert this mapping to a primitive JS object which maps each class property either to itself
23413 * (for cases where the binding property name is the same) or to an array which contains both
23414 * names if they differ.
23415 *
23416 * This object format is used when mappings are serialized (for example into .d.ts files).
23417 */
23418 toJointMappedObject() {
23419 const obj = {};
23420 for (const [classPropertyName, inputOrOutput] of this.forwardMap) {
23421 if (inputOrOutput.bindingPropertyName === classPropertyName) {
23422 obj[classPropertyName] = inputOrOutput.bindingPropertyName;
23423 }
23424 else {
23425 obj[classPropertyName] = [inputOrOutput.bindingPropertyName, classPropertyName];
23426 }
23427 }
23428 return obj;
23429 }
23430 /**
23431 * Implement the iterator protocol and return entry objects which contain the class and binding
23432 * property names (and are useful for destructuring).
23433 */
23434 *[Symbol.iterator]() {
23435 for (const [classPropertyName, inputOrOutput] of this.forwardMap.entries()) {
23436 yield [classPropertyName, inputOrOutput.bindingPropertyName];
23437 }
23438 }
23439 }
23440 function reverseMapFromForwardMap(forwardMap) {
23441 const reverseMap = new Map();
23442 for (const [_, inputOrOutput] of forwardMap) {
23443 if (!reverseMap.has(inputOrOutput.bindingPropertyName)) {
23444 reverseMap.set(inputOrOutput.bindingPropertyName, []);
23445 }
23446 reverseMap.get(inputOrOutput.bindingPropertyName).push(inputOrOutput);
23447 }
23448 return reverseMap;
23449 }
23450
23451 /**
23452 * @license
23453 * Copyright Google LLC All Rights Reserved.
23454 *
23455 * Use of this source code is governed by an MIT-style license that can be
23456 * found in the LICENSE file at https://angular.io/license
23457 */
23458 function extractReferencesFromType(checker, def, ngModuleImportedFrom, resolutionContext) {
23459 if (!ts$1.isTupleTypeNode(def)) {
23460 return [];
23461 }
23462 return def.elements.map(element => {
23463 if (!ts$1.isTypeQueryNode(element)) {
23464 throw new Error(`Expected TypeQueryNode: ${nodeDebugInfo(element)}`);
23465 }
23466 const type = element.exprName;
23467 const { node, from } = reflectTypeEntityToDeclaration(type, checker);
23468 if (!isNamedClassDeclaration(node)) {
23469 throw new Error(`Expected named ClassDeclaration: ${nodeDebugInfo(node)}`);
23470 }
23471 const specifier = (from !== null && !from.startsWith('.') ? from : ngModuleImportedFrom);
23472 if (specifier !== null) {
23473 return new Reference$1(node, { specifier, resolutionContext });
23474 }
23475 else {
23476 return new Reference$1(node);
23477 }
23478 });
23479 }
23480 function readStringType(type) {
23481 if (!ts$1.isLiteralTypeNode(type) || !ts$1.isStringLiteral(type.literal)) {
23482 return null;
23483 }
23484 return type.literal.text;
23485 }
23486 function readStringMapType(type) {
23487 if (!ts$1.isTypeLiteralNode(type)) {
23488 return {};
23489 }
23490 const obj = {};
23491 type.members.forEach(member => {
23492 if (!ts$1.isPropertySignature(member) || member.type === undefined || member.name === undefined ||
23493 !ts$1.isStringLiteral(member.name)) {
23494 return;
23495 }
23496 const value = readStringType(member.type);
23497 if (value === null) {
23498 return null;
23499 }
23500 obj[member.name.text] = value;
23501 });
23502 return obj;
23503 }
23504 function readStringArrayType(type) {
23505 if (!ts$1.isTupleTypeNode(type)) {
23506 return [];
23507 }
23508 const res = [];
23509 type.elements.forEach(el => {
23510 if (!ts$1.isLiteralTypeNode(el) || !ts$1.isStringLiteral(el.literal)) {
23511 return;
23512 }
23513 res.push(el.literal.text);
23514 });
23515 return res;
23516 }
23517 /**
23518 * Inspects the class' members and extracts the metadata that is used when type-checking templates
23519 * that use the directive. This metadata does not contain information from a base class, if any,
23520 * making this metadata invariant to changes of inherited classes.
23521 */
23522 function extractDirectiveTypeCheckMeta(node, inputs, reflector) {
23523 const members = reflector.getMembersOfClass(node);
23524 const staticMembers = members.filter(member => member.isStatic);
23525 const ngTemplateGuards = staticMembers.map(extractTemplateGuard)
23526 .filter((guard) => guard !== null);
23527 const hasNgTemplateContextGuard = staticMembers.some(member => member.kind === ClassMemberKind.Method && member.name === 'ngTemplateContextGuard');
23528 const coercedInputFields = new Set(staticMembers.map(extractCoercedInput)
23529 .filter((inputName) => inputName !== null));
23530 const restrictedInputFields = new Set();
23531 const stringLiteralInputFields = new Set();
23532 const undeclaredInputFields = new Set();
23533 for (const classPropertyName of inputs.classPropertyNames) {
23534 const field = members.find(member => member.name === classPropertyName);
23535 if (field === undefined || field.node === null) {
23536 undeclaredInputFields.add(classPropertyName);
23537 continue;
23538 }
23539 if (isRestricted(field.node)) {
23540 restrictedInputFields.add(classPropertyName);
23541 }
23542 if (field.nameNode !== null && ts$1.isStringLiteral(field.nameNode)) {
23543 stringLiteralInputFields.add(classPropertyName);
23544 }
23545 }
23546 const arity = reflector.getGenericArityOfClass(node);
23547 return {
23548 hasNgTemplateContextGuard,
23549 ngTemplateGuards,
23550 coercedInputFields,
23551 restrictedInputFields,
23552 stringLiteralInputFields,
23553 undeclaredInputFields,
23554 isGeneric: arity !== null && arity > 0,
23555 };
23556 }
23557 function isRestricted(node) {
23558 if (node.modifiers === undefined) {
23559 return false;
23560 }
23561 return node.modifiers.some(modifier => modifier.kind === ts$1.SyntaxKind.PrivateKeyword ||
23562 modifier.kind === ts$1.SyntaxKind.ProtectedKeyword ||
23563 modifier.kind === ts$1.SyntaxKind.ReadonlyKeyword);
23564 }
23565 function extractTemplateGuard(member) {
23566 if (!member.name.startsWith('ngTemplateGuard_')) {
23567 return null;
23568 }
23569 const inputName = afterUnderscore(member.name);
23570 if (member.kind === ClassMemberKind.Property) {
23571 let type = null;
23572 if (member.type !== null && ts$1.isLiteralTypeNode(member.type) &&
23573 ts$1.isStringLiteral(member.type.literal)) {
23574 type = member.type.literal.text;
23575 }
23576 // Only property members with string literal type 'binding' are considered as template guard.
23577 if (type !== 'binding') {
23578 return null;
23579 }
23580 return { inputName, type };
23581 }
23582 else if (member.kind === ClassMemberKind.Method) {
23583 return { inputName, type: 'invocation' };
23584 }
23585 else {
23586 return null;
23587 }
23588 }
23589 function extractCoercedInput(member) {
23590 if (member.kind !== ClassMemberKind.Property || !member.name.startsWith('ngAcceptInputType_')) {
23591 return null;
23592 }
23593 return afterUnderscore(member.name);
23594 }
23595 /**
23596 * A `MetadataReader` that reads from an ordered set of child readers until it obtains the requested
23597 * metadata.
23598 *
23599 * This is used to combine `MetadataReader`s that read from different sources (e.g. from a registry
23600 * and from .d.ts files).
23601 */
23602 class CompoundMetadataReader {
23603 constructor(readers) {
23604 this.readers = readers;
23605 }
23606 getDirectiveMetadata(node) {
23607 for (const reader of this.readers) {
23608 const meta = reader.getDirectiveMetadata(node);
23609 if (meta !== null) {
23610 return meta;
23611 }
23612 }
23613 return null;
23614 }
23615 getNgModuleMetadata(node) {
23616 for (const reader of this.readers) {
23617 const meta = reader.getNgModuleMetadata(node);
23618 if (meta !== null) {
23619 return meta;
23620 }
23621 }
23622 return null;
23623 }
23624 getPipeMetadata(node) {
23625 for (const reader of this.readers) {
23626 const meta = reader.getPipeMetadata(node);
23627 if (meta !== null) {
23628 return meta;
23629 }
23630 }
23631 return null;
23632 }
23633 }
23634 function afterUnderscore(str) {
23635 const pos = str.indexOf('_');
23636 if (pos === -1) {
23637 throw new Error(`Expected '${str}' to contain '_'`);
23638 }
23639 return str.substr(pos + 1);
23640 }
23641 /** Returns whether a class declaration has the necessary class fields to make it injectable. */
23642 function hasInjectableFields(clazz, host) {
23643 const members = host.getMembersOfClass(clazz);
23644 return members.some(({ isStatic, name }) => isStatic && (name === 'ɵprov' || name === 'ɵfac' || name === 'ɵinj'));
23645 }
23646
23647 /**
23648 * @license
23649 * Copyright Google LLC All Rights Reserved.
23650 *
23651 * Use of this source code is governed by an MIT-style license that can be
23652 * found in the LICENSE file at https://angular.io/license
23653 */
23654 /**
23655 * A `MetadataReader` that can read metadata from `.d.ts` files, which have static Ivy properties
23656 * from an upstream compilation already.
23657 */
23658 class DtsMetadataReader {
23659 constructor(checker, reflector) {
23660 this.checker = checker;
23661 this.reflector = reflector;
23662 }
23663 /**
23664 * Read the metadata from a class that has already been compiled somehow (either it's in a .d.ts
23665 * file, or in a .ts file with a handwritten definition).
23666 *
23667 * @param ref `Reference` to the class of interest, with the context of how it was obtained.
23668 */
23669 getNgModuleMetadata(ref) {
23670 const clazz = ref.node;
23671 const resolutionContext = clazz.getSourceFile().fileName;
23672 // This operation is explicitly not memoized, as it depends on `ref.ownedByModuleGuess`.
23673 // TODO(alxhub): investigate caching of .d.ts module metadata.
23674 const ngModuleDef = this.reflector.getMembersOfClass(clazz).find(member => member.name === 'ɵmod' && member.isStatic);
23675 if (ngModuleDef === undefined) {
23676 return null;
23677 }
23678 else if (
23679 // Validate that the shape of the ngModuleDef type is correct.
23680 ngModuleDef.type === null || !ts$1.isTypeReferenceNode(ngModuleDef.type) ||
23681 ngModuleDef.type.typeArguments === undefined ||
23682 ngModuleDef.type.typeArguments.length !== 4) {
23683 return null;
23684 }
23685 // Read the ModuleData out of the type arguments.
23686 const [_, declarationMetadata, importMetadata, exportMetadata] = ngModuleDef.type.typeArguments;
23687 return {
23688 ref,
23689 declarations: extractReferencesFromType(this.checker, declarationMetadata, ref.ownedByModuleGuess, resolutionContext),
23690 exports: extractReferencesFromType(this.checker, exportMetadata, ref.ownedByModuleGuess, resolutionContext),
23691 imports: extractReferencesFromType(this.checker, importMetadata, ref.ownedByModuleGuess, resolutionContext),
23692 schemas: [],
23693 rawDeclarations: null,
23694 };
23695 }
23696 /**
23697 * Read directive (or component) metadata from a referenced class in a .d.ts file.
23698 */
23699 getDirectiveMetadata(ref) {
23700 const clazz = ref.node;
23701 const def = this.reflector.getMembersOfClass(clazz).find(field => field.isStatic && (field.name === 'ɵcmp' || field.name === 'ɵdir'));
23702 if (def === undefined) {
23703 // No definition could be found.
23704 return null;
23705 }
23706 else if (def.type === null || !ts$1.isTypeReferenceNode(def.type) ||
23707 def.type.typeArguments === undefined || def.type.typeArguments.length < 2) {
23708 // The type metadata was the wrong shape.
23709 return null;
23710 }
23711 const isComponent = def.name === 'ɵcmp';
23712 const ctorParams = this.reflector.getConstructorParameters(clazz);
23713 // A directive is considered to be structural if:
23714 // 1) it's a directive, not a component, and
23715 // 2) it injects `TemplateRef`
23716 const isStructural = !isComponent && ctorParams !== null && ctorParams.some(param => {
23717 return param.typeValueReference.kind === 1 /* IMPORTED */ &&
23718 param.typeValueReference.moduleName === '@angular/core' &&
23719 param.typeValueReference.importedName === 'TemplateRef';
23720 });
23721 const inputs = ClassPropertyMapping.fromMappedObject(readStringMapType(def.type.typeArguments[3]));
23722 const outputs = ClassPropertyMapping.fromMappedObject(readStringMapType(def.type.typeArguments[4]));
23723 return Object.assign(Object.assign({ ref, name: clazz.name.text, isComponent, selector: readStringType(def.type.typeArguments[1]), exportAs: readStringArrayType(def.type.typeArguments[2]), inputs,
23724 outputs, queries: readStringArrayType(def.type.typeArguments[5]) }, extractDirectiveTypeCheckMeta(clazz, inputs, this.reflector)), { baseClass: readBaseClass(clazz, this.checker, this.reflector), isPoisoned: false, isStructural });
23725 }
23726 /**
23727 * Read pipe metadata from a referenced class in a .d.ts file.
23728 */
23729 getPipeMetadata(ref) {
23730 const def = this.reflector.getMembersOfClass(ref.node).find(field => field.isStatic && field.name === 'ɵpipe');
23731 if (def === undefined) {
23732 // No definition could be found.
23733 return null;
23734 }
23735 else if (def.type === null || !ts$1.isTypeReferenceNode(def.type) ||
23736 def.type.typeArguments === undefined || def.type.typeArguments.length < 2) {
23737 // The type metadata was the wrong shape.
23738 return null;
23739 }
23740 const type = def.type.typeArguments[1];
23741 if (!ts$1.isLiteralTypeNode(type) || !ts$1.isStringLiteral(type.literal)) {
23742 // The type metadata was the wrong type.
23743 return null;
23744 }
23745 const name = type.literal.text;
23746 return { ref, name };
23747 }
23748 }
23749 function readBaseClass(clazz, checker, reflector) {
23750 if (!isNamedClassDeclaration(clazz)) {
23751 // Technically this is an error in a .d.ts file, but for the purposes of finding the base class
23752 // it's ignored.
23753 return reflector.hasBaseClass(clazz) ? 'dynamic' : null;
23754 }
23755 if (clazz.heritageClauses !== undefined) {
23756 for (const clause of clazz.heritageClauses) {
23757 if (clause.token === ts$1.SyntaxKind.ExtendsKeyword) {
23758 const baseExpr = clause.types[0].expression;
23759 let symbol = checker.getSymbolAtLocation(baseExpr);
23760 if (symbol === undefined) {
23761 return 'dynamic';
23762 }
23763 else if (symbol.flags & ts$1.SymbolFlags.Alias) {
23764 symbol = checker.getAliasedSymbol(symbol);
23765 }
23766 if (symbol.valueDeclaration !== undefined &&
23767 isNamedClassDeclaration(symbol.valueDeclaration)) {
23768 return new Reference$1(symbol.valueDeclaration);
23769 }
23770 else {
23771 return 'dynamic';
23772 }
23773 }
23774 }
23775 }
23776 return null;
23777 }
23778
23779 /**
23780 * @license
23781 * Copyright Google LLC All Rights Reserved.
23782 *
23783 * Use of this source code is governed by an MIT-style license that can be
23784 * found in the LICENSE file at https://angular.io/license
23785 */
23786 /**
23787 * Given a reference to a directive, return a flattened version of its `DirectiveMeta` metadata
23788 * which includes metadata from its entire inheritance chain.
23789 *
23790 * The returned `DirectiveMeta` will either have `baseClass: null` if the inheritance chain could be
23791 * fully resolved, or `baseClass: 'dynamic'` if the inheritance chain could not be completely
23792 * followed.
23793 */
23794 function flattenInheritedDirectiveMetadata(reader, dir) {
23795 const topMeta = reader.getDirectiveMetadata(dir);
23796 if (topMeta === null) {
23797 throw new Error(`Metadata not found for directive: ${dir.debugName}`);
23798 }
23799 if (topMeta.baseClass === null) {
23800 return topMeta;
23801 }
23802 const coercedInputFields = new Set();
23803 const undeclaredInputFields = new Set();
23804 const restrictedInputFields = new Set();
23805 const stringLiteralInputFields = new Set();
23806 let isDynamic = false;
23807 let inputs = ClassPropertyMapping.empty();
23808 let outputs = ClassPropertyMapping.empty();
23809 let isStructural = false;
23810 const addMetadata = (meta) => {
23811 if (meta.baseClass === 'dynamic') {
23812 isDynamic = true;
23813 }
23814 else if (meta.baseClass !== null) {
23815 const baseMeta = reader.getDirectiveMetadata(meta.baseClass);
23816 if (baseMeta !== null) {
23817 addMetadata(baseMeta);
23818 }
23819 else {
23820 // Missing metadata for the base class means it's effectively dynamic.
23821 isDynamic = true;
23822 }
23823 }
23824 isStructural = isStructural || meta.isStructural;
23825 inputs = ClassPropertyMapping.merge(inputs, meta.inputs);
23826 outputs = ClassPropertyMapping.merge(outputs, meta.outputs);
23827 for (const coercedInputField of meta.coercedInputFields) {
23828 coercedInputFields.add(coercedInputField);
23829 }
23830 for (const undeclaredInputField of meta.undeclaredInputFields) {
23831 undeclaredInputFields.add(undeclaredInputField);
23832 }
23833 for (const restrictedInputField of meta.restrictedInputFields) {
23834 restrictedInputFields.add(restrictedInputField);
23835 }
23836 for (const field of meta.stringLiteralInputFields) {
23837 stringLiteralInputFields.add(field);
23838 }
23839 };
23840 addMetadata(topMeta);
23841 return Object.assign(Object.assign({}, topMeta), { inputs,
23842 outputs,
23843 coercedInputFields,
23844 undeclaredInputFields,
23845 restrictedInputFields,
23846 stringLiteralInputFields, baseClass: isDynamic ? 'dynamic' : null, isStructural });
23847 }
23848
23849 /**
23850 * @license
23851 * Copyright Google LLC All Rights Reserved.
23852 *
23853 * Use of this source code is governed by an MIT-style license that can be
23854 * found in the LICENSE file at https://angular.io/license
23855 */
23856 /**
23857 * A registry of directive, pipe, and module metadata for types defined in the current compilation
23858 * unit, which supports both reading and registering.
23859 */
23860 class LocalMetadataRegistry {
23861 constructor() {
23862 this.directives = new Map();
23863 this.ngModules = new Map();
23864 this.pipes = new Map();
23865 }
23866 getDirectiveMetadata(ref) {
23867 return this.directives.has(ref.node) ? this.directives.get(ref.node) : null;
23868 }
23869 getNgModuleMetadata(ref) {
23870 return this.ngModules.has(ref.node) ? this.ngModules.get(ref.node) : null;
23871 }
23872 getPipeMetadata(ref) {
23873 return this.pipes.has(ref.node) ? this.pipes.get(ref.node) : null;
23874 }
23875 registerDirectiveMetadata(meta) {
23876 this.directives.set(meta.ref.node, meta);
23877 }
23878 registerNgModuleMetadata(meta) {
23879 this.ngModules.set(meta.ref.node, meta);
23880 }
23881 registerPipeMetadata(meta) {
23882 this.pipes.set(meta.ref.node, meta);
23883 }
23884 }
23885 /**
23886 * A `MetadataRegistry` which registers metdata with multiple delegate `MetadataRegistry` instances.
23887 */
23888 class CompoundMetadataRegistry {
23889 constructor(registries) {
23890 this.registries = registries;
23891 }
23892 registerDirectiveMetadata(meta) {
23893 for (const registry of this.registries) {
23894 registry.registerDirectiveMetadata(meta);
23895 }
23896 }
23897 registerNgModuleMetadata(meta) {
23898 for (const registry of this.registries) {
23899 registry.registerNgModuleMetadata(meta);
23900 }
23901 }
23902 registerPipeMetadata(meta) {
23903 for (const registry of this.registries) {
23904 registry.registerPipeMetadata(meta);
23905 }
23906 }
23907 }
23908 /**
23909 * Registry that keeps track of classes that can be constructed via dependency injection (e.g.
23910 * injectables, directives, pipes).
23911 */
23912 class InjectableClassRegistry {
23913 constructor(host) {
23914 this.host = host;
23915 this.classes = new Set();
23916 }
23917 registerInjectable(declaration) {
23918 this.classes.add(declaration);
23919 }
23920 isInjectable(declaration) {
23921 // Figure out whether the class is injectable based on the registered classes, otherwise
23922 // fall back to looking at its members since we might not have been able register the class
23923 // if it was compiled already.
23924 return this.classes.has(declaration) || hasInjectableFields(declaration, this.host);
23925 }
23926 }
23927
23928 /**
23929 * @license
23930 * Copyright Google LLC All Rights Reserved.
23931 *
23932 * Use of this source code is governed by an MIT-style license that can be
23933 * found in the LICENSE file at https://angular.io/license
23934 */
23935 function isExternalResource(resource) {
23936 return resource.path !== null;
23937 }
23938 /**
23939 * Tracks the mapping between external template/style files and the component(s) which use them.
23940 *
23941 * This information is produced during analysis of the program and is used mainly to support
23942 * external tooling, for which such a mapping is challenging to determine without compiler
23943 * assistance.
23944 */
23945 class ResourceRegistry {
23946 constructor() {
23947 this.externalTemplateToComponentsMap = new Map();
23948 this.componentToTemplateMap = new Map();
23949 this.componentToStylesMap = new Map();
23950 this.externalStyleToComponentsMap = new Map();
23951 }
23952 getComponentsWithTemplate(template) {
23953 if (!this.externalTemplateToComponentsMap.has(template)) {
23954 return new Set();
23955 }
23956 return this.externalTemplateToComponentsMap.get(template);
23957 }
23958 registerResources(resources, component) {
23959 if (resources.template !== null) {
23960 this.registerTemplate(resources.template, component);
23961 }
23962 for (const style of resources.styles) {
23963 this.registerStyle(style, component);
23964 }
23965 }
23966 registerTemplate(templateResource, component) {
23967 const { path } = templateResource;
23968 if (path !== null) {
23969 if (!this.externalTemplateToComponentsMap.has(path)) {
23970 this.externalTemplateToComponentsMap.set(path, new Set());
23971 }
23972 this.externalTemplateToComponentsMap.get(path).add(component);
23973 }
23974 this.componentToTemplateMap.set(component, templateResource);
23975 }
23976 getTemplate(component) {
23977 if (!this.componentToTemplateMap.has(component)) {
23978 return null;
23979 }
23980 return this.componentToTemplateMap.get(component);
23981 }
23982 registerStyle(styleResource, component) {
23983 const { path } = styleResource;
23984 if (!this.componentToStylesMap.has(component)) {
23985 this.componentToStylesMap.set(component, new Set());
23986 }
23987 if (path !== null) {
23988 if (!this.externalStyleToComponentsMap.has(path)) {
23989 this.externalStyleToComponentsMap.set(path, new Set());
23990 }
23991 this.externalStyleToComponentsMap.get(path).add(component);
23992 }
23993 this.componentToStylesMap.get(component).add(styleResource);
23994 }
23995 getStyles(component) {
23996 if (!this.componentToStylesMap.has(component)) {
23997 return new Set();
23998 }
23999 return this.componentToStylesMap.get(component);
24000 }
24001 getComponentsWithStyle(styleUrl) {
24002 if (!this.externalStyleToComponentsMap.has(styleUrl)) {
24003 return new Set();
24004 }
24005 return this.externalStyleToComponentsMap.get(styleUrl);
24006 }
24007 }
24008
24009 /**
24010 * @license
24011 * Copyright Google LLC All Rights Reserved.
24012 *
24013 * Use of this source code is governed by an MIT-style license that can be
24014 * found in the LICENSE file at https://angular.io/license
24015 */
24016 /**
24017 * Represents a value which cannot be determined statically.
24018 */
24019 class DynamicValue {
24020 constructor(node, reason, code) {
24021 this.node = node;
24022 this.reason = reason;
24023 this.code = code;
24024 }
24025 static fromDynamicInput(node, input) {
24026 return new DynamicValue(node, input, 0 /* DYNAMIC_INPUT */);
24027 }
24028 static fromDynamicString(node) {
24029 return new DynamicValue(node, undefined, 1 /* DYNAMIC_STRING */);
24030 }
24031 static fromExternalReference(node, ref) {
24032 return new DynamicValue(node, ref, 2 /* EXTERNAL_REFERENCE */);
24033 }
24034 static fromUnsupportedSyntax(node) {
24035 return new DynamicValue(node, undefined, 3 /* UNSUPPORTED_SYNTAX */);
24036 }
24037 static fromUnknownIdentifier(node) {
24038 return new DynamicValue(node, undefined, 4 /* UNKNOWN_IDENTIFIER */);
24039 }
24040 static fromInvalidExpressionType(node, value) {
24041 return new DynamicValue(node, value, 5 /* INVALID_EXPRESSION_TYPE */);
24042 }
24043 static fromComplexFunctionCall(node, fn) {
24044 return new DynamicValue(node, fn, 6 /* COMPLEX_FUNCTION_CALL */);
24045 }
24046 static fromUnknown(node) {
24047 return new DynamicValue(node, undefined, 7 /* UNKNOWN */);
24048 }
24049 isFromDynamicInput() {
24050 return this.code === 0 /* DYNAMIC_INPUT */;
24051 }
24052 isFromDynamicString() {
24053 return this.code === 1 /* DYNAMIC_STRING */;
24054 }
24055 isFromExternalReference() {
24056 return this.code === 2 /* EXTERNAL_REFERENCE */;
24057 }
24058 isFromUnsupportedSyntax() {
24059 return this.code === 3 /* UNSUPPORTED_SYNTAX */;
24060 }
24061 isFromUnknownIdentifier() {
24062 return this.code === 4 /* UNKNOWN_IDENTIFIER */;
24063 }
24064 isFromInvalidExpressionType() {
24065 return this.code === 5 /* INVALID_EXPRESSION_TYPE */;
24066 }
24067 isFromComplexFunctionCall() {
24068 return this.code === 6 /* COMPLEX_FUNCTION_CALL */;
24069 }
24070 isFromUnknown() {
24071 return this.code === 7 /* UNKNOWN */;
24072 }
24073 accept(visitor) {
24074 switch (this.code) {
24075 case 0 /* DYNAMIC_INPUT */:
24076 return visitor.visitDynamicInput(this);
24077 case 1 /* DYNAMIC_STRING */:
24078 return visitor.visitDynamicString(this);
24079 case 2 /* EXTERNAL_REFERENCE */:
24080 return visitor.visitExternalReference(this);
24081 case 3 /* UNSUPPORTED_SYNTAX */:
24082 return visitor.visitUnsupportedSyntax(this);
24083 case 4 /* UNKNOWN_IDENTIFIER */:
24084 return visitor.visitUnknownIdentifier(this);
24085 case 5 /* INVALID_EXPRESSION_TYPE */:
24086 return visitor.visitInvalidExpressionType(this);
24087 case 6 /* COMPLEX_FUNCTION_CALL */:
24088 return visitor.visitComplexFunctionCall(this);
24089 case 7 /* UNKNOWN */:
24090 return visitor.visitUnknown(this);
24091 }
24092 }
24093 }
24094
24095 /**
24096 * @license
24097 * Copyright Google LLC All Rights Reserved.
24098 *
24099 * Use of this source code is governed by an MIT-style license that can be
24100 * found in the LICENSE file at https://angular.io/license
24101 */
24102 /**
24103 * A collection of publicly exported declarations from a module. Each declaration is evaluated
24104 * lazily upon request.
24105 */
24106 class ResolvedModule {
24107 constructor(exports, evaluate) {
24108 this.exports = exports;
24109 this.evaluate = evaluate;
24110 }
24111 getExport(name) {
24112 if (!this.exports.has(name)) {
24113 return undefined;
24114 }
24115 return this.evaluate(this.exports.get(name));
24116 }
24117 getExports() {
24118 const map = new Map();
24119 this.exports.forEach((decl, name) => {
24120 map.set(name, this.evaluate(decl));
24121 });
24122 return map;
24123 }
24124 }
24125 /**
24126 * A value member of an enumeration.
24127 *
24128 * Contains a `Reference` to the enumeration itself, and the name of the referenced member.
24129 */
24130 class EnumValue {
24131 constructor(enumRef, name, resolved) {
24132 this.enumRef = enumRef;
24133 this.name = name;
24134 this.resolved = resolved;
24135 }
24136 }
24137 /**
24138 * An implementation of a known function that can be statically evaluated.
24139 * It could be a built-in function or method (such as `Array.prototype.slice`) or a TypeScript
24140 * helper (such as `__spread`).
24141 */
24142 class KnownFn {
24143 }
24144
24145 /**
24146 * @license
24147 * Copyright Google LLC All Rights Reserved.
24148 *
24149 * Use of this source code is governed by an MIT-style license that can be
24150 * found in the LICENSE file at https://angular.io/license
24151 */
24152 /**
24153 * Derives a type representation from a resolved value to be reported in a diagnostic.
24154 *
24155 * @param value The resolved value for which a type representation should be derived.
24156 * @param maxDepth The maximum nesting depth of objects and arrays, defaults to 1 level.
24157 */
24158 function describeResolvedType(value, maxDepth = 1) {
24159 var _a, _b;
24160 if (value === null) {
24161 return 'null';
24162 }
24163 else if (value === undefined) {
24164 return 'undefined';
24165 }
24166 else if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'string') {
24167 return typeof value;
24168 }
24169 else if (value instanceof Map) {
24170 if (maxDepth === 0) {
24171 return 'object';
24172 }
24173 const entries = Array.from(value.entries()).map(([key, v]) => {
24174 return `${quoteKey(key)}: ${describeResolvedType(v, maxDepth - 1)}`;
24175 });
24176 return entries.length > 0 ? `{ ${entries.join('; ')} }` : '{}';
24177 }
24178 else if (value instanceof ResolvedModule) {
24179 return '(module)';
24180 }
24181 else if (value instanceof EnumValue) {
24182 return (_a = value.enumRef.debugName) !== null && _a !== void 0 ? _a : '(anonymous)';
24183 }
24184 else if (value instanceof Reference$1) {
24185 return (_b = value.debugName) !== null && _b !== void 0 ? _b : '(anonymous)';
24186 }
24187 else if (Array.isArray(value)) {
24188 if (maxDepth === 0) {
24189 return 'Array';
24190 }
24191 return `[${value.map(v => describeResolvedType(v, maxDepth - 1)).join(', ')}]`;
24192 }
24193 else if (value instanceof DynamicValue) {
24194 return '(not statically analyzable)';
24195 }
24196 else if (value instanceof KnownFn) {
24197 return 'Function';
24198 }
24199 else {
24200 return 'unknown';
24201 }
24202 }
24203 function quoteKey(key) {
24204 if (/^[a-z0-9_]+$/i.test(key)) {
24205 return key;
24206 }
24207 else {
24208 return `'${key.replace(/'/g, '\\\'')}'`;
24209 }
24210 }
24211 /**
24212 * Creates an array of related information diagnostics for a `DynamicValue` that describe the trace
24213 * of why an expression was evaluated as dynamic.
24214 *
24215 * @param node The node for which a `ts.Diagnostic` is to be created with the trace.
24216 * @param value The dynamic value for which a trace should be created.
24217 */
24218 function traceDynamicValue(node, value) {
24219 return value.accept(new TraceDynamicValueVisitor(node));
24220 }
24221 class TraceDynamicValueVisitor {
24222 constructor(node) {
24223 this.node = node;
24224 this.currentContainerNode = null;
24225 }
24226 visitDynamicInput(value) {
24227 const trace = value.reason.accept(this);
24228 if (this.shouldTrace(value.node)) {
24229 const info = makeRelatedInformation(value.node, 'Unable to evaluate this expression statically.');
24230 trace.unshift(info);
24231 }
24232 return trace;
24233 }
24234 visitDynamicString(value) {
24235 return [makeRelatedInformation(value.node, 'A string value could not be determined statically.')];
24236 }
24237 visitExternalReference(value) {
24238 const name = value.reason.debugName;
24239 const description = name !== null ? `'${name}'` : 'an anonymous declaration';
24240 return [makeRelatedInformation(value.node, `A value for ${description} cannot be determined statically, as it is an external declaration.`)];
24241 }
24242 visitComplexFunctionCall(value) {
24243 return [
24244 makeRelatedInformation(value.node, 'Unable to evaluate function call of complex function. A function must have exactly one return statement.'),
24245 makeRelatedInformation(value.reason.node, 'Function is declared here.')
24246 ];
24247 }
24248 visitInvalidExpressionType(value) {
24249 return [makeRelatedInformation(value.node, 'Unable to evaluate an invalid expression.')];
24250 }
24251 visitUnknown(value) {
24252 return [makeRelatedInformation(value.node, 'Unable to evaluate statically.')];
24253 }
24254 visitUnknownIdentifier(value) {
24255 return [makeRelatedInformation(value.node, 'Unknown reference.')];
24256 }
24257 visitUnsupportedSyntax(value) {
24258 return [makeRelatedInformation(value.node, 'This syntax is not supported.')];
24259 }
24260 /**
24261 * Determines whether the dynamic value reported for the node should be traced, i.e. if it is not
24262 * part of the container for which the most recent trace was created.
24263 */
24264 shouldTrace(node) {
24265 if (node === this.node) {
24266 // Do not include a dynamic value for the origin node, as the main diagnostic is already
24267 // reported on that node.
24268 return false;
24269 }
24270 const container = getContainerNode(node);
24271 if (container === this.currentContainerNode) {
24272 // The node is part of the same container as the previous trace entry, so this dynamic value
24273 // should not become part of the trace.
24274 return false;
24275 }
24276 this.currentContainerNode = container;
24277 return true;
24278 }
24279 }
24280 /**
24281 * Determines the closest parent node that is to be considered as container, which is used to reduce
24282 * the granularity of tracing the dynamic values to a single entry per container. Currently, full
24283 * statements and destructuring patterns are considered as container.
24284 */
24285 function getContainerNode(node) {
24286 let currentNode = node;
24287 while (currentNode !== undefined) {
24288 switch (currentNode.kind) {
24289 case ts$1.SyntaxKind.ExpressionStatement:
24290 case ts$1.SyntaxKind.VariableStatement:
24291 case ts$1.SyntaxKind.ReturnStatement:
24292 case ts$1.SyntaxKind.IfStatement:
24293 case ts$1.SyntaxKind.SwitchStatement:
24294 case ts$1.SyntaxKind.DoStatement:
24295 case ts$1.SyntaxKind.WhileStatement:
24296 case ts$1.SyntaxKind.ForStatement:
24297 case ts$1.SyntaxKind.ForInStatement:
24298 case ts$1.SyntaxKind.ForOfStatement:
24299 case ts$1.SyntaxKind.ContinueStatement:
24300 case ts$1.SyntaxKind.BreakStatement:
24301 case ts$1.SyntaxKind.ThrowStatement:
24302 case ts$1.SyntaxKind.ObjectBindingPattern:
24303 case ts$1.SyntaxKind.ArrayBindingPattern:
24304 return currentNode;
24305 }
24306 currentNode = currentNode.parent;
24307 }
24308 return node.getSourceFile();
24309 }
24310
24311 /**
24312 * @license
24313 * Copyright Google LLC All Rights Reserved.
24314 *
24315 * Use of this source code is governed by an MIT-style license that can be
24316 * found in the LICENSE file at https://angular.io/license
24317 */
24318 class ArraySliceBuiltinFn extends KnownFn {
24319 constructor(lhs) {
24320 super();
24321 this.lhs = lhs;
24322 }
24323 evaluate(node, args) {
24324 if (args.length === 0) {
24325 return this.lhs;
24326 }
24327 else {
24328 return DynamicValue.fromUnknown(node);
24329 }
24330 }
24331 }
24332 class ArrayConcatBuiltinFn extends KnownFn {
24333 constructor(lhs) {
24334 super();
24335 this.lhs = lhs;
24336 }
24337 evaluate(node, args) {
24338 const result = [...this.lhs];
24339 for (const arg of args) {
24340 if (arg instanceof DynamicValue) {
24341 result.push(DynamicValue.fromDynamicInput(node, arg));
24342 }
24343 else if (Array.isArray(arg)) {
24344 result.push(...arg);
24345 }
24346 else {
24347 result.push(arg);
24348 }
24349 }
24350 return result;
24351 }
24352 }
24353 class ObjectAssignBuiltinFn extends KnownFn {
24354 evaluate(node, args) {
24355 if (args.length === 0) {
24356 return DynamicValue.fromUnsupportedSyntax(node);
24357 }
24358 for (const arg of args) {
24359 if (arg instanceof DynamicValue) {
24360 return DynamicValue.fromDynamicInput(node, arg);
24361 }
24362 else if (!(arg instanceof Map)) {
24363 return DynamicValue.fromUnsupportedSyntax(node);
24364 }
24365 }
24366 const [target, ...sources] = args;
24367 for (const source of sources) {
24368 source.forEach((value, key) => target.set(key, value));
24369 }
24370 return target;
24371 }
24372 }
24373
24374 /**
24375 * @license
24376 * Copyright Google LLC All Rights Reserved.
24377 *
24378 * Use of this source code is governed by an MIT-style license that can be
24379 * found in the LICENSE file at https://angular.io/license
24380 */
24381 // Use the same implementation we use for `Object.assign()`. Semantically these functions are the
24382 // same, so they can also share the same evaluation code.
24383 class AssignHelperFn extends ObjectAssignBuiltinFn {
24384 }
24385 // Used for both `__spread()` and `__spreadArrays()` TypeScript helper functions.
24386 class SpreadHelperFn extends KnownFn {
24387 evaluate(node, args) {
24388 const result = [];
24389 for (const arg of args) {
24390 if (arg instanceof DynamicValue) {
24391 result.push(DynamicValue.fromDynamicInput(node, arg));
24392 }
24393 else if (Array.isArray(arg)) {
24394 result.push(...arg);
24395 }
24396 else {
24397 result.push(arg);
24398 }
24399 }
24400 return result;
24401 }
24402 }
24403
24404 /**
24405 * @license
24406 * Copyright Google LLC All Rights Reserved.
24407 *
24408 * Use of this source code is governed by an MIT-style license that can be
24409 * found in the LICENSE file at https://angular.io/license
24410 */
24411 /** Resolved value for the JavaScript global `Object` declaration. */
24412 const jsGlobalObjectValue = new Map([['assign', new ObjectAssignBuiltinFn()]]);
24413 /** Resolved value for the `__assign()` TypeScript helper declaration. */
24414 const assignTsHelperFn = new AssignHelperFn();
24415 /** Resolved value for the `__spread()` and `__spreadArrays()` TypeScript helper declarations. */
24416 const spreadTsHelperFn = new SpreadHelperFn();
24417 /**
24418 * Resolves the specified known declaration to a resolved value. For example,
24419 * the known JavaScript global `Object` will resolve to a `Map` that provides the
24420 * `assign` method with a built-in function. This enables evaluation of `Object.assign`.
24421 */
24422 function resolveKnownDeclaration(decl) {
24423 switch (decl) {
24424 case KnownDeclaration.JsGlobalObject:
24425 return jsGlobalObjectValue;
24426 case KnownDeclaration.TsHelperAssign:
24427 return assignTsHelperFn;
24428 case KnownDeclaration.TsHelperSpread:
24429 case KnownDeclaration.TsHelperSpreadArrays:
24430 return spreadTsHelperFn;
24431 default:
24432 throw new Error(`Cannot resolve known declaration. Received: ${KnownDeclaration[decl]}.`);
24433 }
24434 }
24435
24436 /**
24437 * @license
24438 * Copyright Google LLC All Rights Reserved.
24439 *
24440 * Use of this source code is governed by an MIT-style license that can be
24441 * found in the LICENSE file at https://angular.io/license
24442 */
24443 function literalBinaryOp(op) {
24444 return { op, literal: true };
24445 }
24446 function referenceBinaryOp(op) {
24447 return { op, literal: false };
24448 }
24449 const BINARY_OPERATORS = new Map([
24450 [ts$1.SyntaxKind.PlusToken, literalBinaryOp((a, b) => a + b)],
24451 [ts$1.SyntaxKind.MinusToken, literalBinaryOp((a, b) => a - b)],
24452 [ts$1.SyntaxKind.AsteriskToken, literalBinaryOp((a, b) => a * b)],
24453 [ts$1.SyntaxKind.SlashToken, literalBinaryOp((a, b) => a / b)],
24454 [ts$1.SyntaxKind.PercentToken, literalBinaryOp((a, b) => a % b)],
24455 [ts$1.SyntaxKind.AmpersandToken, literalBinaryOp((a, b) => a & b)],
24456 [ts$1.SyntaxKind.BarToken, literalBinaryOp((a, b) => a | b)],
24457 [ts$1.SyntaxKind.CaretToken, literalBinaryOp((a, b) => a ^ b)],
24458 [ts$1.SyntaxKind.LessThanToken, literalBinaryOp((a, b) => a < b)],
24459 [ts$1.SyntaxKind.LessThanEqualsToken, literalBinaryOp((a, b) => a <= b)],
24460 [ts$1.SyntaxKind.GreaterThanToken, literalBinaryOp((a, b) => a > b)],
24461 [ts$1.SyntaxKind.GreaterThanEqualsToken, literalBinaryOp((a, b) => a >= b)],
24462 [ts$1.SyntaxKind.EqualsEqualsToken, literalBinaryOp((a, b) => a == b)],
24463 [ts$1.SyntaxKind.EqualsEqualsEqualsToken, literalBinaryOp((a, b) => a === b)],
24464 [ts$1.SyntaxKind.ExclamationEqualsToken, literalBinaryOp((a, b) => a != b)],
24465 [ts$1.SyntaxKind.ExclamationEqualsEqualsToken, literalBinaryOp((a, b) => a !== b)],
24466 [ts$1.SyntaxKind.LessThanLessThanToken, literalBinaryOp((a, b) => a << b)],
24467 [ts$1.SyntaxKind.GreaterThanGreaterThanToken, literalBinaryOp((a, b) => a >> b)],
24468 [ts$1.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, literalBinaryOp((a, b) => a >>> b)],
24469 [ts$1.SyntaxKind.AsteriskAsteriskToken, literalBinaryOp((a, b) => Math.pow(a, b))],
24470 [ts$1.SyntaxKind.AmpersandAmpersandToken, referenceBinaryOp((a, b) => a && b)],
24471 [ts$1.SyntaxKind.BarBarToken, referenceBinaryOp((a, b) => a || b)]
24472 ]);
24473 const UNARY_OPERATORS = new Map([
24474 [ts$1.SyntaxKind.TildeToken, a => ~a], [ts$1.SyntaxKind.MinusToken, a => -a],
24475 [ts$1.SyntaxKind.PlusToken, a => +a], [ts$1.SyntaxKind.ExclamationToken, a => !a]
24476 ]);
24477 class StaticInterpreter {
24478 constructor(host, checker, dependencyTracker) {
24479 this.host = host;
24480 this.checker = checker;
24481 this.dependencyTracker = dependencyTracker;
24482 }
24483 visit(node, context) {
24484 return this.visitExpression(node, context);
24485 }
24486 visitExpression(node, context) {
24487 let result;
24488 if (node.kind === ts$1.SyntaxKind.TrueKeyword) {
24489 return true;
24490 }
24491 else if (node.kind === ts$1.SyntaxKind.FalseKeyword) {
24492 return false;
24493 }
24494 else if (node.kind === ts$1.SyntaxKind.NullKeyword) {
24495 return null;
24496 }
24497 else if (ts$1.isStringLiteral(node)) {
24498 return node.text;
24499 }
24500 else if (ts$1.isNoSubstitutionTemplateLiteral(node)) {
24501 return node.text;
24502 }
24503 else if (ts$1.isTemplateExpression(node)) {
24504 result = this.visitTemplateExpression(node, context);
24505 }
24506 else if (ts$1.isNumericLiteral(node)) {
24507 return parseFloat(node.text);
24508 }
24509 else if (ts$1.isObjectLiteralExpression(node)) {
24510 result = this.visitObjectLiteralExpression(node, context);
24511 }
24512 else if (ts$1.isIdentifier(node)) {
24513 result = this.visitIdentifier(node, context);
24514 }
24515 else if (ts$1.isPropertyAccessExpression(node)) {
24516 result = this.visitPropertyAccessExpression(node, context);
24517 }
24518 else if (ts$1.isCallExpression(node)) {
24519 result = this.visitCallExpression(node, context);
24520 }
24521 else if (ts$1.isConditionalExpression(node)) {
24522 result = this.visitConditionalExpression(node, context);
24523 }
24524 else if (ts$1.isPrefixUnaryExpression(node)) {
24525 result = this.visitPrefixUnaryExpression(node, context);
24526 }
24527 else if (ts$1.isBinaryExpression(node)) {
24528 result = this.visitBinaryExpression(node, context);
24529 }
24530 else if (ts$1.isArrayLiteralExpression(node)) {
24531 result = this.visitArrayLiteralExpression(node, context);
24532 }
24533 else if (ts$1.isParenthesizedExpression(node)) {
24534 result = this.visitParenthesizedExpression(node, context);
24535 }
24536 else if (ts$1.isElementAccessExpression(node)) {
24537 result = this.visitElementAccessExpression(node, context);
24538 }
24539 else if (ts$1.isAsExpression(node)) {
24540 result = this.visitExpression(node.expression, context);
24541 }
24542 else if (ts$1.isNonNullExpression(node)) {
24543 result = this.visitExpression(node.expression, context);
24544 }
24545 else if (this.host.isClass(node)) {
24546 result = this.visitDeclaration(node, context);
24547 }
24548 else {
24549 return DynamicValue.fromUnsupportedSyntax(node);
24550 }
24551 if (result instanceof DynamicValue && result.node !== node) {
24552 return DynamicValue.fromDynamicInput(node, result);
24553 }
24554 return result;
24555 }
24556 visitArrayLiteralExpression(node, context) {
24557 const array = [];
24558 for (let i = 0; i < node.elements.length; i++) {
24559 const element = node.elements[i];
24560 if (ts$1.isSpreadElement(element)) {
24561 array.push(...this.visitSpreadElement(element, context));
24562 }
24563 else {
24564 array.push(this.visitExpression(element, context));
24565 }
24566 }
24567 return array;
24568 }
24569 visitObjectLiteralExpression(node, context) {
24570 const map = new Map();
24571 for (let i = 0; i < node.properties.length; i++) {
24572 const property = node.properties[i];
24573 if (ts$1.isPropertyAssignment(property)) {
24574 const name = this.stringNameFromPropertyName(property.name, context);
24575 // Check whether the name can be determined statically.
24576 if (name === undefined) {
24577 return DynamicValue.fromDynamicInput(node, DynamicValue.fromDynamicString(property.name));
24578 }
24579 map.set(name, this.visitExpression(property.initializer, context));
24580 }
24581 else if (ts$1.isShorthandPropertyAssignment(property)) {
24582 const symbol = this.checker.getShorthandAssignmentValueSymbol(property);
24583 if (symbol === undefined || symbol.valueDeclaration === undefined) {
24584 map.set(property.name.text, DynamicValue.fromUnknown(property));
24585 }
24586 else {
24587 map.set(property.name.text, this.visitDeclaration(symbol.valueDeclaration, context));
24588 }
24589 }
24590 else if (ts$1.isSpreadAssignment(property)) {
24591 const spread = this.visitExpression(property.expression, context);
24592 if (spread instanceof DynamicValue) {
24593 return DynamicValue.fromDynamicInput(node, spread);
24594 }
24595 else if (spread instanceof Map) {
24596 spread.forEach((value, key) => map.set(key, value));
24597 }
24598 else if (spread instanceof ResolvedModule) {
24599 spread.getExports().forEach((value, key) => map.set(key, value));
24600 }
24601 else {
24602 return DynamicValue.fromDynamicInput(node, DynamicValue.fromInvalidExpressionType(property, spread));
24603 }
24604 }
24605 else {
24606 return DynamicValue.fromUnknown(node);
24607 }
24608 }
24609 return map;
24610 }
24611 visitTemplateExpression(node, context) {
24612 const pieces = [node.head.text];
24613 for (let i = 0; i < node.templateSpans.length; i++) {
24614 const span = node.templateSpans[i];
24615 const value = literal$1(this.visit(span.expression, context), () => DynamicValue.fromDynamicString(span.expression));
24616 if (value instanceof DynamicValue) {
24617 return DynamicValue.fromDynamicInput(node, value);
24618 }
24619 pieces.push(`${value}`, span.literal.text);
24620 }
24621 return pieces.join('');
24622 }
24623 visitIdentifier(node, context) {
24624 const decl = this.host.getDeclarationOfIdentifier(node);
24625 if (decl === null) {
24626 if (node.originalKeywordKind === ts$1.SyntaxKind.UndefinedKeyword) {
24627 return undefined;
24628 }
24629 else {
24630 // Check if the symbol here is imported.
24631 if (this.dependencyTracker !== null && this.host.getImportOfIdentifier(node) !== null) {
24632 // It was, but no declaration for the node could be found. This means that the dependency
24633 // graph for the current file cannot be properly updated to account for this (broken)
24634 // import. Instead, the originating file is reported as failing dependency analysis,
24635 // ensuring that future compilations will always attempt to re-resolve the previously
24636 // broken identifier.
24637 this.dependencyTracker.recordDependencyAnalysisFailure(context.originatingFile);
24638 }
24639 return DynamicValue.fromUnknownIdentifier(node);
24640 }
24641 }
24642 if (decl.known !== null) {
24643 return resolveKnownDeclaration(decl.known);
24644 }
24645 else if (isConcreteDeclaration(decl) && decl.identity !== null &&
24646 decl.identity.kind === 0 /* DownleveledEnum */) {
24647 return this.getResolvedEnum(decl.node, decl.identity.enumMembers, context);
24648 }
24649 const declContext = Object.assign(Object.assign({}, context), joinModuleContext(context, node, decl));
24650 const result = this.visitAmbiguousDeclaration(decl, declContext);
24651 if (result instanceof Reference$1) {
24652 // Only record identifiers to non-synthetic references. Synthetic references may not have the
24653 // same value at runtime as they do at compile time, so it's not legal to refer to them by the
24654 // identifier here.
24655 if (!result.synthetic) {
24656 result.addIdentifier(node);
24657 }
24658 }
24659 else if (result instanceof DynamicValue) {
24660 return DynamicValue.fromDynamicInput(node, result);
24661 }
24662 return result;
24663 }
24664 visitDeclaration(node, context) {
24665 if (this.dependencyTracker !== null) {
24666 this.dependencyTracker.addDependency(context.originatingFile, node.getSourceFile());
24667 }
24668 if (this.host.isClass(node)) {
24669 return this.getReference(node, context);
24670 }
24671 else if (ts$1.isVariableDeclaration(node)) {
24672 return this.visitVariableDeclaration(node, context);
24673 }
24674 else if (ts$1.isParameter(node) && context.scope.has(node)) {
24675 return context.scope.get(node);
24676 }
24677 else if (ts$1.isExportAssignment(node)) {
24678 return this.visitExpression(node.expression, context);
24679 }
24680 else if (ts$1.isEnumDeclaration(node)) {
24681 return this.visitEnumDeclaration(node, context);
24682 }
24683 else if (ts$1.isSourceFile(node)) {
24684 return this.visitSourceFile(node, context);
24685 }
24686 else if (ts$1.isBindingElement(node)) {
24687 return this.visitBindingElement(node, context);
24688 }
24689 else {
24690 return this.getReference(node, context);
24691 }
24692 }
24693 visitVariableDeclaration(node, context) {
24694 const value = this.host.getVariableValue(node);
24695 if (value !== null) {
24696 return this.visitExpression(value, context);
24697 }
24698 else if (isVariableDeclarationDeclared(node)) {
24699 return this.getReference(node, context);
24700 }
24701 else {
24702 return undefined;
24703 }
24704 }
24705 visitEnumDeclaration(node, context) {
24706 const enumRef = this.getReference(node, context);
24707 const map = new Map();
24708 node.members.forEach(member => {
24709 const name = this.stringNameFromPropertyName(member.name, context);
24710 if (name !== undefined) {
24711 const resolved = member.initializer && this.visit(member.initializer, context);
24712 map.set(name, new EnumValue(enumRef, name, resolved));
24713 }
24714 });
24715 return map;
24716 }
24717 visitElementAccessExpression(node, context) {
24718 const lhs = this.visitExpression(node.expression, context);
24719 if (lhs instanceof DynamicValue) {
24720 return DynamicValue.fromDynamicInput(node, lhs);
24721 }
24722 const rhs = this.visitExpression(node.argumentExpression, context);
24723 if (rhs instanceof DynamicValue) {
24724 return DynamicValue.fromDynamicInput(node, rhs);
24725 }
24726 if (typeof rhs !== 'string' && typeof rhs !== 'number') {
24727 return DynamicValue.fromInvalidExpressionType(node, rhs);
24728 }
24729 return this.accessHelper(node, lhs, rhs, context);
24730 }
24731 visitPropertyAccessExpression(node, context) {
24732 const lhs = this.visitExpression(node.expression, context);
24733 const rhs = node.name.text;
24734 // TODO: handle reference to class declaration.
24735 if (lhs instanceof DynamicValue) {
24736 return DynamicValue.fromDynamicInput(node, lhs);
24737 }
24738 return this.accessHelper(node, lhs, rhs, context);
24739 }
24740 visitSourceFile(node, context) {
24741 const declarations = this.host.getExportsOfModule(node);
24742 if (declarations === null) {
24743 return DynamicValue.fromUnknown(node);
24744 }
24745 return new ResolvedModule(declarations, decl => {
24746 if (decl.known !== null) {
24747 return resolveKnownDeclaration(decl.known);
24748 }
24749 const declContext = Object.assign(Object.assign({}, context), joinModuleContext(context, node, decl));
24750 // Visit both concrete and inline declarations.
24751 return this.visitAmbiguousDeclaration(decl, declContext);
24752 });
24753 }
24754 visitAmbiguousDeclaration(decl, declContext) {
24755 return decl.kind === 1 /* Inline */ && decl.implementation !== undefined &&
24756 !isDeclaration(decl.implementation) ?
24757 // Inline declarations whose `implementation` is a `ts.Expression` should be visited as
24758 // an expression.
24759 this.visitExpression(decl.implementation, declContext) :
24760 // Otherwise just visit the `node` as a declaration.
24761 this.visitDeclaration(decl.node, declContext);
24762 }
24763 accessHelper(node, lhs, rhs, context) {
24764 const strIndex = `${rhs}`;
24765 if (lhs instanceof Map) {
24766 if (lhs.has(strIndex)) {
24767 return lhs.get(strIndex);
24768 }
24769 else {
24770 return undefined;
24771 }
24772 }
24773 else if (lhs instanceof ResolvedModule) {
24774 return lhs.getExport(strIndex);
24775 }
24776 else if (Array.isArray(lhs)) {
24777 if (rhs === 'length') {
24778 return lhs.length;
24779 }
24780 else if (rhs === 'slice') {
24781 return new ArraySliceBuiltinFn(lhs);
24782 }
24783 else if (rhs === 'concat') {
24784 return new ArrayConcatBuiltinFn(lhs);
24785 }
24786 if (typeof rhs !== 'number' || !Number.isInteger(rhs)) {
24787 return DynamicValue.fromInvalidExpressionType(node, rhs);
24788 }
24789 return lhs[rhs];
24790 }
24791 else if (lhs instanceof Reference$1) {
24792 const ref = lhs.node;
24793 if (this.host.isClass(ref)) {
24794 const module = owningModule(context, lhs.bestGuessOwningModule);
24795 let value = undefined;
24796 const member = this.host.getMembersOfClass(ref).find(member => member.isStatic && member.name === strIndex);
24797 if (member !== undefined) {
24798 if (member.value !== null) {
24799 value = this.visitExpression(member.value, context);
24800 }
24801 else if (member.implementation !== null) {
24802 value = new Reference$1(member.implementation, module);
24803 }
24804 else if (member.node) {
24805 value = new Reference$1(member.node, module);
24806 }
24807 }
24808 return value;
24809 }
24810 else if (isDeclaration(ref)) {
24811 return DynamicValue.fromDynamicInput(node, DynamicValue.fromExternalReference(ref, lhs));
24812 }
24813 }
24814 else if (lhs instanceof DynamicValue) {
24815 return DynamicValue.fromDynamicInput(node, lhs);
24816 }
24817 return DynamicValue.fromUnknown(node);
24818 }
24819 visitCallExpression(node, context) {
24820 const lhs = this.visitExpression(node.expression, context);
24821 if (lhs instanceof DynamicValue) {
24822 return DynamicValue.fromDynamicInput(node, lhs);
24823 }
24824 // If the call refers to a builtin function, attempt to evaluate the function.
24825 if (lhs instanceof KnownFn) {
24826 return lhs.evaluate(node, this.evaluateFunctionArguments(node, context));
24827 }
24828 if (!(lhs instanceof Reference$1)) {
24829 return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
24830 }
24831 const fn = this.host.getDefinitionOfFunction(lhs.node);
24832 if (fn === null) {
24833 return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
24834 }
24835 if (!isFunctionOrMethodReference(lhs)) {
24836 return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
24837 }
24838 // If the function is foreign (declared through a d.ts file), attempt to resolve it with the
24839 // foreignFunctionResolver, if one is specified.
24840 if (fn.body === null) {
24841 let expr = null;
24842 if (context.foreignFunctionResolver) {
24843 expr = context.foreignFunctionResolver(lhs, node.arguments);
24844 }
24845 if (expr === null) {
24846 return DynamicValue.fromDynamicInput(node, DynamicValue.fromExternalReference(node.expression, lhs));
24847 }
24848 // If the function is declared in a different file, resolve the foreign function expression
24849 // using the absolute module name of that file (if any).
24850 if (lhs.bestGuessOwningModule !== null) {
24851 context = Object.assign(Object.assign({}, context), { absoluteModuleName: lhs.bestGuessOwningModule.specifier, resolutionContext: node.getSourceFile().fileName });
24852 }
24853 return this.visitFfrExpression(expr, context);
24854 }
24855 let res = this.visitFunctionBody(node, fn, context);
24856 // If the result of attempting to resolve the function body was a DynamicValue, attempt to use
24857 // the foreignFunctionResolver if one is present. This could still potentially yield a usable
24858 // value.
24859 if (res instanceof DynamicValue && context.foreignFunctionResolver !== undefined) {
24860 const ffrExpr = context.foreignFunctionResolver(lhs, node.arguments);
24861 if (ffrExpr !== null) {
24862 // The foreign function resolver was able to extract an expression from this function. See
24863 // if that expression leads to a non-dynamic result.
24864 const ffrRes = this.visitFfrExpression(ffrExpr, context);
24865 if (!(ffrRes instanceof DynamicValue)) {
24866 // FFR yielded an actual result that's not dynamic, so use that instead of the original
24867 // resolution.
24868 res = ffrRes;
24869 }
24870 }
24871 }
24872 return res;
24873 }
24874 /**
24875 * Visit an expression which was extracted from a foreign-function resolver.
24876 *
24877 * This will process the result and ensure it's correct for FFR-resolved values, including marking
24878 * `Reference`s as synthetic.
24879 */
24880 visitFfrExpression(expr, context) {
24881 const res = this.visitExpression(expr, context);
24882 if (res instanceof Reference$1) {
24883 // This Reference was created synthetically, via a foreign function resolver. The real
24884 // runtime value of the function expression may be different than the foreign function
24885 // resolved value, so mark the Reference as synthetic to avoid it being misinterpreted.
24886 res.synthetic = true;
24887 }
24888 return res;
24889 }
24890 visitFunctionBody(node, fn, context) {
24891 if (fn.body === null) {
24892 return DynamicValue.fromUnknown(node);
24893 }
24894 else if (fn.body.length !== 1 || !ts$1.isReturnStatement(fn.body[0])) {
24895 return DynamicValue.fromComplexFunctionCall(node, fn);
24896 }
24897 const ret = fn.body[0];
24898 const args = this.evaluateFunctionArguments(node, context);
24899 const newScope = new Map();
24900 const calleeContext = Object.assign(Object.assign({}, context), { scope: newScope });
24901 fn.parameters.forEach((param, index) => {
24902 let arg = args[index];
24903 if (param.node.dotDotDotToken !== undefined) {
24904 arg = args.slice(index);
24905 }
24906 if (arg === undefined && param.initializer !== null) {
24907 arg = this.visitExpression(param.initializer, calleeContext);
24908 }
24909 newScope.set(param.node, arg);
24910 });
24911 return ret.expression !== undefined ? this.visitExpression(ret.expression, calleeContext) :
24912 undefined;
24913 }
24914 visitConditionalExpression(node, context) {
24915 const condition = this.visitExpression(node.condition, context);
24916 if (condition instanceof DynamicValue) {
24917 return DynamicValue.fromDynamicInput(node, condition);
24918 }
24919 if (condition) {
24920 return this.visitExpression(node.whenTrue, context);
24921 }
24922 else {
24923 return this.visitExpression(node.whenFalse, context);
24924 }
24925 }
24926 visitPrefixUnaryExpression(node, context) {
24927 const operatorKind = node.operator;
24928 if (!UNARY_OPERATORS.has(operatorKind)) {
24929 return DynamicValue.fromUnsupportedSyntax(node);
24930 }
24931 const op = UNARY_OPERATORS.get(operatorKind);
24932 const value = this.visitExpression(node.operand, context);
24933 if (value instanceof DynamicValue) {
24934 return DynamicValue.fromDynamicInput(node, value);
24935 }
24936 else {
24937 return op(value);
24938 }
24939 }
24940 visitBinaryExpression(node, context) {
24941 const tokenKind = node.operatorToken.kind;
24942 if (!BINARY_OPERATORS.has(tokenKind)) {
24943 return DynamicValue.fromUnsupportedSyntax(node);
24944 }
24945 const opRecord = BINARY_OPERATORS.get(tokenKind);
24946 let lhs, rhs;
24947 if (opRecord.literal) {
24948 lhs = literal$1(this.visitExpression(node.left, context), value => DynamicValue.fromInvalidExpressionType(node.left, value));
24949 rhs = literal$1(this.visitExpression(node.right, context), value => DynamicValue.fromInvalidExpressionType(node.right, value));
24950 }
24951 else {
24952 lhs = this.visitExpression(node.left, context);
24953 rhs = this.visitExpression(node.right, context);
24954 }
24955 if (lhs instanceof DynamicValue) {
24956 return DynamicValue.fromDynamicInput(node, lhs);
24957 }
24958 else if (rhs instanceof DynamicValue) {
24959 return DynamicValue.fromDynamicInput(node, rhs);
24960 }
24961 else {
24962 return opRecord.op(lhs, rhs);
24963 }
24964 }
24965 visitParenthesizedExpression(node, context) {
24966 return this.visitExpression(node.expression, context);
24967 }
24968 evaluateFunctionArguments(node, context) {
24969 const args = [];
24970 for (const arg of node.arguments) {
24971 if (ts$1.isSpreadElement(arg)) {
24972 args.push(...this.visitSpreadElement(arg, context));
24973 }
24974 else {
24975 args.push(this.visitExpression(arg, context));
24976 }
24977 }
24978 return args;
24979 }
24980 visitSpreadElement(node, context) {
24981 const spread = this.visitExpression(node.expression, context);
24982 if (spread instanceof DynamicValue) {
24983 return [DynamicValue.fromDynamicInput(node, spread)];
24984 }
24985 else if (!Array.isArray(spread)) {
24986 return [DynamicValue.fromInvalidExpressionType(node, spread)];
24987 }
24988 else {
24989 return spread;
24990 }
24991 }
24992 visitBindingElement(node, context) {
24993 const path = [];
24994 let closestDeclaration = node;
24995 while (ts$1.isBindingElement(closestDeclaration) ||
24996 ts$1.isArrayBindingPattern(closestDeclaration) ||
24997 ts$1.isObjectBindingPattern(closestDeclaration)) {
24998 if (ts$1.isBindingElement(closestDeclaration)) {
24999 path.unshift(closestDeclaration);
25000 }
25001 closestDeclaration = closestDeclaration.parent;
25002 }
25003 if (!ts$1.isVariableDeclaration(closestDeclaration) ||
25004 closestDeclaration.initializer === undefined) {
25005 return DynamicValue.fromUnknown(node);
25006 }
25007 let value = this.visit(closestDeclaration.initializer, context);
25008 for (const element of path) {
25009 let key;
25010 if (ts$1.isArrayBindingPattern(element.parent)) {
25011 key = element.parent.elements.indexOf(element);
25012 }
25013 else {
25014 const name = element.propertyName || element.name;
25015 if (ts$1.isIdentifier(name)) {
25016 key = name.text;
25017 }
25018 else {
25019 return DynamicValue.fromUnknown(element);
25020 }
25021 }
25022 value = this.accessHelper(element, value, key, context);
25023 if (value instanceof DynamicValue) {
25024 return value;
25025 }
25026 }
25027 return value;
25028 }
25029 stringNameFromPropertyName(node, context) {
25030 if (ts$1.isIdentifier(node) || ts$1.isStringLiteral(node) || ts$1.isNumericLiteral(node)) {
25031 return node.text;
25032 }
25033 else if (ts$1.isComputedPropertyName(node)) {
25034 const literal = this.visitExpression(node.expression, context);
25035 return typeof literal === 'string' ? literal : undefined;
25036 }
25037 else {
25038 return undefined;
25039 }
25040 }
25041 getResolvedEnum(node, enumMembers, context) {
25042 const enumRef = this.getReference(node, context);
25043 const map = new Map();
25044 enumMembers.forEach(member => {
25045 const name = this.stringNameFromPropertyName(member.name, context);
25046 if (name !== undefined) {
25047 const resolved = this.visit(member.initializer, context);
25048 map.set(name, new EnumValue(enumRef, name, resolved));
25049 }
25050 });
25051 return map;
25052 }
25053 getReference(node, context) {
25054 return new Reference$1(node, owningModule(context));
25055 }
25056 }
25057 function isFunctionOrMethodReference(ref) {
25058 return ts$1.isFunctionDeclaration(ref.node) || ts$1.isMethodDeclaration(ref.node) ||
25059 ts$1.isFunctionExpression(ref.node);
25060 }
25061 function literal$1(value, reject) {
25062 if (value instanceof EnumValue) {
25063 value = value.resolved;
25064 }
25065 if (value instanceof DynamicValue || value === null || value === undefined ||
25066 typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
25067 return value;
25068 }
25069 return reject(value);
25070 }
25071 function isVariableDeclarationDeclared(node) {
25072 if (node.parent === undefined || !ts$1.isVariableDeclarationList(node.parent)) {
25073 return false;
25074 }
25075 const declList = node.parent;
25076 if (declList.parent === undefined || !ts$1.isVariableStatement(declList.parent)) {
25077 return false;
25078 }
25079 const varStmt = declList.parent;
25080 return varStmt.modifiers !== undefined &&
25081 varStmt.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.DeclareKeyword);
25082 }
25083 const EMPTY = {};
25084 function joinModuleContext(existing, node, decl) {
25085 if (decl.viaModule !== null && decl.viaModule !== existing.absoluteModuleName) {
25086 return {
25087 absoluteModuleName: decl.viaModule,
25088 resolutionContext: node.getSourceFile().fileName,
25089 };
25090 }
25091 else {
25092 return EMPTY;
25093 }
25094 }
25095 function owningModule(context, override = null) {
25096 let specifier = context.absoluteModuleName;
25097 if (override !== null) {
25098 specifier = override.specifier;
25099 }
25100 if (specifier !== null) {
25101 return {
25102 specifier,
25103 resolutionContext: context.resolutionContext,
25104 };
25105 }
25106 else {
25107 return null;
25108 }
25109 }
25110
25111 /**
25112 * @license
25113 * Copyright Google LLC All Rights Reserved.
25114 *
25115 * Use of this source code is governed by an MIT-style license that can be
25116 * found in the LICENSE file at https://angular.io/license
25117 */
25118 class PartialEvaluator {
25119 constructor(host, checker, dependencyTracker) {
25120 this.host = host;
25121 this.checker = checker;
25122 this.dependencyTracker = dependencyTracker;
25123 }
25124 evaluate(expr, foreignFunctionResolver) {
25125 const interpreter = new StaticInterpreter(this.host, this.checker, this.dependencyTracker);
25126 const sourceFile = expr.getSourceFile();
25127 return interpreter.visit(expr, {
25128 originatingFile: sourceFile,
25129 absoluteModuleName: null,
25130 resolutionContext: sourceFile.fileName,
25131 scope: new Map(),
25132 foreignFunctionResolver,
25133 });
25134 }
25135 }
25136
25137 /**
25138 * @license
25139 * Copyright Google LLC All Rights Reserved.
25140 *
25141 * Use of this source code is governed by an MIT-style license that can be
25142 * found in the LICENSE file at https://angular.io/license
25143 */
25144 /**
25145 * Specifies the compilation mode that is used for the compilation.
25146 */
25147 var CompilationMode;
25148 (function (CompilationMode) {
25149 /**
25150 * Generates fully AOT compiled code using Ivy instructions.
25151 */
25152 CompilationMode[CompilationMode["FULL"] = 0] = "FULL";
25153 /**
25154 * Generates code using a stable, but intermediate format suitable to be published to NPM.
25155 */
25156 CompilationMode[CompilationMode["PARTIAL"] = 1] = "PARTIAL";
25157 })(CompilationMode || (CompilationMode = {}));
25158 var HandlerPrecedence;
25159 (function (HandlerPrecedence) {
25160 /**
25161 * Handler with PRIMARY precedence cannot overlap - there can only be one on a given class.
25162 *
25163 * If more than one PRIMARY handler matches a class, an error is produced.
25164 */
25165 HandlerPrecedence[HandlerPrecedence["PRIMARY"] = 0] = "PRIMARY";
25166 /**
25167 * Handlers with SHARED precedence can match any class, possibly in addition to a single PRIMARY
25168 * handler.
25169 *
25170 * It is not an error for a class to have any number of SHARED handlers.
25171 */
25172 HandlerPrecedence[HandlerPrecedence["SHARED"] = 1] = "SHARED";
25173 /**
25174 * Handlers with WEAK precedence that match a class are ignored if any handlers with stronger
25175 * precedence match a class.
25176 */
25177 HandlerPrecedence[HandlerPrecedence["WEAK"] = 2] = "WEAK";
25178 })(HandlerPrecedence || (HandlerPrecedence = {}));
25179 /**
25180 * A set of options which can be passed to a `DecoratorHandler` by a consumer, to tailor the output
25181 * of compilation beyond the decorators themselves.
25182 */
25183 var HandlerFlags;
25184 (function (HandlerFlags) {
25185 /**
25186 * No flags set.
25187 */
25188 HandlerFlags[HandlerFlags["NONE"] = 0] = "NONE";
25189 /**
25190 * Indicates that this decorator is fully inherited from its parent at runtime. In addition to
25191 * normally inherited aspects such as inputs and queries, full inheritance applies to every aspect
25192 * of the component or directive, such as the template function itself.
25193 *
25194 * Its primary effect is to cause the `CopyDefinitionFeature` to be applied to the definition
25195 * being compiled. See that class for more information.
25196 */
25197 HandlerFlags[HandlerFlags["FULL_INHERITANCE"] = 1] = "FULL_INHERITANCE";
25198 })(HandlerFlags || (HandlerFlags = {}));
25199
25200 /**
25201 * @license
25202 * Copyright Google LLC All Rights Reserved.
25203 *
25204 * Use of this source code is governed by an MIT-style license that can be
25205 * found in the LICENSE file at https://angular.io/license
25206 */
25207 function aliasTransformFactory(exportStatements) {
25208 return (context) => {
25209 return (file) => {
25210 if (ts$1.isBundle(file) || !exportStatements.has(file.fileName)) {
25211 return file;
25212 }
25213 const statements = [...file.statements];
25214 exportStatements.get(file.fileName).forEach(([moduleName, symbolName], aliasName) => {
25215 const stmt = ts$1.createExportDeclaration(
25216 /* decorators */ undefined,
25217 /* modifiers */ undefined,
25218 /* exportClause */ ts$1.createNamedExports([ts$1.createExportSpecifier(
25219 /* propertyName */ symbolName,
25220 /* name */ aliasName)]),
25221 /* moduleSpecifier */ ts$1.createStringLiteral(moduleName));
25222 statements.push(stmt);
25223 });
25224 return ts$1.updateSourceFileNode(file, statements);
25225 };
25226 };
25227 }
25228
25229 /**
25230 * @license
25231 * Copyright Google LLC All Rights Reserved.
25232 *
25233 * Use of this source code is governed by an MIT-style license that can be
25234 * found in the LICENSE file at https://angular.io/license
25235 */
25236 var TraitState;
25237 (function (TraitState) {
25238 /**
25239 * Pending traits are freshly created and have never been analyzed.
25240 */
25241 TraitState[TraitState["Pending"] = 0] = "Pending";
25242 /**
25243 * Analyzed traits have successfully been analyzed, but are pending resolution.
25244 */
25245 TraitState[TraitState["Analyzed"] = 1] = "Analyzed";
25246 /**
25247 * Resolved traits have successfully been analyzed and resolved and are ready for compilation.
25248 */
25249 TraitState[TraitState["Resolved"] = 2] = "Resolved";
25250 /**
25251 * Skipped traits are no longer considered for compilation.
25252 */
25253 TraitState[TraitState["Skipped"] = 3] = "Skipped";
25254 })(TraitState || (TraitState = {}));
25255 /**
25256 * The value side of `Trait` exposes a helper to create a `Trait` in a pending state (by delegating
25257 * to `TraitImpl`).
25258 */
25259 const Trait = {
25260 pending: (handler, detected) => TraitImpl.pending(handler, detected),
25261 };
25262 /**
25263 * An implementation of the `Trait` type which transitions safely between the various
25264 * `TraitState`s.
25265 */
25266 class TraitImpl {
25267 constructor(handler, detected) {
25268 this.state = TraitState.Pending;
25269 this.analysis = null;
25270 this.resolution = null;
25271 this.analysisDiagnostics = null;
25272 this.resolveDiagnostics = null;
25273 this.handler = handler;
25274 this.detected = detected;
25275 }
25276 toAnalyzed(analysis, diagnostics) {
25277 // Only pending traits can be analyzed.
25278 this.assertTransitionLegal(TraitState.Pending, TraitState.Analyzed);
25279 this.analysis = analysis;
25280 this.analysisDiagnostics = diagnostics;
25281 this.state = TraitState.Analyzed;
25282 return this;
25283 }
25284 toResolved(resolution, diagnostics) {
25285 // Only analyzed traits can be resolved.
25286 this.assertTransitionLegal(TraitState.Analyzed, TraitState.Resolved);
25287 if (this.analysis === null) {
25288 throw new Error(`Cannot transition an Analyzed trait with a null analysis to Resolved`);
25289 }
25290 this.resolution = resolution;
25291 this.state = TraitState.Resolved;
25292 this.resolveDiagnostics = diagnostics;
25293 return this;
25294 }
25295 toSkipped() {
25296 // Only pending traits can be skipped.
25297 this.assertTransitionLegal(TraitState.Pending, TraitState.Skipped);
25298 this.state = TraitState.Skipped;
25299 return this;
25300 }
25301 /**
25302 * Verifies that the trait is currently in one of the `allowedState`s.
25303 *
25304 * If correctly used, the `Trait` type and transition methods prevent illegal transitions from
25305 * occurring. However, if a reference to the `TraitImpl` instance typed with the previous
25306 * interface is retained after calling one of its transition methods, it will allow for illegal
25307 * transitions to take place. Hence, this assertion provides a little extra runtime protection.
25308 */
25309 assertTransitionLegal(allowedState, transitionTo) {
25310 if (!(this.state === allowedState)) {
25311 throw new Error(`Assertion failure: cannot transition from ${TraitState[this.state]} to ${TraitState[transitionTo]}.`);
25312 }
25313 }
25314 /**
25315 * Construct a new `TraitImpl` in the pending state.
25316 */
25317 static pending(handler, detected) {
25318 return new TraitImpl(handler, detected);
25319 }
25320 }
25321
25322 /**
25323 * @license
25324 * Copyright Google LLC All Rights Reserved.
25325 *
25326 * Use of this source code is governed by an MIT-style license that can be
25327 * found in the LICENSE file at https://angular.io/license
25328 */
25329 /**
25330 * The heart of Angular compilation.
25331 *
25332 * The `TraitCompiler` is responsible for processing all classes in the program. Any time a
25333 * `DecoratorHandler` matches a class, a "trait" is created to represent that Angular aspect of the
25334 * class (such as the class having a component definition).
25335 *
25336 * The `TraitCompiler` transitions each trait through the various phases of compilation, culminating
25337 * in the production of `CompileResult`s instructing the compiler to apply various mutations to the
25338 * class (like adding fields or type declarations).
25339 */
25340 class TraitCompiler {
25341 constructor(handlers, reflector, perf, incrementalBuild, compileNonExportedClasses, compilationMode, dtsTransforms) {
25342 this.handlers = handlers;
25343 this.reflector = reflector;
25344 this.perf = perf;
25345 this.incrementalBuild = incrementalBuild;
25346 this.compileNonExportedClasses = compileNonExportedClasses;
25347 this.compilationMode = compilationMode;
25348 this.dtsTransforms = dtsTransforms;
25349 /**
25350 * Maps class declarations to their `ClassRecord`, which tracks the Ivy traits being applied to
25351 * those classes.
25352 */
25353 this.classes = new Map();
25354 /**
25355 * Maps source files to any class declaration(s) within them which have been discovered to contain
25356 * Ivy traits.
25357 */
25358 this.fileToClasses = new Map();
25359 this.reexportMap = new Map();
25360 this.handlersByName = new Map();
25361 for (const handler of handlers) {
25362 this.handlersByName.set(handler.name, handler);
25363 }
25364 }
25365 analyzeSync(sf) {
25366 this.analyze(sf, false);
25367 }
25368 analyzeAsync(sf) {
25369 return this.analyze(sf, true);
25370 }
25371 analyze(sf, preanalyze) {
25372 // We shouldn't analyze declaration files.
25373 if (sf.isDeclarationFile) {
25374 return undefined;
25375 }
25376 // analyze() really wants to return `Promise<void>|void`, but TypeScript cannot narrow a return
25377 // type of 'void', so `undefined` is used instead.
25378 const promises = [];
25379 const priorWork = this.incrementalBuild.priorWorkFor(sf);
25380 if (priorWork !== null) {
25381 for (const priorRecord of priorWork) {
25382 this.adopt(priorRecord);
25383 }
25384 // Skip the rest of analysis, as this file's prior traits are being reused.
25385 return;
25386 }
25387 const visit = (node) => {
25388 if (this.reflector.isClass(node)) {
25389 this.analyzeClass(node, preanalyze ? promises : null);
25390 }
25391 ts$1.forEachChild(node, visit);
25392 };
25393 visit(sf);
25394 if (preanalyze && promises.length > 0) {
25395 return Promise.all(promises).then(() => undefined);
25396 }
25397 else {
25398 return undefined;
25399 }
25400 }
25401 recordFor(clazz) {
25402 if (this.classes.has(clazz)) {
25403 return this.classes.get(clazz);
25404 }
25405 else {
25406 return null;
25407 }
25408 }
25409 recordsFor(sf) {
25410 if (!this.fileToClasses.has(sf)) {
25411 return null;
25412 }
25413 const records = [];
25414 for (const clazz of this.fileToClasses.get(sf)) {
25415 records.push(this.classes.get(clazz));
25416 }
25417 return records;
25418 }
25419 /**
25420 * Import a `ClassRecord` from a previous compilation.
25421 *
25422 * Traits from the `ClassRecord` have accurate metadata, but the `handler` is from the old program
25423 * and needs to be updated (matching is done by name). A new pending trait is created and then
25424 * transitioned to analyzed using the previous analysis. If the trait is in the errored state,
25425 * instead the errors are copied over.
25426 */
25427 adopt(priorRecord) {
25428 const record = {
25429 hasPrimaryHandler: priorRecord.hasPrimaryHandler,
25430 hasWeakHandlers: priorRecord.hasWeakHandlers,
25431 metaDiagnostics: priorRecord.metaDiagnostics,
25432 node: priorRecord.node,
25433 traits: [],
25434 };
25435 for (const priorTrait of priorRecord.traits) {
25436 const handler = this.handlersByName.get(priorTrait.handler.name);
25437 let trait = Trait.pending(handler, priorTrait.detected);
25438 if (priorTrait.state === TraitState.Analyzed || priorTrait.state === TraitState.Resolved) {
25439 trait = trait.toAnalyzed(priorTrait.analysis, priorTrait.analysisDiagnostics);
25440 if (trait.analysis !== null && trait.handler.register !== undefined) {
25441 trait.handler.register(record.node, trait.analysis);
25442 }
25443 }
25444 else if (priorTrait.state === TraitState.Skipped) {
25445 trait = trait.toSkipped();
25446 }
25447 record.traits.push(trait);
25448 }
25449 this.classes.set(record.node, record);
25450 const sf = record.node.getSourceFile();
25451 if (!this.fileToClasses.has(sf)) {
25452 this.fileToClasses.set(sf, new Set());
25453 }
25454 this.fileToClasses.get(sf).add(record.node);
25455 }
25456 scanClassForTraits(clazz) {
25457 if (!this.compileNonExportedClasses && !isExported(clazz)) {
25458 return null;
25459 }
25460 const decorators = this.reflector.getDecoratorsOfDeclaration(clazz);
25461 return this.detectTraits(clazz, decorators);
25462 }
25463 detectTraits(clazz, decorators) {
25464 let record = this.recordFor(clazz);
25465 let foundTraits = [];
25466 for (const handler of this.handlers) {
25467 const result = handler.detect(clazz, decorators);
25468 if (result === undefined) {
25469 continue;
25470 }
25471 const isPrimaryHandler = handler.precedence === HandlerPrecedence.PRIMARY;
25472 const isWeakHandler = handler.precedence === HandlerPrecedence.WEAK;
25473 const trait = Trait.pending(handler, result);
25474 foundTraits.push(trait);
25475 if (record === null) {
25476 // This is the first handler to match this class. This path is a fast path through which
25477 // most classes will flow.
25478 record = {
25479 node: clazz,
25480 traits: [trait],
25481 metaDiagnostics: null,
25482 hasPrimaryHandler: isPrimaryHandler,
25483 hasWeakHandlers: isWeakHandler,
25484 };
25485 this.classes.set(clazz, record);
25486 const sf = clazz.getSourceFile();
25487 if (!this.fileToClasses.has(sf)) {
25488 this.fileToClasses.set(sf, new Set());
25489 }
25490 this.fileToClasses.get(sf).add(clazz);
25491 }
25492 else {
25493 // This is at least the second handler to match this class. This is a slower path that some
25494 // classes will go through, which validates that the set of decorators applied to the class
25495 // is valid.
25496 // Validate according to rules as follows:
25497 //
25498 // * WEAK handlers are removed if a non-WEAK handler matches.
25499 // * Only one PRIMARY handler can match at a time. Any other PRIMARY handler matching a
25500 // class with an existing PRIMARY handler is an error.
25501 if (!isWeakHandler && record.hasWeakHandlers) {
25502 // The current handler is not a WEAK handler, but the class has other WEAK handlers.
25503 // Remove them.
25504 record.traits =
25505 record.traits.filter(field => field.handler.precedence !== HandlerPrecedence.WEAK);
25506 record.hasWeakHandlers = false;
25507 }
25508 else if (isWeakHandler && !record.hasWeakHandlers) {
25509 // The current handler is a WEAK handler, but the class has non-WEAK handlers already.
25510 // Drop the current one.
25511 continue;
25512 }
25513 if (isPrimaryHandler && record.hasPrimaryHandler) {
25514 // The class already has a PRIMARY handler, and another one just matched.
25515 record.metaDiagnostics = [{
25516 category: ts$1.DiagnosticCategory.Error,
25517 code: Number('-99' + ErrorCode.DECORATOR_COLLISION),
25518 file: getSourceFile(clazz),
25519 start: clazz.getStart(undefined, false),
25520 length: clazz.getWidth(),
25521 messageText: 'Two incompatible decorators on class',
25522 }];
25523 record.traits = foundTraits = [];
25524 break;
25525 }
25526 // Otherwise, it's safe to accept the multiple decorators here. Update some of the metadata
25527 // regarding this class.
25528 record.traits.push(trait);
25529 record.hasPrimaryHandler = record.hasPrimaryHandler || isPrimaryHandler;
25530 }
25531 }
25532 return foundTraits.length > 0 ? foundTraits : null;
25533 }
25534 analyzeClass(clazz, preanalyzeQueue) {
25535 const traits = this.scanClassForTraits(clazz);
25536 if (traits === null) {
25537 // There are no Ivy traits on the class, so it can safely be skipped.
25538 return;
25539 }
25540 for (const trait of traits) {
25541 const analyze = () => this.analyzeTrait(clazz, trait);
25542 let preanalysis = null;
25543 if (preanalyzeQueue !== null && trait.handler.preanalyze !== undefined) {
25544 // Attempt to run preanalysis. This could fail with a `FatalDiagnosticError`; catch it if it
25545 // does.
25546 try {
25547 preanalysis = trait.handler.preanalyze(clazz, trait.detected.metadata) || null;
25548 }
25549 catch (err) {
25550 if (err instanceof FatalDiagnosticError) {
25551 trait.toAnalyzed(null, [err.toDiagnostic()]);
25552 return;
25553 }
25554 else {
25555 throw err;
25556 }
25557 }
25558 }
25559 if (preanalysis !== null) {
25560 preanalyzeQueue.push(preanalysis.then(analyze));
25561 }
25562 else {
25563 analyze();
25564 }
25565 }
25566 }
25567 analyzeTrait(clazz, trait, flags) {
25568 var _a, _b;
25569 if (trait.state !== TraitState.Pending) {
25570 throw new Error(`Attempt to analyze trait of ${clazz.name.text} in state ${TraitState[trait.state]} (expected DETECTED)`);
25571 }
25572 // Attempt analysis. This could fail with a `FatalDiagnosticError`; catch it if it does.
25573 let result;
25574 try {
25575 result = trait.handler.analyze(clazz, trait.detected.metadata, flags);
25576 }
25577 catch (err) {
25578 if (err instanceof FatalDiagnosticError) {
25579 trait.toAnalyzed(null, [err.toDiagnostic()]);
25580 return;
25581 }
25582 else {
25583 throw err;
25584 }
25585 }
25586 if (result.analysis !== undefined && trait.handler.register !== undefined) {
25587 trait.handler.register(clazz, result.analysis);
25588 }
25589 trait = trait.toAnalyzed((_a = result.analysis) !== null && _a !== void 0 ? _a : null, (_b = result.diagnostics) !== null && _b !== void 0 ? _b : null);
25590 }
25591 resolve() {
25592 var _a, _b;
25593 const classes = Array.from(this.classes.keys());
25594 for (const clazz of classes) {
25595 const record = this.classes.get(clazz);
25596 for (let trait of record.traits) {
25597 const handler = trait.handler;
25598 switch (trait.state) {
25599 case TraitState.Skipped:
25600 continue;
25601 case TraitState.Pending:
25602 throw new Error(`Resolving a trait that hasn't been analyzed: ${clazz.name.text} / ${Object.getPrototypeOf(trait.handler).constructor.name}`);
25603 case TraitState.Resolved:
25604 throw new Error(`Resolving an already resolved trait`);
25605 }
25606 if (trait.analysis === null) {
25607 // No analysis results, cannot further process this trait.
25608 continue;
25609 }
25610 if (handler.resolve === undefined) {
25611 // No resolution of this trait needed - it's considered successful by default.
25612 trait = trait.toResolved(null, null);
25613 continue;
25614 }
25615 let result;
25616 try {
25617 result = handler.resolve(clazz, trait.analysis);
25618 }
25619 catch (err) {
25620 if (err instanceof FatalDiagnosticError) {
25621 trait = trait.toResolved(null, [err.toDiagnostic()]);
25622 continue;
25623 }
25624 else {
25625 throw err;
25626 }
25627 }
25628 trait = trait.toResolved((_a = result.data) !== null && _a !== void 0 ? _a : null, (_b = result.diagnostics) !== null && _b !== void 0 ? _b : null);
25629 if (result.reexports !== undefined) {
25630 const fileName = clazz.getSourceFile().fileName;
25631 if (!this.reexportMap.has(fileName)) {
25632 this.reexportMap.set(fileName, new Map());
25633 }
25634 const fileReexports = this.reexportMap.get(fileName);
25635 for (const reexport of result.reexports) {
25636 fileReexports.set(reexport.asAlias, [reexport.fromModule, reexport.symbolName]);
25637 }
25638 }
25639 }
25640 }
25641 }
25642 /**
25643 * Generate type-checking code into the `TypeCheckContext` for any components within the given
25644 * `ts.SourceFile`.
25645 */
25646 typeCheck(sf, ctx) {
25647 if (!this.fileToClasses.has(sf)) {
25648 return;
25649 }
25650 for (const clazz of this.fileToClasses.get(sf)) {
25651 const record = this.classes.get(clazz);
25652 for (const trait of record.traits) {
25653 if (trait.state !== TraitState.Resolved) {
25654 continue;
25655 }
25656 else if (trait.handler.typeCheck === undefined) {
25657 continue;
25658 }
25659 if (trait.resolution !== null) {
25660 trait.handler.typeCheck(ctx, clazz, trait.analysis, trait.resolution);
25661 }
25662 }
25663 }
25664 }
25665 index(ctx) {
25666 for (const clazz of this.classes.keys()) {
25667 const record = this.classes.get(clazz);
25668 for (const trait of record.traits) {
25669 if (trait.state !== TraitState.Resolved) {
25670 // Skip traits that haven't been resolved successfully.
25671 continue;
25672 }
25673 else if (trait.handler.index === undefined) {
25674 // Skip traits that don't affect indexing.
25675 continue;
25676 }
25677 if (trait.resolution !== null) {
25678 trait.handler.index(ctx, clazz, trait.analysis, trait.resolution);
25679 }
25680 }
25681 }
25682 }
25683 updateResources(clazz) {
25684 if (!this.reflector.isClass(clazz) || !this.classes.has(clazz)) {
25685 return;
25686 }
25687 const record = this.classes.get(clazz);
25688 for (const trait of record.traits) {
25689 if (trait.state !== TraitState.Resolved || trait.handler.updateResources === undefined) {
25690 continue;
25691 }
25692 trait.handler.updateResources(clazz, trait.analysis, trait.resolution);
25693 }
25694 }
25695 compile(clazz, constantPool) {
25696 const original = ts$1.getOriginalNode(clazz);
25697 if (!this.reflector.isClass(clazz) || !this.reflector.isClass(original) ||
25698 !this.classes.has(original)) {
25699 return null;
25700 }
25701 const record = this.classes.get(original);
25702 let res = [];
25703 for (const trait of record.traits) {
25704 if (trait.state !== TraitState.Resolved || trait.analysisDiagnostics !== null ||
25705 trait.resolveDiagnostics !== null) {
25706 // Cannot compile a trait that is not resolved, or had any errors in its declaration.
25707 continue;
25708 }
25709 const compileSpan = this.perf.start('compileClass', original);
25710 // `trait.resolution` is non-null asserted here because TypeScript does not recognize that
25711 // `Readonly<unknown>` is nullable (as `unknown` itself is nullable) due to the way that
25712 // `Readonly` works.
25713 let compileRes;
25714 if (this.compilationMode === CompilationMode.PARTIAL &&
25715 trait.handler.compilePartial !== undefined) {
25716 compileRes = trait.handler.compilePartial(clazz, trait.analysis, trait.resolution);
25717 }
25718 else {
25719 compileRes =
25720 trait.handler.compileFull(clazz, trait.analysis, trait.resolution, constantPool);
25721 }
25722 const compileMatchRes = compileRes;
25723 this.perf.stop(compileSpan);
25724 if (Array.isArray(compileMatchRes)) {
25725 for (const result of compileMatchRes) {
25726 if (!res.some(r => r.name === result.name)) {
25727 res.push(result);
25728 }
25729 }
25730 }
25731 else if (!res.some(result => result.name === compileMatchRes.name)) {
25732 res.push(compileMatchRes);
25733 }
25734 }
25735 // Look up the .d.ts transformer for the input file and record that at least one field was
25736 // generated, which will allow the .d.ts to be transformed later.
25737 this.dtsTransforms.getIvyDeclarationTransform(original.getSourceFile())
25738 .addFields(original, res);
25739 // Return the instruction to the transformer so the fields will be added.
25740 return res.length > 0 ? res : null;
25741 }
25742 decoratorsFor(node) {
25743 const original = ts$1.getOriginalNode(node);
25744 if (!this.reflector.isClass(original) || !this.classes.has(original)) {
25745 return [];
25746 }
25747 const record = this.classes.get(original);
25748 const decorators = [];
25749 for (const trait of record.traits) {
25750 if (trait.state !== TraitState.Resolved) {
25751 continue;
25752 }
25753 if (trait.detected.trigger !== null && ts$1.isDecorator(trait.detected.trigger)) {
25754 decorators.push(trait.detected.trigger);
25755 }
25756 }
25757 return decorators;
25758 }
25759 get diagnostics() {
25760 const diagnostics = [];
25761 for (const clazz of this.classes.keys()) {
25762 const record = this.classes.get(clazz);
25763 if (record.metaDiagnostics !== null) {
25764 diagnostics.push(...record.metaDiagnostics);
25765 }
25766 for (const trait of record.traits) {
25767 if ((trait.state === TraitState.Analyzed || trait.state === TraitState.Resolved) &&
25768 trait.analysisDiagnostics !== null) {
25769 diagnostics.push(...trait.analysisDiagnostics);
25770 }
25771 if (trait.state === TraitState.Resolved && trait.resolveDiagnostics !== null) {
25772 diagnostics.push(...trait.resolveDiagnostics);
25773 }
25774 }
25775 }
25776 return diagnostics;
25777 }
25778 get exportStatements() {
25779 return this.reexportMap;
25780 }
25781 }
25782
25783 /**
25784 * @license
25785 * Copyright Google LLC All Rights Reserved.
25786 *
25787 * Use of this source code is governed by an MIT-style license that can be
25788 * found in the LICENSE file at https://angular.io/license
25789 */
25790 /**
25791 * The current context of a translator visitor as it traverses the AST tree.
25792 *
25793 * It tracks whether we are in the process of outputting a statement or an expression.
25794 */
25795 class Context {
25796 constructor(isStatement) {
25797 this.isStatement = isStatement;
25798 }
25799 get withExpressionMode() {
25800 return this.isStatement ? new Context(false) : this;
25801 }
25802 get withStatementMode() {
25803 return !this.isStatement ? new Context(true) : this;
25804 }
25805 }
25806
25807 /**
25808 * @license
25809 * Copyright Google LLC All Rights Reserved.
25810 *
25811 * Use of this source code is governed by an MIT-style license that can be
25812 * found in the LICENSE file at https://angular.io/license
25813 */
25814 class ImportManager {
25815 constructor(rewriter = new NoopImportRewriter(), prefix = 'i') {
25816 this.rewriter = rewriter;
25817 this.prefix = prefix;
25818 this.specifierToIdentifier = new Map();
25819 this.nextIndex = 0;
25820 }
25821 generateNamespaceImport(moduleName) {
25822 if (!this.specifierToIdentifier.has(moduleName)) {
25823 this.specifierToIdentifier.set(moduleName, ts$1.createIdentifier(`${this.prefix}${this.nextIndex++}`));
25824 }
25825 return this.specifierToIdentifier.get(moduleName);
25826 }
25827 generateNamedImport(moduleName, originalSymbol) {
25828 // First, rewrite the symbol name.
25829 const symbol = this.rewriter.rewriteSymbol(originalSymbol, moduleName);
25830 // Ask the rewriter if this symbol should be imported at all. If not, it can be referenced
25831 // directly (moduleImport: null).
25832 if (!this.rewriter.shouldImportSymbol(symbol, moduleName)) {
25833 // The symbol should be referenced directly.
25834 return { moduleImport: null, symbol };
25835 }
25836 // If not, this symbol will be imported using a generated namespace import.
25837 const moduleImport = this.generateNamespaceImport(moduleName);
25838 return { moduleImport, symbol };
25839 }
25840 getAllImports(contextPath) {
25841 const imports = [];
25842 for (const [originalSpecifier, qualifier] of this.specifierToIdentifier) {
25843 const specifier = this.rewriter.rewriteSpecifier(originalSpecifier, contextPath);
25844 imports.push({
25845 specifier,
25846 qualifier,
25847 });
25848 }
25849 return imports;
25850 }
25851 }
25852
25853 /**
25854 * @license
25855 * Copyright Google LLC All Rights Reserved.
25856 *
25857 * Use of this source code is governed by an MIT-style license that can be
25858 * found in the LICENSE file at https://angular.io/license
25859 */
25860 const UNARY_OPERATORS$1 = new Map([
25861 [UnaryOperator.Minus, '-'],
25862 [UnaryOperator.Plus, '+'],
25863 ]);
25864 const BINARY_OPERATORS$1 = new Map([
25865 [BinaryOperator.And, '&&'],
25866 [BinaryOperator.Bigger, '>'],
25867 [BinaryOperator.BiggerEquals, '>='],
25868 [BinaryOperator.BitwiseAnd, '&'],
25869 [BinaryOperator.Divide, '/'],
25870 [BinaryOperator.Equals, '=='],
25871 [BinaryOperator.Identical, '==='],
25872 [BinaryOperator.Lower, '<'],
25873 [BinaryOperator.LowerEquals, '<='],
25874 [BinaryOperator.Minus, '-'],
25875 [BinaryOperator.Modulo, '%'],
25876 [BinaryOperator.Multiply, '*'],
25877 [BinaryOperator.NotEquals, '!='],
25878 [BinaryOperator.NotIdentical, '!=='],
25879 [BinaryOperator.Or, '||'],
25880 [BinaryOperator.Plus, '+'],
25881 ]);
25882 class ExpressionTranslatorVisitor {
25883 constructor(factory, imports, options) {
25884 this.factory = factory;
25885 this.imports = imports;
25886 this.downlevelTaggedTemplates = options.downlevelTaggedTemplates === true;
25887 this.downlevelVariableDeclarations = options.downlevelVariableDeclarations === true;
25888 this.recordWrappedNodeExpr = options.recordWrappedNodeExpr || (() => { });
25889 }
25890 visitDeclareVarStmt(stmt, context) {
25891 var _a;
25892 const varType = this.downlevelVariableDeclarations ?
25893 'var' :
25894 stmt.hasModifier(StmtModifier.Final) ? 'const' : 'let';
25895 return this.attachComments(this.factory.createVariableDeclaration(stmt.name, (_a = stmt.value) === null || _a === void 0 ? void 0 : _a.visitExpression(this, context.withExpressionMode), varType), stmt.leadingComments);
25896 }
25897 visitDeclareFunctionStmt(stmt, context) {
25898 return this.attachComments(this.factory.createFunctionDeclaration(stmt.name, stmt.params.map(param => param.name), this.factory.createBlock(this.visitStatements(stmt.statements, context.withStatementMode))), stmt.leadingComments);
25899 }
25900 visitExpressionStmt(stmt, context) {
25901 return this.attachComments(this.factory.createExpressionStatement(stmt.expr.visitExpression(this, context.withStatementMode)), stmt.leadingComments);
25902 }
25903 visitReturnStmt(stmt, context) {
25904 return this.attachComments(this.factory.createReturnStatement(stmt.value.visitExpression(this, context.withExpressionMode)), stmt.leadingComments);
25905 }
25906 visitDeclareClassStmt(_stmt, _context) {
25907 throw new Error('Method not implemented.');
25908 }
25909 visitIfStmt(stmt, context) {
25910 return this.attachComments(this.factory.createIfStatement(stmt.condition.visitExpression(this, context), this.factory.createBlock(this.visitStatements(stmt.trueCase, context.withStatementMode)), stmt.falseCase.length > 0 ? this.factory.createBlock(this.visitStatements(stmt.falseCase, context.withStatementMode)) :
25911 null), stmt.leadingComments);
25912 }
25913 visitTryCatchStmt(_stmt, _context) {
25914 throw new Error('Method not implemented.');
25915 }
25916 visitThrowStmt(stmt, context) {
25917 return this.attachComments(this.factory.createThrowStatement(stmt.error.visitExpression(this, context.withExpressionMode)), stmt.leadingComments);
25918 }
25919 visitReadVarExpr(ast, _context) {
25920 const identifier = this.factory.createIdentifier(ast.name);
25921 this.setSourceMapRange(identifier, ast.sourceSpan);
25922 return identifier;
25923 }
25924 visitWriteVarExpr(expr, context) {
25925 const assignment = this.factory.createAssignment(this.setSourceMapRange(this.factory.createIdentifier(expr.name), expr.sourceSpan), expr.value.visitExpression(this, context));
25926 return context.isStatement ? assignment :
25927 this.factory.createParenthesizedExpression(assignment);
25928 }
25929 visitWriteKeyExpr(expr, context) {
25930 const exprContext = context.withExpressionMode;
25931 const target = this.factory.createElementAccess(expr.receiver.visitExpression(this, exprContext), expr.index.visitExpression(this, exprContext));
25932 const assignment = this.factory.createAssignment(target, expr.value.visitExpression(this, exprContext));
25933 return context.isStatement ? assignment :
25934 this.factory.createParenthesizedExpression(assignment);
25935 }
25936 visitWritePropExpr(expr, context) {
25937 const target = this.factory.createPropertyAccess(expr.receiver.visitExpression(this, context), expr.name);
25938 return this.factory.createAssignment(target, expr.value.visitExpression(this, context));
25939 }
25940 visitInvokeMethodExpr(ast, context) {
25941 const target = ast.receiver.visitExpression(this, context);
25942 return this.setSourceMapRange(this.factory.createCallExpression(ast.name !== null ? this.factory.createPropertyAccess(target, ast.name) : target, ast.args.map(arg => arg.visitExpression(this, context)),
25943 /* pure */ false), ast.sourceSpan);
25944 }
25945 visitInvokeFunctionExpr(ast, context) {
25946 return this.setSourceMapRange(this.factory.createCallExpression(ast.fn.visitExpression(this, context), ast.args.map(arg => arg.visitExpression(this, context)), ast.pure), ast.sourceSpan);
25947 }
25948 visitTaggedTemplateExpr(ast, context) {
25949 return this.setSourceMapRange(this.createTaggedTemplateExpression(ast.tag.visitExpression(this, context), {
25950 elements: ast.template.elements.map(e => {
25951 var _a;
25952 return createTemplateElement({
25953 cooked: e.text,
25954 raw: e.rawText,
25955 range: (_a = e.sourceSpan) !== null && _a !== void 0 ? _a : ast.sourceSpan,
25956 });
25957 }),
25958 expressions: ast.template.expressions.map(e => e.visitExpression(this, context))
25959 }), ast.sourceSpan);
25960 }
25961 visitInstantiateExpr(ast, context) {
25962 return this.factory.createNewExpression(ast.classExpr.visitExpression(this, context), ast.args.map(arg => arg.visitExpression(this, context)));
25963 }
25964 visitLiteralExpr(ast, _context) {
25965 return this.setSourceMapRange(this.factory.createLiteral(ast.value), ast.sourceSpan);
25966 }
25967 visitLocalizedString(ast, context) {
25968 // A `$localize` message consists of `messageParts` and `expressions`, which get interleaved
25969 // together. The interleaved pieces look like:
25970 // `[messagePart0, expression0, messagePart1, expression1, messagePart2]`
25971 //
25972 // Note that there is always a message part at the start and end, and so therefore
25973 // `messageParts.length === expressions.length + 1`.
25974 //
25975 // Each message part may be prefixed with "metadata", which is wrapped in colons (:) delimiters.
25976 // The metadata is attached to the first and subsequent message parts by calls to
25977 // `serializeI18nHead()` and `serializeI18nTemplatePart()` respectively.
25978 //
25979 // The first message part (i.e. `ast.messageParts[0]`) is used to initialize `messageParts`
25980 // array.
25981 const elements = [createTemplateElement(ast.serializeI18nHead())];
25982 const expressions = [];
25983 for (let i = 0; i < ast.expressions.length; i++) {
25984 const placeholder = this.setSourceMapRange(ast.expressions[i].visitExpression(this, context), ast.getPlaceholderSourceSpan(i));
25985 expressions.push(placeholder);
25986 elements.push(createTemplateElement(ast.serializeI18nTemplatePart(i + 1)));
25987 }
25988 const localizeTag = this.factory.createIdentifier('$localize');
25989 return this.setSourceMapRange(this.createTaggedTemplateExpression(localizeTag, { elements, expressions }), ast.sourceSpan);
25990 }
25991 createTaggedTemplateExpression(tag, template) {
25992 return this.downlevelTaggedTemplates ? this.createES5TaggedTemplateFunctionCall(tag, template) :
25993 this.factory.createTaggedTemplate(tag, template);
25994 }
25995 /**
25996 * Translate the tagged template literal into a call that is compatible with ES5, using the
25997 * imported `__makeTemplateObject` helper for ES5 formatted output.
25998 */
25999 createES5TaggedTemplateFunctionCall(tagHandler, { elements, expressions }) {
26000 // Ensure that the `__makeTemplateObject()` helper has been imported.
26001 const { moduleImport, symbol } = this.imports.generateNamedImport('tslib', '__makeTemplateObject');
26002 const __makeTemplateObjectHelper = (moduleImport === null) ?
26003 this.factory.createIdentifier(symbol) :
26004 this.factory.createPropertyAccess(moduleImport, symbol);
26005 // Collect up the cooked and raw strings into two separate arrays.
26006 const cooked = [];
26007 const raw = [];
26008 for (const element of elements) {
26009 cooked.push(this.factory.setSourceMapRange(this.factory.createLiteral(element.cooked), element.range));
26010 raw.push(this.factory.setSourceMapRange(this.factory.createLiteral(element.raw), element.range));
26011 }
26012 // Generate the helper call in the form: `__makeTemplateObject([cooked], [raw]);`
26013 const templateHelperCall = this.factory.createCallExpression(__makeTemplateObjectHelper, [this.factory.createArrayLiteral(cooked), this.factory.createArrayLiteral(raw)],
26014 /* pure */ false);
26015 // Finally create the tagged handler call in the form:
26016 // `tag(__makeTemplateObject([cooked], [raw]), ...expressions);`
26017 return this.factory.createCallExpression(tagHandler, [templateHelperCall, ...expressions],
26018 /* pure */ false);
26019 }
26020 visitExternalExpr(ast, _context) {
26021 if (ast.value.name === null) {
26022 if (ast.value.moduleName === null) {
26023 throw new Error('Invalid import without name nor moduleName');
26024 }
26025 return this.imports.generateNamespaceImport(ast.value.moduleName);
26026 }
26027 // If a moduleName is specified, this is a normal import. If there's no module name, it's a
26028 // reference to a global/ambient symbol.
26029 if (ast.value.moduleName !== null) {
26030 // This is a normal import. Find the imported module.
26031 const { moduleImport, symbol } = this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
26032 if (moduleImport === null) {
26033 // The symbol was ambient after all.
26034 return this.factory.createIdentifier(symbol);
26035 }
26036 else {
26037 return this.factory.createPropertyAccess(moduleImport, symbol);
26038 }
26039 }
26040 else {
26041 // The symbol is ambient, so just reference it.
26042 return this.factory.createIdentifier(ast.value.name);
26043 }
26044 }
26045 visitConditionalExpr(ast, context) {
26046 let cond = ast.condition.visitExpression(this, context);
26047 // Ordinarily the ternary operator is right-associative. The following are equivalent:
26048 // `a ? b : c ? d : e` => `a ? b : (c ? d : e)`
26049 //
26050 // However, occasionally Angular needs to produce a left-associative conditional, such as in
26051 // the case of a null-safe navigation production: `{{a?.b ? c : d}}`. This template produces
26052 // a ternary of the form:
26053 // `a == null ? null : rest of expression`
26054 // If the rest of the expression is also a ternary though, this would produce the form:
26055 // `a == null ? null : a.b ? c : d`
26056 // which, if left as right-associative, would be incorrectly associated as:
26057 // `a == null ? null : (a.b ? c : d)`
26058 //
26059 // In such cases, the left-associativity needs to be enforced with parentheses:
26060 // `(a == null ? null : a.b) ? c : d`
26061 //
26062 // Such parentheses could always be included in the condition (guaranteeing correct behavior) in
26063 // all cases, but this has a code size cost. Instead, parentheses are added only when a
26064 // conditional expression is directly used as the condition of another.
26065 //
26066 // TODO(alxhub): investigate better logic for precendence of conditional operators
26067 if (ast.condition instanceof ConditionalExpr) {
26068 // The condition of this ternary needs to be wrapped in parentheses to maintain
26069 // left-associativity.
26070 cond = this.factory.createParenthesizedExpression(cond);
26071 }
26072 return this.factory.createConditional(cond, ast.trueCase.visitExpression(this, context), ast.falseCase.visitExpression(this, context));
26073 }
26074 visitNotExpr(ast, context) {
26075 return this.factory.createUnaryExpression('!', ast.condition.visitExpression(this, context));
26076 }
26077 visitAssertNotNullExpr(ast, context) {
26078 return ast.condition.visitExpression(this, context);
26079 }
26080 visitCastExpr(ast, context) {
26081 return ast.value.visitExpression(this, context);
26082 }
26083 visitFunctionExpr(ast, context) {
26084 var _a;
26085 return this.factory.createFunctionExpression((_a = ast.name) !== null && _a !== void 0 ? _a : null, ast.params.map(param => param.name), this.factory.createBlock(this.visitStatements(ast.statements, context)));
26086 }
26087 visitBinaryOperatorExpr(ast, context) {
26088 if (!BINARY_OPERATORS$1.has(ast.operator)) {
26089 throw new Error(`Unknown binary operator: ${BinaryOperator[ast.operator]}`);
26090 }
26091 return this.factory.createBinaryExpression(ast.lhs.visitExpression(this, context), BINARY_OPERATORS$1.get(ast.operator), ast.rhs.visitExpression(this, context));
26092 }
26093 visitReadPropExpr(ast, context) {
26094 return this.factory.createPropertyAccess(ast.receiver.visitExpression(this, context), ast.name);
26095 }
26096 visitReadKeyExpr(ast, context) {
26097 return this.factory.createElementAccess(ast.receiver.visitExpression(this, context), ast.index.visitExpression(this, context));
26098 }
26099 visitLiteralArrayExpr(ast, context) {
26100 return this.factory.createArrayLiteral(ast.entries.map(expr => this.setSourceMapRange(expr.visitExpression(this, context), ast.sourceSpan)));
26101 }
26102 visitLiteralMapExpr(ast, context) {
26103 const properties = ast.entries.map(entry => {
26104 return {
26105 propertyName: entry.key,
26106 quoted: entry.quoted,
26107 value: entry.value.visitExpression(this, context)
26108 };
26109 });
26110 return this.setSourceMapRange(this.factory.createObjectLiteral(properties), ast.sourceSpan);
26111 }
26112 visitCommaExpr(ast, context) {
26113 throw new Error('Method not implemented.');
26114 }
26115 visitWrappedNodeExpr(ast, _context) {
26116 this.recordWrappedNodeExpr(ast.node);
26117 return ast.node;
26118 }
26119 visitTypeofExpr(ast, context) {
26120 return this.factory.createTypeOfExpression(ast.expr.visitExpression(this, context));
26121 }
26122 visitUnaryOperatorExpr(ast, context) {
26123 if (!UNARY_OPERATORS$1.has(ast.operator)) {
26124 throw new Error(`Unknown unary operator: ${UnaryOperator[ast.operator]}`);
26125 }
26126 return this.factory.createUnaryExpression(UNARY_OPERATORS$1.get(ast.operator), ast.expr.visitExpression(this, context));
26127 }
26128 visitStatements(statements, context) {
26129 return statements.map(stmt => stmt.visitStatement(this, context))
26130 .filter(stmt => stmt !== undefined);
26131 }
26132 setSourceMapRange(ast, span) {
26133 return this.factory.setSourceMapRange(ast, createRange(span));
26134 }
26135 attachComments(statement, leadingComments) {
26136 if (leadingComments !== undefined) {
26137 this.factory.attachComments(statement, leadingComments);
26138 }
26139 return statement;
26140 }
26141 }
26142 /**
26143 * Convert a cooked-raw string object into one that can be used by the AST factories.
26144 */
26145 function createTemplateElement({ cooked, raw, range }) {
26146 return { cooked, raw, range: createRange(range) };
26147 }
26148 /**
26149 * Convert an OutputAST source-span into a range that can be used by the AST factories.
26150 */
26151 function createRange(span) {
26152 if (span === null) {
26153 return null;
26154 }
26155 const { start, end } = span;
26156 const { url, content } = start.file;
26157 if (!url) {
26158 return null;
26159 }
26160 return {
26161 url,
26162 content,
26163 start: { offset: start.offset, line: start.line, column: start.col },
26164 end: { offset: end.offset, line: end.line, column: end.col },
26165 };
26166 }
26167
26168 /**
26169 * @license
26170 * Copyright Google LLC All Rights Reserved.
26171 *
26172 * Use of this source code is governed by an MIT-style license that can be
26173 * found in the LICENSE file at https://angular.io/license
26174 */
26175 function translateType(type, imports) {
26176 return type.visitType(new TypeTranslatorVisitor(imports), new Context(false));
26177 }
26178 class TypeTranslatorVisitor {
26179 constructor(imports) {
26180 this.imports = imports;
26181 }
26182 visitBuiltinType(type, context) {
26183 switch (type.name) {
26184 case BuiltinTypeName.Bool:
26185 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.BooleanKeyword);
26186 case BuiltinTypeName.Dynamic:
26187 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword);
26188 case BuiltinTypeName.Int:
26189 case BuiltinTypeName.Number:
26190 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.NumberKeyword);
26191 case BuiltinTypeName.String:
26192 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.StringKeyword);
26193 case BuiltinTypeName.None:
26194 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.NeverKeyword);
26195 default:
26196 throw new Error(`Unsupported builtin type: ${BuiltinTypeName[type.name]}`);
26197 }
26198 }
26199 visitExpressionType(type, context) {
26200 const typeNode = this.translateExpression(type.value, context);
26201 if (type.typeParams === null) {
26202 return typeNode;
26203 }
26204 if (!ts$1.isTypeReferenceNode(typeNode)) {
26205 throw new Error('An ExpressionType with type arguments must translate into a TypeReferenceNode');
26206 }
26207 else if (typeNode.typeArguments !== undefined) {
26208 throw new Error(`An ExpressionType with type arguments cannot have multiple levels of type arguments`);
26209 }
26210 const typeArgs = type.typeParams.map(param => this.translateType(param, context));
26211 return ts$1.createTypeReferenceNode(typeNode.typeName, typeArgs);
26212 }
26213 visitArrayType(type, context) {
26214 return ts$1.createArrayTypeNode(this.translateType(type.of, context));
26215 }
26216 visitMapType(type, context) {
26217 const parameter = ts$1.createParameter(undefined, undefined, undefined, 'key', undefined, ts$1.createKeywordTypeNode(ts$1.SyntaxKind.StringKeyword));
26218 const typeArgs = type.valueType !== null ?
26219 this.translateType(type.valueType, context) :
26220 ts$1.createKeywordTypeNode(ts$1.SyntaxKind.UnknownKeyword);
26221 const indexSignature = ts$1.createIndexSignature(undefined, undefined, [parameter], typeArgs);
26222 return ts$1.createTypeLiteralNode([indexSignature]);
26223 }
26224 visitReadVarExpr(ast, context) {
26225 if (ast.name === null) {
26226 throw new Error(`ReadVarExpr with no variable name in type`);
26227 }
26228 return ts$1.createTypeQueryNode(ts$1.createIdentifier(ast.name));
26229 }
26230 visitWriteVarExpr(expr, context) {
26231 throw new Error('Method not implemented.');
26232 }
26233 visitWriteKeyExpr(expr, context) {
26234 throw new Error('Method not implemented.');
26235 }
26236 visitWritePropExpr(expr, context) {
26237 throw new Error('Method not implemented.');
26238 }
26239 visitInvokeMethodExpr(ast, context) {
26240 throw new Error('Method not implemented.');
26241 }
26242 visitInvokeFunctionExpr(ast, context) {
26243 throw new Error('Method not implemented.');
26244 }
26245 visitTaggedTemplateExpr(ast, context) {
26246 throw new Error('Method not implemented.');
26247 }
26248 visitInstantiateExpr(ast, context) {
26249 throw new Error('Method not implemented.');
26250 }
26251 visitLiteralExpr(ast, context) {
26252 if (ast.value === null) {
26253 return ts$1.createLiteralTypeNode(ts$1.createNull());
26254 }
26255 else if (ast.value === undefined) {
26256 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.UndefinedKeyword);
26257 }
26258 else if (typeof ast.value === 'boolean') {
26259 return ts$1.createLiteralTypeNode(ts$1.createLiteral(ast.value));
26260 }
26261 else if (typeof ast.value === 'number') {
26262 return ts$1.createLiteralTypeNode(ts$1.createLiteral(ast.value));
26263 }
26264 else {
26265 return ts$1.createLiteralTypeNode(ts$1.createLiteral(ast.value));
26266 }
26267 }
26268 visitLocalizedString(ast, context) {
26269 throw new Error('Method not implemented.');
26270 }
26271 visitExternalExpr(ast, context) {
26272 if (ast.value.moduleName === null || ast.value.name === null) {
26273 throw new Error(`Import unknown module or symbol`);
26274 }
26275 const { moduleImport, symbol } = this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
26276 const symbolIdentifier = ts$1.createIdentifier(symbol);
26277 const typeName = moduleImport ? ts$1.createQualifiedName(moduleImport, symbolIdentifier) : symbolIdentifier;
26278 const typeArguments = ast.typeParams !== null ?
26279 ast.typeParams.map(type => this.translateType(type, context)) :
26280 undefined;
26281 return ts$1.createTypeReferenceNode(typeName, typeArguments);
26282 }
26283 visitConditionalExpr(ast, context) {
26284 throw new Error('Method not implemented.');
26285 }
26286 visitNotExpr(ast, context) {
26287 throw new Error('Method not implemented.');
26288 }
26289 visitAssertNotNullExpr(ast, context) {
26290 throw new Error('Method not implemented.');
26291 }
26292 visitCastExpr(ast, context) {
26293 throw new Error('Method not implemented.');
26294 }
26295 visitFunctionExpr(ast, context) {
26296 throw new Error('Method not implemented.');
26297 }
26298 visitUnaryOperatorExpr(ast, context) {
26299 throw new Error('Method not implemented.');
26300 }
26301 visitBinaryOperatorExpr(ast, context) {
26302 throw new Error('Method not implemented.');
26303 }
26304 visitReadPropExpr(ast, context) {
26305 throw new Error('Method not implemented.');
26306 }
26307 visitReadKeyExpr(ast, context) {
26308 throw new Error('Method not implemented.');
26309 }
26310 visitLiteralArrayExpr(ast, context) {
26311 const values = ast.entries.map(expr => this.translateExpression(expr, context));
26312 return ts$1.createTupleTypeNode(values);
26313 }
26314 visitLiteralMapExpr(ast, context) {
26315 const entries = ast.entries.map(entry => {
26316 const { key, quoted } = entry;
26317 const type = this.translateExpression(entry.value, context);
26318 return ts$1.createPropertySignature(
26319 /* modifiers */ undefined,
26320 /* name */ quoted ? ts$1.createStringLiteral(key) : key,
26321 /* questionToken */ undefined,
26322 /* type */ type,
26323 /* initializer */ undefined);
26324 });
26325 return ts$1.createTypeLiteralNode(entries);
26326 }
26327 visitCommaExpr(ast, context) {
26328 throw new Error('Method not implemented.');
26329 }
26330 visitWrappedNodeExpr(ast, context) {
26331 const node = ast.node;
26332 if (ts$1.isEntityName(node)) {
26333 return ts$1.createTypeReferenceNode(node, /* typeArguments */ undefined);
26334 }
26335 else if (ts$1.isTypeNode(node)) {
26336 return node;
26337 }
26338 else if (ts$1.isLiteralExpression(node)) {
26339 return ts$1.createLiteralTypeNode(node);
26340 }
26341 else {
26342 throw new Error(`Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts$1.SyntaxKind[node.kind]}`);
26343 }
26344 }
26345 visitTypeofExpr(ast, context) {
26346 const typeNode = this.translateExpression(ast.expr, context);
26347 if (!ts$1.isTypeReferenceNode(typeNode)) {
26348 throw new Error(`The target of a typeof expression must be a type reference, but it was
26349 ${ts$1.SyntaxKind[typeNode.kind]}`);
26350 }
26351 return ts$1.createTypeQueryNode(typeNode.typeName);
26352 }
26353 translateType(type, context) {
26354 const typeNode = type.visitType(this, context);
26355 if (!ts$1.isTypeNode(typeNode)) {
26356 throw new Error(`A Type must translate to a TypeNode, but was ${ts$1.SyntaxKind[typeNode.kind]}`);
26357 }
26358 return typeNode;
26359 }
26360 translateExpression(expr, context) {
26361 const typeNode = expr.visitExpression(this, context);
26362 if (!ts$1.isTypeNode(typeNode)) {
26363 throw new Error(`An Expression must translate to a TypeNode, but was ${ts$1.SyntaxKind[typeNode.kind]}`);
26364 }
26365 return typeNode;
26366 }
26367 }
26368
26369 /**
26370 * @license
26371 * Copyright Google LLC All Rights Reserved.
26372 *
26373 * Use of this source code is governed by an MIT-style license that can be
26374 * found in the LICENSE file at https://angular.io/license
26375 */
26376 const UNARY_OPERATORS$2 = {
26377 '+': ts$1.SyntaxKind.PlusToken,
26378 '-': ts$1.SyntaxKind.MinusToken,
26379 '!': ts$1.SyntaxKind.ExclamationToken,
26380 };
26381 const BINARY_OPERATORS$2 = {
26382 '&&': ts$1.SyntaxKind.AmpersandAmpersandToken,
26383 '>': ts$1.SyntaxKind.GreaterThanToken,
26384 '>=': ts$1.SyntaxKind.GreaterThanEqualsToken,
26385 '&': ts$1.SyntaxKind.AmpersandToken,
26386 '/': ts$1.SyntaxKind.SlashToken,
26387 '==': ts$1.SyntaxKind.EqualsEqualsToken,
26388 '===': ts$1.SyntaxKind.EqualsEqualsEqualsToken,
26389 '<': ts$1.SyntaxKind.LessThanToken,
26390 '<=': ts$1.SyntaxKind.LessThanEqualsToken,
26391 '-': ts$1.SyntaxKind.MinusToken,
26392 '%': ts$1.SyntaxKind.PercentToken,
26393 '*': ts$1.SyntaxKind.AsteriskToken,
26394 '!=': ts$1.SyntaxKind.ExclamationEqualsToken,
26395 '!==': ts$1.SyntaxKind.ExclamationEqualsEqualsToken,
26396 '||': ts$1.SyntaxKind.BarBarToken,
26397 '+': ts$1.SyntaxKind.PlusToken,
26398 };
26399 const VAR_TYPES = {
26400 'const': ts$1.NodeFlags.Const,
26401 'let': ts$1.NodeFlags.Let,
26402 'var': ts$1.NodeFlags.None,
26403 };
26404 /**
26405 * A TypeScript flavoured implementation of the AstFactory.
26406 */
26407 class TypeScriptAstFactory {
26408 constructor() {
26409 this.externalSourceFiles = new Map();
26410 this.attachComments = attachComments;
26411 this.createArrayLiteral = ts$1.createArrayLiteral;
26412 this.createConditional = ts$1.createConditional;
26413 this.createElementAccess = ts$1.createElementAccess;
26414 this.createExpressionStatement = ts$1.createExpressionStatement;
26415 this.createIdentifier = ts$1.createIdentifier;
26416 this.createParenthesizedExpression = ts$1.createParen;
26417 this.createPropertyAccess = ts$1.createPropertyAccess;
26418 this.createThrowStatement = ts$1.createThrow;
26419 this.createTypeOfExpression = ts$1.createTypeOf;
26420 }
26421 createAssignment(target, value) {
26422 return ts$1.createBinary(target, ts$1.SyntaxKind.EqualsToken, value);
26423 }
26424 createBinaryExpression(leftOperand, operator, rightOperand) {
26425 return ts$1.createBinary(leftOperand, BINARY_OPERATORS$2[operator], rightOperand);
26426 }
26427 createBlock(body) {
26428 return ts$1.createBlock(body);
26429 }
26430 createCallExpression(callee, args, pure) {
26431 const call = ts$1.createCall(callee, undefined, args);
26432 if (pure) {
26433 ts$1.addSyntheticLeadingComment(call, ts$1.SyntaxKind.MultiLineCommentTrivia, '@__PURE__', /* trailing newline */ false);
26434 }
26435 return call;
26436 }
26437 createFunctionDeclaration(functionName, parameters, body) {
26438 if (!ts$1.isBlock(body)) {
26439 throw new Error(`Invalid syntax, expected a block, but got ${ts$1.SyntaxKind[body.kind]}.`);
26440 }
26441 return ts$1.createFunctionDeclaration(undefined, undefined, undefined, functionName, undefined, parameters.map(param => ts$1.createParameter(undefined, undefined, undefined, param)), undefined, body);
26442 }
26443 createFunctionExpression(functionName, parameters, body) {
26444 if (!ts$1.isBlock(body)) {
26445 throw new Error(`Invalid syntax, expected a block, but got ${ts$1.SyntaxKind[body.kind]}.`);
26446 }
26447 return ts$1.createFunctionExpression(undefined, undefined, functionName !== null && functionName !== void 0 ? functionName : undefined, undefined, parameters.map(param => ts$1.createParameter(undefined, undefined, undefined, param)), undefined, body);
26448 }
26449 createIfStatement(condition, thenStatement, elseStatement) {
26450 return ts$1.createIf(condition, thenStatement, elseStatement !== null && elseStatement !== void 0 ? elseStatement : undefined);
26451 }
26452 createLiteral(value) {
26453 if (value === undefined) {
26454 return ts$1.createIdentifier('undefined');
26455 }
26456 else if (value === null) {
26457 return ts$1.createNull();
26458 }
26459 else {
26460 return ts$1.createLiteral(value);
26461 }
26462 }
26463 createNewExpression(expression, args) {
26464 return ts$1.createNew(expression, undefined, args);
26465 }
26466 createObjectLiteral(properties) {
26467 return ts$1.createObjectLiteral(properties.map(prop => ts$1.createPropertyAssignment(prop.quoted ? ts$1.createLiteral(prop.propertyName) :
26468 ts$1.createIdentifier(prop.propertyName), prop.value)));
26469 }
26470 createReturnStatement(expression) {
26471 return ts$1.createReturn(expression !== null && expression !== void 0 ? expression : undefined);
26472 }
26473 createTaggedTemplate(tag, template) {
26474 let templateLiteral;
26475 const length = template.elements.length;
26476 const head = template.elements[0];
26477 if (length === 1) {
26478 templateLiteral = ts$1.createNoSubstitutionTemplateLiteral(head.cooked, head.raw);
26479 }
26480 else {
26481 const spans = [];
26482 // Create the middle parts
26483 for (let i = 1; i < length - 1; i++) {
26484 const { cooked, raw, range } = template.elements[i];
26485 const middle = createTemplateMiddle(cooked, raw);
26486 if (range !== null) {
26487 this.setSourceMapRange(middle, range);
26488 }
26489 spans.push(ts$1.createTemplateSpan(template.expressions[i - 1], middle));
26490 }
26491 // Create the tail part
26492 const resolvedExpression = template.expressions[length - 2];
26493 const templatePart = template.elements[length - 1];
26494 const templateTail = createTemplateTail(templatePart.cooked, templatePart.raw);
26495 if (templatePart.range !== null) {
26496 this.setSourceMapRange(templateTail, templatePart.range);
26497 }
26498 spans.push(ts$1.createTemplateSpan(resolvedExpression, templateTail));
26499 // Put it all together
26500 templateLiteral =
26501 ts$1.createTemplateExpression(ts$1.createTemplateHead(head.cooked, head.raw), spans);
26502 }
26503 if (head.range !== null) {
26504 this.setSourceMapRange(templateLiteral, head.range);
26505 }
26506 return ts$1.createTaggedTemplate(tag, templateLiteral);
26507 }
26508 createUnaryExpression(operator, operand) {
26509 return ts$1.createPrefix(UNARY_OPERATORS$2[operator], operand);
26510 }
26511 createVariableDeclaration(variableName, initializer, type) {
26512 return ts$1.createVariableStatement(undefined, ts$1.createVariableDeclarationList([ts$1.createVariableDeclaration(variableName, undefined, initializer !== null && initializer !== void 0 ? initializer : undefined)], VAR_TYPES[type]));
26513 }
26514 setSourceMapRange(node, sourceMapRange) {
26515 if (sourceMapRange === null) {
26516 return node;
26517 }
26518 const url = sourceMapRange.url;
26519 if (!this.externalSourceFiles.has(url)) {
26520 this.externalSourceFiles.set(url, ts$1.createSourceMapSource(url, sourceMapRange.content, pos => pos));
26521 }
26522 const source = this.externalSourceFiles.get(url);
26523 ts$1.setSourceMapRange(node, { pos: sourceMapRange.start.offset, end: sourceMapRange.end.offset, source });
26524 return node;
26525 }
26526 }
26527 // HACK: Use this in place of `ts.createTemplateMiddle()`.
26528 // Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed.
26529 function createTemplateMiddle(cooked, raw) {
26530 const node = ts$1.createTemplateHead(cooked, raw);
26531 node.kind = ts$1.SyntaxKind.TemplateMiddle;
26532 return node;
26533 }
26534 // HACK: Use this in place of `ts.createTemplateTail()`.
26535 // Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed.
26536 function createTemplateTail(cooked, raw) {
26537 const node = ts$1.createTemplateHead(cooked, raw);
26538 node.kind = ts$1.SyntaxKind.TemplateTail;
26539 return node;
26540 }
26541 /**
26542 * Attach the given `leadingComments` to the `statement` node.
26543 *
26544 * @param statement The statement that will have comments attached.
26545 * @param leadingComments The comments to attach to the statement.
26546 */
26547 function attachComments(statement, leadingComments) {
26548 for (const comment of leadingComments) {
26549 const commentKind = comment.multiline ? ts$1.SyntaxKind.MultiLineCommentTrivia :
26550 ts$1.SyntaxKind.SingleLineCommentTrivia;
26551 if (comment.multiline) {
26552 ts$1.addSyntheticLeadingComment(statement, commentKind, comment.toString(), comment.trailingNewline);
26553 }
26554 else {
26555 for (const line of comment.toString().split('\n')) {
26556 ts$1.addSyntheticLeadingComment(statement, commentKind, line, comment.trailingNewline);
26557 }
26558 }
26559 }
26560 }
26561
26562 /**
26563 * @license
26564 * Copyright Google LLC All Rights Reserved.
26565 *
26566 * Use of this source code is governed by an MIT-style license that can be
26567 * found in the LICENSE file at https://angular.io/license
26568 */
26569 function translateExpression(expression, imports, options = {}) {
26570 return expression.visitExpression(new ExpressionTranslatorVisitor(new TypeScriptAstFactory(), imports, options), new Context(false));
26571 }
26572 function translateStatement(statement, imports, options = {}) {
26573 return statement.visitStatement(new ExpressionTranslatorVisitor(new TypeScriptAstFactory(), imports, options), new Context(true));
26574 }
26575
26576 /**
26577 * @license
26578 * Copyright Google LLC All Rights Reserved.
26579 *
26580 * Use of this source code is governed by an MIT-style license that can be
26581 * found in the LICENSE file at https://angular.io/license
26582 */
26583 /**
26584 * Adds extra imports in the import manage for this source file, after the existing imports
26585 * and before the module body.
26586 * Can optionally add extra statements (e.g. new constants) before the body as well.
26587 */
26588 function addImports(importManager, sf, extraStatements = []) {
26589 // Generate the import statements to prepend.
26590 const addedImports = importManager.getAllImports(sf.fileName).map(i => {
26591 const qualifier = ts$1.createIdentifier(i.qualifier.text);
26592 const importClause = ts$1.createImportClause(
26593 /* name */ undefined,
26594 /* namedBindings */ ts$1.createNamespaceImport(qualifier));
26595 const decl = ts$1.createImportDeclaration(
26596 /* decorators */ undefined,
26597 /* modifiers */ undefined,
26598 /* importClause */ importClause,
26599 /* moduleSpecifier */ ts$1.createLiteral(i.specifier));
26600 // Set the qualifier's original TS node to the `ts.ImportDeclaration`. This allows downstream
26601 // transforms such as tsickle to properly process references to this import.
26602 //
26603 // This operation is load-bearing in g3 as some imported modules contain special metadata
26604 // generated by clutz, which tsickle uses to transform imports and references to those imports.
26605 //
26606 // TODO(alxhub): add a test for this when tsickle is updated externally to depend on this
26607 // behavior.
26608 ts$1.setOriginalNode(i.qualifier, decl);
26609 return decl;
26610 });
26611 // Filter out the existing imports and the source file body. All new statements
26612 // will be inserted between them.
26613 const existingImports = sf.statements.filter(stmt => isImportStatement(stmt));
26614 const body = sf.statements.filter(stmt => !isImportStatement(stmt));
26615 // Prepend imports if needed.
26616 if (addedImports.length > 0) {
26617 // If we prepend imports, we also prepend NotEmittedStatement to use it as an anchor
26618 // for @fileoverview Closure annotation. If there is no @fileoverview annotations, this
26619 // statement would be a noop.
26620 const fileoverviewAnchorStmt = ts$1.createNotEmittedStatement(sf);
26621 return ts$1.updateSourceFileNode(sf, ts$1.createNodeArray([
26622 fileoverviewAnchorStmt, ...existingImports, ...addedImports, ...extraStatements, ...body
26623 ]));
26624 }
26625 return sf;
26626 }
26627 function isImportStatement(stmt) {
26628 return ts$1.isImportDeclaration(stmt) || ts$1.isImportEqualsDeclaration(stmt) ||
26629 ts$1.isNamespaceImport(stmt);
26630 }
26631
26632 /**
26633 * @license
26634 * Copyright Google LLC All Rights Reserved.
26635 *
26636 * Use of this source code is governed by an MIT-style license that can be
26637 * found in the LICENSE file at https://angular.io/license
26638 */
26639 /**
26640 * Keeps track of `DtsTransform`s per source file, so that it is known which source files need to
26641 * have their declaration file transformed.
26642 */
26643 class DtsTransformRegistry {
26644 constructor() {
26645 this.ivyDeclarationTransforms = new Map();
26646 this.returnTypeTransforms = new Map();
26647 }
26648 getIvyDeclarationTransform(sf) {
26649 if (!this.ivyDeclarationTransforms.has(sf)) {
26650 this.ivyDeclarationTransforms.set(sf, new IvyDeclarationDtsTransform());
26651 }
26652 return this.ivyDeclarationTransforms.get(sf);
26653 }
26654 getReturnTypeTransform(sf) {
26655 if (!this.returnTypeTransforms.has(sf)) {
26656 this.returnTypeTransforms.set(sf, new ReturnTypeTransform());
26657 }
26658 return this.returnTypeTransforms.get(sf);
26659 }
26660 /**
26661 * Gets the dts transforms to be applied for the given source file, or `null` if no transform is
26662 * necessary.
26663 */
26664 getAllTransforms(sf) {
26665 // No need to transform if it's not a declarations file, or if no changes have been requested
26666 // to the input file. Due to the way TypeScript afterDeclarations transformers work, the
26667 // `ts.SourceFile` path is the same as the original .ts. The only way we know it's actually a
26668 // declaration file is via the `isDeclarationFile` property.
26669 if (!sf.isDeclarationFile) {
26670 return null;
26671 }
26672 const originalSf = ts$1.getOriginalNode(sf);
26673 let transforms = null;
26674 if (this.ivyDeclarationTransforms.has(originalSf)) {
26675 transforms = [];
26676 transforms.push(this.ivyDeclarationTransforms.get(originalSf));
26677 }
26678 if (this.returnTypeTransforms.has(originalSf)) {
26679 transforms = transforms || [];
26680 transforms.push(this.returnTypeTransforms.get(originalSf));
26681 }
26682 return transforms;
26683 }
26684 }
26685 function declarationTransformFactory(transformRegistry, importRewriter, importPrefix) {
26686 return (context) => {
26687 const transformer = new DtsTransformer(context, importRewriter, importPrefix);
26688 return (fileOrBundle) => {
26689 if (ts$1.isBundle(fileOrBundle)) {
26690 // Only attempt to transform source files.
26691 return fileOrBundle;
26692 }
26693 const transforms = transformRegistry.getAllTransforms(fileOrBundle);
26694 if (transforms === null) {
26695 return fileOrBundle;
26696 }
26697 return transformer.transform(fileOrBundle, transforms);
26698 };
26699 };
26700 }
26701 /**
26702 * Processes .d.ts file text and adds static field declarations, with types.
26703 */
26704 class DtsTransformer {
26705 constructor(ctx, importRewriter, importPrefix) {
26706 this.ctx = ctx;
26707 this.importRewriter = importRewriter;
26708 this.importPrefix = importPrefix;
26709 }
26710 /**
26711 * Transform the declaration file and add any declarations which were recorded.
26712 */
26713 transform(sf, transforms) {
26714 const imports = new ImportManager(this.importRewriter, this.importPrefix);
26715 const visitor = (node) => {
26716 if (ts$1.isClassDeclaration(node)) {
26717 return this.transformClassDeclaration(node, transforms, imports);
26718 }
26719 else if (ts$1.isFunctionDeclaration(node)) {
26720 return this.transformFunctionDeclaration(node, transforms, imports);
26721 }
26722 else {
26723 // Otherwise return node as is.
26724 return ts$1.visitEachChild(node, visitor, this.ctx);
26725 }
26726 };
26727 // Recursively scan through the AST and process all nodes as desired.
26728 sf = ts$1.visitNode(sf, visitor);
26729 // Add new imports for this file.
26730 return addImports(imports, sf);
26731 }
26732 transformClassDeclaration(clazz, transforms, imports) {
26733 let elements = clazz.members;
26734 let elementsChanged = false;
26735 for (const transform of transforms) {
26736 if (transform.transformClassElement !== undefined) {
26737 for (let i = 0; i < elements.length; i++) {
26738 const res = transform.transformClassElement(elements[i], imports);
26739 if (res !== elements[i]) {
26740 if (!elementsChanged) {
26741 elements = [...elements];
26742 elementsChanged = true;
26743 }
26744 elements[i] = res;
26745 }
26746 }
26747 }
26748 }
26749 let newClazz = clazz;
26750 for (const transform of transforms) {
26751 if (transform.transformClass !== undefined) {
26752 // If no DtsTransform has changed the class yet, then the (possibly mutated) elements have
26753 // not yet been incorporated. Otherwise, `newClazz.members` holds the latest class members.
26754 const inputMembers = (clazz === newClazz ? elements : newClazz.members);
26755 newClazz = transform.transformClass(newClazz, inputMembers, imports);
26756 }
26757 }
26758 // If some elements have been transformed but the class itself has not been transformed, create
26759 // an updated class declaration with the updated elements.
26760 if (elementsChanged && clazz === newClazz) {
26761 newClazz = ts$1.updateClassDeclaration(
26762 /* node */ clazz,
26763 /* decorators */ clazz.decorators,
26764 /* modifiers */ clazz.modifiers,
26765 /* name */ clazz.name,
26766 /* typeParameters */ clazz.typeParameters,
26767 /* heritageClauses */ clazz.heritageClauses,
26768 /* members */ elements);
26769 }
26770 return newClazz;
26771 }
26772 transformFunctionDeclaration(declaration, transforms, imports) {
26773 let newDecl = declaration;
26774 for (const transform of transforms) {
26775 if (transform.transformFunctionDeclaration !== undefined) {
26776 newDecl = transform.transformFunctionDeclaration(newDecl, imports);
26777 }
26778 }
26779 return newDecl;
26780 }
26781 }
26782 class IvyDeclarationDtsTransform {
26783 constructor() {
26784 this.declarationFields = new Map();
26785 }
26786 addFields(decl, fields) {
26787 this.declarationFields.set(decl, fields);
26788 }
26789 transformClass(clazz, members, imports) {
26790 const original = ts$1.getOriginalNode(clazz);
26791 if (!this.declarationFields.has(original)) {
26792 return clazz;
26793 }
26794 const fields = this.declarationFields.get(original);
26795 const newMembers = fields.map(decl => {
26796 const modifiers = [ts$1.createModifier(ts$1.SyntaxKind.StaticKeyword)];
26797 const typeRef = translateType(decl.type, imports);
26798 markForEmitAsSingleLine(typeRef);
26799 return ts$1.createProperty(
26800 /* decorators */ undefined,
26801 /* modifiers */ modifiers,
26802 /* name */ decl.name,
26803 /* questionOrExclamationToken */ undefined,
26804 /* type */ typeRef,
26805 /* initializer */ undefined);
26806 });
26807 return ts$1.updateClassDeclaration(
26808 /* node */ clazz,
26809 /* decorators */ clazz.decorators,
26810 /* modifiers */ clazz.modifiers,
26811 /* name */ clazz.name,
26812 /* typeParameters */ clazz.typeParameters,
26813 /* heritageClauses */ clazz.heritageClauses,
26814 /* members */ [...members, ...newMembers]);
26815 }
26816 }
26817 function markForEmitAsSingleLine(node) {
26818 ts$1.setEmitFlags(node, ts$1.EmitFlags.SingleLine);
26819 ts$1.forEachChild(node, markForEmitAsSingleLine);
26820 }
26821 class ReturnTypeTransform {
26822 constructor() {
26823 this.typeReplacements = new Map();
26824 }
26825 addTypeReplacement(declaration, type) {
26826 this.typeReplacements.set(declaration, type);
26827 }
26828 transformClassElement(element, imports) {
26829 if (ts$1.isMethodDeclaration(element)) {
26830 const original = ts$1.getOriginalNode(element, ts$1.isMethodDeclaration);
26831 if (!this.typeReplacements.has(original)) {
26832 return element;
26833 }
26834 const returnType = this.typeReplacements.get(original);
26835 const tsReturnType = translateType(returnType, imports);
26836 return ts$1.updateMethod(element, element.decorators, element.modifiers, element.asteriskToken, element.name, element.questionToken, element.typeParameters, element.parameters, tsReturnType, element.body);
26837 }
26838 return element;
26839 }
26840 transformFunctionDeclaration(element, imports) {
26841 const original = ts$1.getOriginalNode(element);
26842 if (!this.typeReplacements.has(original)) {
26843 return element;
26844 }
26845 const returnType = this.typeReplacements.get(original);
26846 const tsReturnType = translateType(returnType, imports);
26847 return ts$1.updateFunctionDeclaration(
26848 /* node */ element,
26849 /* decorators */ element.decorators,
26850 /* modifiers */ element.modifiers,
26851 /* asteriskToken */ element.asteriskToken,
26852 /* name */ element.name,
26853 /* typeParameters */ element.typeParameters,
26854 /* parameters */ element.parameters,
26855 /* type */ tsReturnType,
26856 /* body */ element.body);
26857 }
26858 }
26859
26860 /**
26861 * @license
26862 * Copyright Google LLC All Rights Reserved.
26863 *
26864 * Use of this source code is governed by an MIT-style license that can be
26865 * found in the LICENSE file at https://angular.io/license
26866 */
26867 /**
26868 * Visit a node with the given visitor and return a transformed copy.
26869 */
26870 function visit(node, visitor, context) {
26871 return visitor._visit(node, context);
26872 }
26873 /**
26874 * Abstract base class for visitors, which processes certain nodes specially to allow insertion
26875 * of other nodes before them.
26876 */
26877 class Visitor {
26878 constructor() {
26879 /**
26880 * Maps statements to an array of statements that should be inserted before them.
26881 */
26882 this._before = new Map();
26883 /**
26884 * Maps statements to an array of statements that should be inserted after them.
26885 */
26886 this._after = new Map();
26887 }
26888 /**
26889 * Visit a class declaration, returning at least the transformed declaration and optionally other
26890 * nodes to insert before the declaration.
26891 */
26892 visitClassDeclaration(node) {
26893 return { node };
26894 }
26895 _visitListEntryNode(node, visitor) {
26896 const result = visitor(node);
26897 if (result.before !== undefined) {
26898 // Record that some nodes should be inserted before the given declaration. The declaration's
26899 // parent's _visit call is responsible for performing this insertion.
26900 this._before.set(result.node, result.before);
26901 }
26902 if (result.after !== undefined) {
26903 // Same with nodes that should be inserted after.
26904 this._after.set(result.node, result.after);
26905 }
26906 return result.node;
26907 }
26908 /**
26909 * Visit types of nodes which don't have their own explicit visitor.
26910 */
26911 visitOtherNode(node) {
26912 return node;
26913 }
26914 /**
26915 * @internal
26916 */
26917 _visit(node, context) {
26918 // First, visit the node. visitedNode starts off as `null` but should be set after visiting
26919 // is completed.
26920 let visitedNode = null;
26921 node = ts$1.visitEachChild(node, child => this._visit(child, context), context);
26922 if (ts$1.isClassDeclaration(node)) {
26923 visitedNode =
26924 this._visitListEntryNode(node, (node) => this.visitClassDeclaration(node));
26925 }
26926 else {
26927 visitedNode = this.visitOtherNode(node);
26928 }
26929 // If the visited node has a `statements` array then process them, maybe replacing the visited
26930 // node and adding additional statements.
26931 if (hasStatements(visitedNode)) {
26932 visitedNode = this._maybeProcessStatements(visitedNode);
26933 }
26934 return visitedNode;
26935 }
26936 _maybeProcessStatements(node) {
26937 // Shortcut - if every statement doesn't require nodes to be prepended or appended,
26938 // this is a no-op.
26939 if (node.statements.every(stmt => !this._before.has(stmt) && !this._after.has(stmt))) {
26940 return node;
26941 }
26942 // There are statements to prepend, so clone the original node.
26943 const clone = ts$1.getMutableClone(node);
26944 // Build a new list of statements and patch it onto the clone.
26945 const newStatements = [];
26946 clone.statements.forEach(stmt => {
26947 if (this._before.has(stmt)) {
26948 newStatements.push(...this._before.get(stmt));
26949 this._before.delete(stmt);
26950 }
26951 newStatements.push(stmt);
26952 if (this._after.has(stmt)) {
26953 newStatements.push(...this._after.get(stmt));
26954 this._after.delete(stmt);
26955 }
26956 });
26957 clone.statements = ts$1.createNodeArray(newStatements, node.statements.hasTrailingComma);
26958 return clone;
26959 }
26960 }
26961 function hasStatements(node) {
26962 const block = node;
26963 return block.statements !== undefined && Array.isArray(block.statements);
26964 }
26965
26966 /**
26967 * @license
26968 * Copyright Google LLC All Rights Reserved.
26969 *
26970 * Use of this source code is governed by an MIT-style license that can be
26971 * found in the LICENSE file at https://angular.io/license
26972 */
26973 const NO_DECORATORS = new Set();
26974 const CLOSURE_FILE_OVERVIEW_REGEXP = /\s+@fileoverview\s+/i;
26975 function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportRecorder, isCore, isClosureCompilerEnabled) {
26976 const recordWrappedNodeExpr = createRecorderFn(defaultImportRecorder);
26977 return (context) => {
26978 return (file) => {
26979 return transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNodeExpr);
26980 };
26981 };
26982 }
26983 /**
26984 * Visits all classes, performs Ivy compilation where Angular decorators are present and collects
26985 * result in a Map that associates a ts.ClassDeclaration with Ivy compilation results. This visitor
26986 * does NOT perform any TS transformations.
26987 */
26988 class IvyCompilationVisitor extends Visitor {
26989 constructor(compilation, constantPool) {
26990 super();
26991 this.compilation = compilation;
26992 this.constantPool = constantPool;
26993 this.classCompilationMap = new Map();
26994 }
26995 visitClassDeclaration(node) {
26996 // Determine if this class has an Ivy field that needs to be added, and compile the field
26997 // to an expression if so.
26998 const result = this.compilation.compile(node, this.constantPool);
26999 if (result !== null) {
27000 this.classCompilationMap.set(node, result);
27001 }
27002 return { node };
27003 }
27004 }
27005 /**
27006 * Visits all classes and performs transformation of corresponding TS nodes based on the Ivy
27007 * compilation results (provided as an argument).
27008 */
27009 class IvyTransformationVisitor extends Visitor {
27010 constructor(compilation, classCompilationMap, reflector, importManager, recordWrappedNodeExpr, isClosureCompilerEnabled, isCore) {
27011 super();
27012 this.compilation = compilation;
27013 this.classCompilationMap = classCompilationMap;
27014 this.reflector = reflector;
27015 this.importManager = importManager;
27016 this.recordWrappedNodeExpr = recordWrappedNodeExpr;
27017 this.isClosureCompilerEnabled = isClosureCompilerEnabled;
27018 this.isCore = isCore;
27019 }
27020 visitClassDeclaration(node) {
27021 // If this class is not registered in the map, it means that it doesn't have Angular decorators,
27022 // thus no further processing is required.
27023 if (!this.classCompilationMap.has(node)) {
27024 return { node };
27025 }
27026 // There is at least one field to add.
27027 const statements = [];
27028 const members = [...node.members];
27029 for (const field of this.classCompilationMap.get(node)) {
27030 // Translate the initializer for the field into TS nodes.
27031 const exprNode = translateExpression(field.initializer, this.importManager, { recordWrappedNodeExpr: this.recordWrappedNodeExpr });
27032 // Create a static property declaration for the new field.
27033 const property = ts$1.createProperty(undefined, [ts$1.createToken(ts$1.SyntaxKind.StaticKeyword)], field.name, undefined, undefined, exprNode);
27034 if (this.isClosureCompilerEnabled) {
27035 // Closure compiler transforms the form `Service.ɵprov = X` into `Service$ɵprov = X`. To
27036 // prevent this transformation, such assignments need to be annotated with @nocollapse.
27037 // Note that tsickle is typically responsible for adding such annotations, however it
27038 // doesn't yet handle synthetic fields added during other transformations.
27039 ts$1.addSyntheticLeadingComment(property, ts$1.SyntaxKind.MultiLineCommentTrivia, '* @nocollapse ',
27040 /* hasTrailingNewLine */ false);
27041 }
27042 field.statements
27043 .map(stmt => translateStatement(stmt, this.importManager, { recordWrappedNodeExpr: this.recordWrappedNodeExpr }))
27044 .forEach(stmt => statements.push(stmt));
27045 members.push(property);
27046 }
27047 // Replace the class declaration with an updated version.
27048 node = ts$1.updateClassDeclaration(node,
27049 // Remove the decorator which triggered this compilation, leaving the others alone.
27050 maybeFilterDecorator(node.decorators, this.compilation.decoratorsFor(node)), node.modifiers, node.name, node.typeParameters, node.heritageClauses || [],
27051 // Map over the class members and remove any Angular decorators from them.
27052 members.map(member => this._stripAngularDecorators(member)));
27053 return { node, after: statements };
27054 }
27055 /**
27056 * Return all decorators on a `Declaration` which are from @angular/core, or an empty set if none
27057 * are.
27058 */
27059 _angularCoreDecorators(decl) {
27060 const decorators = this.reflector.getDecoratorsOfDeclaration(decl);
27061 if (decorators === null) {
27062 return NO_DECORATORS;
27063 }
27064 const coreDecorators = decorators.filter(dec => this.isCore || isFromAngularCore(dec))
27065 .map(dec => dec.node);
27066 if (coreDecorators.length > 0) {
27067 return new Set(coreDecorators);
27068 }
27069 else {
27070 return NO_DECORATORS;
27071 }
27072 }
27073 /**
27074 * Given a `ts.Node`, filter the decorators array and return a version containing only non-Angular
27075 * decorators.
27076 *
27077 * If all decorators are removed (or none existed in the first place), this method returns
27078 * `undefined`.
27079 */
27080 _nonCoreDecoratorsOnly(node) {
27081 // Shortcut if the node has no decorators.
27082 if (node.decorators === undefined) {
27083 return undefined;
27084 }
27085 // Build a Set of the decorators on this node from @angular/core.
27086 const coreDecorators = this._angularCoreDecorators(node);
27087 if (coreDecorators.size === node.decorators.length) {
27088 // If all decorators are to be removed, return `undefined`.
27089 return undefined;
27090 }
27091 else if (coreDecorators.size === 0) {
27092 // If no decorators need to be removed, return the original decorators array.
27093 return node.decorators;
27094 }
27095 // Filter out the core decorators.
27096 const filtered = node.decorators.filter(dec => !coreDecorators.has(dec));
27097 // If no decorators survive, return `undefined`. This can only happen if a core decorator is
27098 // repeated on the node.
27099 if (filtered.length === 0) {
27100 return undefined;
27101 }
27102 // Create a new `NodeArray` with the filtered decorators that sourcemaps back to the original.
27103 const array = ts$1.createNodeArray(filtered);
27104 array.pos = node.decorators.pos;
27105 array.end = node.decorators.end;
27106 return array;
27107 }
27108 /**
27109 * Remove Angular decorators from a `ts.Node` in a shallow manner.
27110 *
27111 * This will remove decorators from class elements (getters, setters, properties, methods) as well
27112 * as parameters of constructors.
27113 */
27114 _stripAngularDecorators(node) {
27115 if (ts$1.isParameter(node)) {
27116 // Strip decorators from parameters (probably of the constructor).
27117 node = ts$1.updateParameter(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.dotDotDotToken, node.name, node.questionToken, node.type, node.initializer);
27118 }
27119 else if (ts$1.isMethodDeclaration(node) && node.decorators !== undefined) {
27120 // Strip decorators of methods.
27121 node = ts$1.updateMethod(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters, node.type, node.body);
27122 }
27123 else if (ts$1.isPropertyDeclaration(node) && node.decorators !== undefined) {
27124 // Strip decorators of properties.
27125 node = ts$1.updateProperty(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name, node.questionToken, node.type, node.initializer);
27126 }
27127 else if (ts$1.isGetAccessor(node)) {
27128 // Strip decorators of getters.
27129 node = ts$1.updateGetAccessor(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name, node.parameters, node.type, node.body);
27130 }
27131 else if (ts$1.isSetAccessor(node)) {
27132 // Strip decorators of setters.
27133 node = ts$1.updateSetAccessor(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name, node.parameters, node.body);
27134 }
27135 else if (ts$1.isConstructorDeclaration(node)) {
27136 // For constructors, strip decorators of the parameters.
27137 const parameters = node.parameters.map(param => this._stripAngularDecorators(param));
27138 node =
27139 ts$1.updateConstructor(node, node.decorators, node.modifiers, parameters, node.body);
27140 }
27141 return node;
27142 }
27143 }
27144 /**
27145 * A transformer which operates on ts.SourceFiles and applies changes from an `IvyCompilation`.
27146 */
27147 function transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNodeExpr) {
27148 const constantPool = new ConstantPool(isClosureCompilerEnabled);
27149 const importManager = new ImportManager(importRewriter);
27150 // The transformation process consists of 2 steps:
27151 //
27152 // 1. Visit all classes, perform compilation and collect the results.
27153 // 2. Perform actual transformation of required TS nodes using compilation results from the first
27154 // step.
27155 //
27156 // This is needed to have all `o.Expression`s generated before any TS transforms happen. This
27157 // allows `ConstantPool` to properly identify expressions that can be shared across multiple
27158 // components declared in the same file.
27159 // Step 1. Go though all classes in AST, perform compilation and collect the results.
27160 const compilationVisitor = new IvyCompilationVisitor(compilation, constantPool);
27161 visit(file, compilationVisitor, context);
27162 // Step 2. Scan through the AST again and perform transformations based on Ivy compilation
27163 // results obtained at Step 1.
27164 const transformationVisitor = new IvyTransformationVisitor(compilation, compilationVisitor.classCompilationMap, reflector, importManager, recordWrappedNodeExpr, isClosureCompilerEnabled, isCore);
27165 let sf = visit(file, transformationVisitor, context);
27166 // Generate the constant statements first, as they may involve adding additional imports
27167 // to the ImportManager.
27168 const downlevelTranslatedCode = getLocalizeCompileTarget(context) < ts$1.ScriptTarget.ES2015;
27169 const constants = constantPool.statements.map(stmt => translateStatement(stmt, importManager, {
27170 recordWrappedNodeExpr,
27171 downlevelTaggedTemplates: downlevelTranslatedCode,
27172 downlevelVariableDeclarations: downlevelTranslatedCode,
27173 }));
27174 // Preserve @fileoverview comments required by Closure, since the location might change as a
27175 // result of adding extra imports and constant pool statements.
27176 const fileOverviewMeta = isClosureCompilerEnabled ? getFileOverviewComment(sf.statements) : null;
27177 // Add new imports for this file.
27178 sf = addImports(importManager, sf, constants);
27179 if (fileOverviewMeta !== null) {
27180 setFileOverviewComment(sf, fileOverviewMeta);
27181 }
27182 return sf;
27183 }
27184 /**
27185 * Compute the correct target output for `$localize` messages generated by Angular
27186 *
27187 * In some versions of TypeScript, the transformation of synthetic `$localize` tagged template
27188 * literals is broken. See https://github.com/microsoft/TypeScript/issues/38485
27189 *
27190 * Here we compute what the expected final output target of the compilation will
27191 * be so that we can generate ES5 compliant `$localize` calls instead of relying upon TS to do the
27192 * downleveling for us.
27193 */
27194 function getLocalizeCompileTarget(context) {
27195 const target = context.getCompilerOptions().target || ts$1.ScriptTarget.ES2015;
27196 return target !== ts$1.ScriptTarget.JSON ? target : ts$1.ScriptTarget.ES2015;
27197 }
27198 function getFileOverviewComment(statements) {
27199 if (statements.length > 0) {
27200 const host = statements[0];
27201 let trailing = false;
27202 let comments = ts$1.getSyntheticLeadingComments(host);
27203 // If @fileoverview tag is not found in source file, tsickle produces fake node with trailing
27204 // comment and inject it at the very beginning of the generated file. So we need to check for
27205 // leading as well as trailing comments.
27206 if (!comments || comments.length === 0) {
27207 trailing = true;
27208 comments = ts$1.getSyntheticTrailingComments(host);
27209 }
27210 if (comments && comments.length > 0 && CLOSURE_FILE_OVERVIEW_REGEXP.test(comments[0].text)) {
27211 return { comments, host, trailing };
27212 }
27213 }
27214 return null;
27215 }
27216 function setFileOverviewComment(sf, fileoverview) {
27217 const { comments, host, trailing } = fileoverview;
27218 // If host statement is no longer the first one, it means that extra statements were added at the
27219 // very beginning, so we need to relocate @fileoverview comment and cleanup the original statement
27220 // that hosted it.
27221 if (sf.statements.length > 0 && host !== sf.statements[0]) {
27222 if (trailing) {
27223 ts$1.setSyntheticTrailingComments(host, undefined);
27224 }
27225 else {
27226 ts$1.setSyntheticLeadingComments(host, undefined);
27227 }
27228 ts$1.setSyntheticLeadingComments(sf.statements[0], comments);
27229 }
27230 }
27231 function maybeFilterDecorator(decorators, toRemove) {
27232 if (decorators === undefined) {
27233 return undefined;
27234 }
27235 const filtered = decorators.filter(dec => toRemove.find(decToRemove => ts$1.getOriginalNode(dec) === decToRemove) === undefined);
27236 if (filtered.length === 0) {
27237 return undefined;
27238 }
27239 return ts$1.createNodeArray(filtered);
27240 }
27241 function isFromAngularCore(decorator) {
27242 return decorator.import !== null && decorator.import.from === '@angular/core';
27243 }
27244 function createRecorderFn(defaultImportRecorder) {
27245 return expr => {
27246 if (ts$1.isIdentifier(expr)) {
27247 defaultImportRecorder.recordUsedIdentifier(expr);
27248 }
27249 };
27250 }
27251
27252 /**
27253 * @license
27254 * Copyright Google LLC All Rights Reserved.
27255 *
27256 * Use of this source code is governed by an MIT-style license that can be
27257 * found in the LICENSE file at https://angular.io/license
27258 */
27259 let _tsSourceMapBug29300Fixed;
27260 /**
27261 * Test the current version of TypeScript to see if it has fixed the external SourceMap
27262 * file bug: https://github.com/Microsoft/TypeScript/issues/29300.
27263 *
27264 * The bug is fixed in TS 3.3+ but this check avoid us having to rely upon the version number,
27265 * and allows us to gracefully fail if the TS version still has the bug.
27266 *
27267 * We check for the bug by compiling a very small program `a;` and transforming it to `b;`,
27268 * where we map the new `b` identifier to an external source file, which has different lines to
27269 * the original source file. If the bug is fixed then the output SourceMap should contain
27270 * mappings that correspond ot the correct line/col pairs for this transformed node.
27271 *
27272 * @returns true if the bug is fixed.
27273 */
27274 function tsSourceMapBug29300Fixed() {
27275 if (_tsSourceMapBug29300Fixed === undefined) {
27276 let writtenFiles = {};
27277 const sourceFile = ts$1.createSourceFile('test.ts', 'a;', ts$1.ScriptTarget.ES2015, true, ts$1.ScriptKind.TS);
27278 const host = {
27279 getSourceFile() {
27280 return sourceFile;
27281 },
27282 fileExists() {
27283 return true;
27284 },
27285 readFile() {
27286 return '';
27287 },
27288 writeFile(fileName, data) {
27289 writtenFiles[fileName] = data;
27290 },
27291 getDefaultLibFileName() {
27292 return '';
27293 },
27294 getCurrentDirectory() {
27295 return '';
27296 },
27297 getDirectories() {
27298 return [];
27299 },
27300 getCanonicalFileName() {
27301 return '';
27302 },
27303 useCaseSensitiveFileNames() {
27304 return true;
27305 },
27306 getNewLine() {
27307 return '\n';
27308 },
27309 };
27310 const transform = (context) => {
27311 return (node) => ts$1.visitNode(node, visitor);
27312 function visitor(node) {
27313 if (ts$1.isIdentifier(node) && node.text === 'a') {
27314 const newNode = ts$1.createIdentifier('b');
27315 ts$1.setSourceMapRange(newNode, {
27316 pos: 16,
27317 end: 16,
27318 source: ts$1.createSourceMapSource('test.html', 'abc\ndef\nghi\njkl\nmno\npqr')
27319 });
27320 return newNode;
27321 }
27322 return ts$1.visitEachChild(node, visitor, context);
27323 }
27324 };
27325 const program = ts$1.createProgram(['test.ts'], { sourceMap: true }, host);
27326 program.emit(sourceFile, undefined, undefined, undefined, { after: [transform] });
27327 // The first two mappings in the source map should look like:
27328 // [0,1,4,0] col 0 => source file 1, row 4, column 0)
27329 // [1,0,0,0] col 1 => source file 1, row 4, column 0)
27330 _tsSourceMapBug29300Fixed = /ACIA,CAAA/.test(writtenFiles['test.js.map']);
27331 }
27332 return _tsSourceMapBug29300Fixed;
27333 }
27334
27335 /**
27336 * @license
27337 * Copyright Google LLC All Rights Reserved.
27338 *
27339 * Use of this source code is governed by an MIT-style license that can be
27340 * found in the LICENSE file at https://angular.io/license
27341 */
27342 function getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore) {
27343 const deps = [];
27344 const errors = [];
27345 let ctorParams = reflector.getConstructorParameters(clazz);
27346 if (ctorParams === null) {
27347 if (reflector.hasBaseClass(clazz)) {
27348 return null;
27349 }
27350 else {
27351 ctorParams = [];
27352 }
27353 }
27354 ctorParams.forEach((param, idx) => {
27355 let token = valueReferenceToExpression(param.typeValueReference, defaultImportRecorder);
27356 let attribute = null;
27357 let optional = false, self = false, skipSelf = false, host = false;
27358 let resolved = R3ResolvedDependencyType.Token;
27359 (param.decorators || []).filter(dec => isCore || isAngularCore(dec)).forEach(dec => {
27360 const name = isCore || dec.import === null ? dec.name : dec.import.name;
27361 if (name === 'Inject') {
27362 if (dec.args === null || dec.args.length !== 1) {
27363 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(dec), `Unexpected number of arguments to @Inject().`);
27364 }
27365 token = new WrappedNodeExpr(dec.args[0]);
27366 }
27367 else if (name === 'Optional') {
27368 optional = true;
27369 }
27370 else if (name === 'SkipSelf') {
27371 skipSelf = true;
27372 }
27373 else if (name === 'Self') {
27374 self = true;
27375 }
27376 else if (name === 'Host') {
27377 host = true;
27378 }
27379 else if (name === 'Attribute') {
27380 if (dec.args === null || dec.args.length !== 1) {
27381 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(dec), `Unexpected number of arguments to @Attribute().`);
27382 }
27383 const attributeName = dec.args[0];
27384 token = new WrappedNodeExpr(attributeName);
27385 if (ts$1.isStringLiteralLike(attributeName)) {
27386 attribute = new LiteralExpr(attributeName.text);
27387 }
27388 else {
27389 attribute = new WrappedNodeExpr(ts$1.createKeywordTypeNode(ts$1.SyntaxKind.UnknownKeyword));
27390 }
27391 resolved = R3ResolvedDependencyType.Attribute;
27392 }
27393 else {
27394 throw new FatalDiagnosticError(ErrorCode.DECORATOR_UNEXPECTED, Decorator.nodeForError(dec), `Unexpected decorator ${name} on parameter.`);
27395 }
27396 });
27397 if (token instanceof ExternalExpr && token.value.name === 'ChangeDetectorRef' &&
27398 token.value.moduleName === '@angular/core') {
27399 resolved = R3ResolvedDependencyType.ChangeDetectorRef;
27400 }
27401 if (token === null) {
27402 if (param.typeValueReference.kind !== 2 /* UNAVAILABLE */) {
27403 throw new Error('Illegal state: expected value reference to be unavailable if no token is present');
27404 }
27405 errors.push({
27406 index: idx,
27407 param,
27408 reason: param.typeValueReference.reason,
27409 });
27410 }
27411 else {
27412 deps.push({ token, attribute, optional, self, skipSelf, host, resolved });
27413 }
27414 });
27415 if (errors.length === 0) {
27416 return { deps };
27417 }
27418 else {
27419 return { deps: null, errors };
27420 }
27421 }
27422 function valueReferenceToExpression(valueRef, defaultImportRecorder) {
27423 if (valueRef.kind === 2 /* UNAVAILABLE */) {
27424 return null;
27425 }
27426 else if (valueRef.kind === 0 /* LOCAL */) {
27427 if (defaultImportRecorder !== null && valueRef.defaultImportStatement !== null &&
27428 ts$1.isIdentifier(valueRef.expression)) {
27429 defaultImportRecorder.recordImportedIdentifier(valueRef.expression, valueRef.defaultImportStatement);
27430 }
27431 return new WrappedNodeExpr(valueRef.expression);
27432 }
27433 else {
27434 let importExpr = new ExternalExpr({ moduleName: valueRef.moduleName, name: valueRef.importedName });
27435 if (valueRef.nestedPath !== null) {
27436 for (const property of valueRef.nestedPath) {
27437 importExpr = new ReadPropExpr(importExpr, property);
27438 }
27439 }
27440 return importExpr;
27441 }
27442 }
27443 /**
27444 * Convert `ConstructorDeps` into the `R3DependencyMetadata` array for those deps if they're valid,
27445 * or into an `'invalid'` signal if they're not.
27446 *
27447 * This is a companion function to `validateConstructorDependencies` which accepts invalid deps.
27448 */
27449 function unwrapConstructorDependencies(deps) {
27450 if (deps === null) {
27451 return null;
27452 }
27453 else if (deps.deps !== null) {
27454 // These constructor dependencies are valid.
27455 return deps.deps;
27456 }
27457 else {
27458 // These deps are invalid.
27459 return 'invalid';
27460 }
27461 }
27462 function getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore) {
27463 return validateConstructorDependencies(clazz, getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore));
27464 }
27465 /**
27466 * Validate that `ConstructorDeps` does not have any invalid dependencies and convert them into the
27467 * `R3DependencyMetadata` array if so, or raise a diagnostic if some deps are invalid.
27468 *
27469 * This is a companion function to `unwrapConstructorDependencies` which does not accept invalid
27470 * deps.
27471 */
27472 function validateConstructorDependencies(clazz, deps) {
27473 if (deps === null) {
27474 return null;
27475 }
27476 else if (deps.deps !== null) {
27477 return deps.deps;
27478 }
27479 else {
27480 // TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here.
27481 // There is at least one error.
27482 const error = deps.errors[0];
27483 throw createUnsuitableInjectionTokenError(clazz, error);
27484 }
27485 }
27486 /**
27487 * Creates a fatal error with diagnostic for an invalid injection token.
27488 * @param clazz The class for which the injection token was unavailable.
27489 * @param error The reason why no valid injection token is available.
27490 */
27491 function createUnsuitableInjectionTokenError(clazz, error) {
27492 const { param, index, reason } = error;
27493 let chainMessage = undefined;
27494 let hints = undefined;
27495 switch (reason.kind) {
27496 case 5 /* UNSUPPORTED */:
27497 chainMessage = 'Consider using the @Inject decorator to specify an injection token.';
27498 hints = [
27499 makeRelatedInformation(reason.typeNode, 'This type is not supported as injection token.'),
27500 ];
27501 break;
27502 case 1 /* NO_VALUE_DECLARATION */:
27503 chainMessage = 'Consider using the @Inject decorator to specify an injection token.';
27504 hints = [
27505 makeRelatedInformation(reason.typeNode, 'This type does not have a value, so it cannot be used as injection token.'),
27506 ];
27507 if (reason.decl !== null) {
27508 hints.push(makeRelatedInformation(reason.decl, 'The type is declared here.'));
27509 }
27510 break;
27511 case 2 /* TYPE_ONLY_IMPORT */:
27512 chainMessage =
27513 'Consider changing the type-only import to a regular import, or use the @Inject decorator to specify an injection token.';
27514 hints = [
27515 makeRelatedInformation(reason.typeNode, 'This type is imported using a type-only import, which prevents it from being usable as an injection token.'),
27516 makeRelatedInformation(reason.importClause, 'The type-only import occurs here.'),
27517 ];
27518 break;
27519 case 4 /* NAMESPACE */:
27520 chainMessage = 'Consider using the @Inject decorator to specify an injection token.';
27521 hints = [
27522 makeRelatedInformation(reason.typeNode, 'This type corresponds with a namespace, which cannot be used as injection token.'),
27523 makeRelatedInformation(reason.importClause, 'The namespace import occurs here.'),
27524 ];
27525 break;
27526 case 3 /* UNKNOWN_REFERENCE */:
27527 chainMessage = 'The type should reference a known declaration.';
27528 hints = [makeRelatedInformation(reason.typeNode, 'This type could not be resolved.')];
27529 break;
27530 case 0 /* MISSING_TYPE */:
27531 chainMessage =
27532 'Consider adding a type to the parameter or use the @Inject decorator to specify an injection token.';
27533 break;
27534 }
27535 const chain = {
27536 messageText: `No suitable injection token for parameter '${param.name || index}' of class '${clazz.name.text}'.`,
27537 category: ts$1.DiagnosticCategory.Error,
27538 code: 0,
27539 next: [{
27540 messageText: chainMessage,
27541 category: ts$1.DiagnosticCategory.Message,
27542 code: 0,
27543 }],
27544 };
27545 return new FatalDiagnosticError(ErrorCode.PARAM_MISSING_TOKEN, param.nameNode, chain, hints);
27546 }
27547 function toR3Reference(valueRef, typeRef, valueContext, typeContext, refEmitter) {
27548 const value = refEmitter.emit(valueRef, valueContext);
27549 const type = refEmitter.emit(typeRef, typeContext, ImportFlags.ForceNewImport | ImportFlags.AllowTypeImports);
27550 if (value === null || type === null) {
27551 throw new Error(`Could not refer to ${ts$1.SyntaxKind[valueRef.node.kind]}`);
27552 }
27553 return { value, type };
27554 }
27555 function isAngularCore(decorator) {
27556 return decorator.import !== null && decorator.import.from === '@angular/core';
27557 }
27558 function isAngularCoreReference(reference, symbolName) {
27559 return reference.ownedByModuleGuess === '@angular/core' && reference.debugName === symbolName;
27560 }
27561 function findAngularDecorator(decorators, name, isCore) {
27562 return decorators.find(decorator => isAngularDecorator(decorator, name, isCore));
27563 }
27564 function isAngularDecorator(decorator, name, isCore) {
27565 if (isCore) {
27566 return decorator.name === name;
27567 }
27568 else if (isAngularCore(decorator)) {
27569 return decorator.import.name === name;
27570 }
27571 return false;
27572 }
27573 /**
27574 * Unwrap a `ts.Expression`, removing outer type-casts or parentheses until the expression is in its
27575 * lowest level form.
27576 *
27577 * For example, the expression "(foo as Type)" unwraps to "foo".
27578 */
27579 function unwrapExpression(node) {
27580 while (ts$1.isAsExpression(node) || ts$1.isParenthesizedExpression(node)) {
27581 node = node.expression;
27582 }
27583 return node;
27584 }
27585 function expandForwardRef(arg) {
27586 arg = unwrapExpression(arg);
27587 if (!ts$1.isArrowFunction(arg) && !ts$1.isFunctionExpression(arg)) {
27588 return null;
27589 }
27590 const body = arg.body;
27591 // Either the body is a ts.Expression directly, or a block with a single return statement.
27592 if (ts$1.isBlock(body)) {
27593 // Block body - look for a single return statement.
27594 if (body.statements.length !== 1) {
27595 return null;
27596 }
27597 const stmt = body.statements[0];
27598 if (!ts$1.isReturnStatement(stmt) || stmt.expression === undefined) {
27599 return null;
27600 }
27601 return stmt.expression;
27602 }
27603 else {
27604 // Shorthand body - return as an expression.
27605 return body;
27606 }
27607 }
27608 /**
27609 * Possibly resolve a forwardRef() expression into the inner value.
27610 *
27611 * @param node the forwardRef() expression to resolve
27612 * @param reflector a ReflectionHost
27613 * @returns the resolved expression, if the original expression was a forwardRef(), or the original
27614 * expression otherwise
27615 */
27616 function unwrapForwardRef(node, reflector) {
27617 node = unwrapExpression(node);
27618 if (!ts$1.isCallExpression(node) || node.arguments.length !== 1) {
27619 return node;
27620 }
27621 const fn = ts$1.isPropertyAccessExpression(node.expression) ? node.expression.name : node.expression;
27622 if (!ts$1.isIdentifier(fn)) {
27623 return node;
27624 }
27625 const expr = expandForwardRef(node.arguments[0]);
27626 if (expr === null) {
27627 return node;
27628 }
27629 const imp = reflector.getImportOfIdentifier(fn);
27630 if (imp === null || imp.from !== '@angular/core' || imp.name !== 'forwardRef') {
27631 return node;
27632 }
27633 else {
27634 return expr;
27635 }
27636 }
27637 /**
27638 * A foreign function resolver for `staticallyResolve` which unwraps forwardRef() expressions.
27639 *
27640 * @param ref a Reference to the declaration of the function being called (which might be
27641 * forwardRef)
27642 * @param args the arguments to the invocation of the forwardRef expression
27643 * @returns an unwrapped argument if `ref` pointed to forwardRef, or null otherwise
27644 */
27645 function forwardRefResolver(ref, args) {
27646 if (!isAngularCoreReference(ref, 'forwardRef') || args.length !== 1) {
27647 return null;
27648 }
27649 return expandForwardRef(args[0]);
27650 }
27651 /**
27652 * Combines an array of resolver functions into a one.
27653 * @param resolvers Resolvers to be combined.
27654 */
27655 function combineResolvers(resolvers) {
27656 return (ref, args) => {
27657 for (const resolver of resolvers) {
27658 const resolved = resolver(ref, args);
27659 if (resolved !== null) {
27660 return resolved;
27661 }
27662 }
27663 return null;
27664 };
27665 }
27666 function isExpressionForwardReference(expr, context, contextSource) {
27667 if (isWrappedTsNodeExpr(expr)) {
27668 const node = ts$1.getOriginalNode(expr.node);
27669 return node.getSourceFile() === contextSource && context.pos < node.pos;
27670 }
27671 else {
27672 return false;
27673 }
27674 }
27675 function isWrappedTsNodeExpr(expr) {
27676 return expr instanceof WrappedNodeExpr;
27677 }
27678 function readBaseClass$1(node, reflector, evaluator) {
27679 const baseExpression = reflector.getBaseClassExpression(node);
27680 if (baseExpression !== null) {
27681 const baseClass = evaluator.evaluate(baseExpression);
27682 if (baseClass instanceof Reference$1 && reflector.isClass(baseClass.node)) {
27683 return baseClass;
27684 }
27685 else {
27686 return 'dynamic';
27687 }
27688 }
27689 return null;
27690 }
27691 const parensWrapperTransformerFactory = (context) => {
27692 const visitor = (node) => {
27693 const visited = ts$1.visitEachChild(node, visitor, context);
27694 if (ts$1.isArrowFunction(visited) || ts$1.isFunctionExpression(visited)) {
27695 return ts$1.createParen(visited);
27696 }
27697 return visited;
27698 };
27699 return (node) => ts$1.visitEachChild(node, visitor, context);
27700 };
27701 /**
27702 * Wraps all functions in a given expression in parentheses. This is needed to avoid problems
27703 * where Tsickle annotations added between analyse and transform phases in Angular may trigger
27704 * automatic semicolon insertion, e.g. if a function is the expression in a `return` statement.
27705 * More
27706 * info can be found in Tsickle source code here:
27707 * https://github.com/angular/tsickle/blob/d7974262571c8a17d684e5ba07680e1b1993afdd/src/jsdoc_transformer.ts#L1021
27708 *
27709 * @param expression Expression where functions should be wrapped in parentheses
27710 */
27711 function wrapFunctionExpressionsInParens(expression) {
27712 return ts$1.transform(expression, [parensWrapperTransformerFactory]).transformed[0];
27713 }
27714 /**
27715 * Create a `ts.Diagnostic` which indicates the given class is part of the declarations of two or
27716 * more NgModules.
27717 *
27718 * The resulting `ts.Diagnostic` will have a context entry for each NgModule showing the point where
27719 * the directive/pipe exists in its `declarations` (if possible).
27720 */
27721 function makeDuplicateDeclarationError(node, data, kind) {
27722 const context = [];
27723 for (const decl of data) {
27724 if (decl.rawDeclarations === null) {
27725 continue;
27726 }
27727 // Try to find the reference to the declaration within the declarations array, to hang the
27728 // error there. If it can't be found, fall back on using the NgModule's name.
27729 const contextNode = decl.ref.getOriginForDiagnostics(decl.rawDeclarations, decl.ngModule.name);
27730 context.push(makeRelatedInformation(contextNode, `'${node.name.text}' is listed in the declarations of the NgModule '${decl.ngModule.name.text}'.`));
27731 }
27732 // Finally, produce the diagnostic.
27733 return makeDiagnostic(ErrorCode.NGMODULE_DECLARATION_NOT_UNIQUE, node.name, `The ${kind} '${node.name.text}' is declared by more than one NgModule.`, context);
27734 }
27735 /**
27736 * Resolves the given `rawProviders` into `ClassDeclarations` and returns
27737 * a set containing those that are known to require a factory definition.
27738 * @param rawProviders Expression that declared the providers array in the source.
27739 */
27740 function resolveProvidersRequiringFactory(rawProviders, reflector, evaluator) {
27741 const providers = new Set();
27742 const resolvedProviders = evaluator.evaluate(rawProviders);
27743 if (!Array.isArray(resolvedProviders)) {
27744 return providers;
27745 }
27746 resolvedProviders.forEach(function processProviders(provider) {
27747 let tokenClass = null;
27748 if (Array.isArray(provider)) {
27749 // If we ran into an array, recurse into it until we've resolve all the classes.
27750 provider.forEach(processProviders);
27751 }
27752 else if (provider instanceof Reference$1) {
27753 tokenClass = provider;
27754 }
27755 else if (provider instanceof Map && provider.has('useClass') && !provider.has('deps')) {
27756 const useExisting = provider.get('useClass');
27757 if (useExisting instanceof Reference$1) {
27758 tokenClass = useExisting;
27759 }
27760 }
27761 // TODO(alxhub): there was a bug where `getConstructorParameters` would return `null` for a
27762 // class in a .d.ts file, always, even if the class had a constructor. This was fixed for
27763 // `getConstructorParameters`, but that fix causes more classes to be recognized here as needing
27764 // provider checks, which is a breaking change in g3. Avoid this breakage for now by skipping
27765 // classes from .d.ts files here directly, until g3 can be cleaned up.
27766 if (tokenClass !== null && !tokenClass.node.getSourceFile().isDeclarationFile &&
27767 reflector.isClass(tokenClass.node)) {
27768 const constructorParameters = reflector.getConstructorParameters(tokenClass.node);
27769 // Note that we only want to capture providers with a non-trivial constructor,
27770 // because they're the ones that might be using DI and need to be decorated.
27771 if (constructorParameters !== null && constructorParameters.length > 0) {
27772 providers.add(tokenClass);
27773 }
27774 }
27775 });
27776 return providers;
27777 }
27778 /**
27779 * Create an R3Reference for a class.
27780 *
27781 * The `value` is the exported declaration of the class from its source file.
27782 * The `type` is an expression that would be used by ngcc in the typings (.d.ts) files.
27783 */
27784 function wrapTypeReference(reflector, clazz) {
27785 const dtsClass = reflector.getDtsDeclaration(clazz);
27786 const value = new WrappedNodeExpr(clazz.name);
27787 const type = dtsClass !== null && isNamedClassDeclaration(dtsClass) ?
27788 new WrappedNodeExpr(dtsClass.name) :
27789 value;
27790 return { value, type };
27791 }
27792 /** Creates a ParseSourceSpan for a TypeScript node. */
27793 function createSourceSpan(node) {
27794 const sf = node.getSourceFile();
27795 const [startOffset, endOffset] = [node.getStart(), node.getEnd()];
27796 const { line: startLine, character: startCol } = sf.getLineAndCharacterOfPosition(startOffset);
27797 const { line: endLine, character: endCol } = sf.getLineAndCharacterOfPosition(endOffset);
27798 const parseSf = new ParseSourceFile(sf.getFullText(), sf.fileName);
27799 // +1 because values are zero-indexed.
27800 return new ParseSourceSpan(new ParseLocation(parseSf, startOffset, startLine + 1, startCol + 1), new ParseLocation(parseSf, endOffset, endLine + 1, endCol + 1));
27801 }
27802
27803 /**
27804 * @license
27805 * Copyright Google LLC All Rights Reserved.
27806 *
27807 * Use of this source code is governed by an MIT-style license that can be
27808 * found in the LICENSE file at https://angular.io/license
27809 */
27810 /**
27811 * Creates a `FatalDiagnosticError` for a node that did not evaluate to the expected type. The
27812 * diagnostic that is created will include details on why the value is incorrect, i.e. it includes
27813 * a representation of the actual type that was unsupported, or in the case of a dynamic value the
27814 * trace to the node where the dynamic value originated.
27815 *
27816 * @param node The node for which the diagnostic should be produced.
27817 * @param value The evaluated value that has the wrong type.
27818 * @param messageText The message text of the error.
27819 */
27820 function createValueHasWrongTypeError(node, value, messageText) {
27821 var _a;
27822 let chainedMessage;
27823 let relatedInformation;
27824 if (value instanceof DynamicValue) {
27825 chainedMessage = 'Value could not be determined statically.';
27826 relatedInformation = traceDynamicValue(node, value);
27827 }
27828 else if (value instanceof Reference$1) {
27829 const target = value.debugName !== null ? `'${value.debugName}'` : 'an anonymous declaration';
27830 chainedMessage = `Value is a reference to ${target}.`;
27831 const referenceNode = (_a = identifierOfNode(value.node)) !== null && _a !== void 0 ? _a : value.node;
27832 relatedInformation = [makeRelatedInformation(referenceNode, 'Reference is declared here.')];
27833 }
27834 else {
27835 chainedMessage = `Value is of type '${describeResolvedType(value)}'.`;
27836 }
27837 const chain = {
27838 messageText,
27839 category: ts$1.DiagnosticCategory.Error,
27840 code: 0,
27841 next: [{
27842 messageText: chainedMessage,
27843 category: ts$1.DiagnosticCategory.Message,
27844 code: 0,
27845 }]
27846 };
27847 return new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, node, chain, relatedInformation);
27848 }
27849 /**
27850 * Gets the diagnostics for a set of provider classes.
27851 * @param providerClasses Classes that should be checked.
27852 * @param providersDeclaration Node that declares the providers array.
27853 * @param registry Registry that keeps track of the registered injectable classes.
27854 */
27855 function getProviderDiagnostics(providerClasses, providersDeclaration, registry) {
27856 const diagnostics = [];
27857 for (const provider of providerClasses) {
27858 if (registry.isInjectable(provider.node)) {
27859 continue;
27860 }
27861 const contextNode = provider.getOriginForDiagnostics(providersDeclaration);
27862 diagnostics.push(makeDiagnostic(ErrorCode.UNDECORATED_PROVIDER, contextNode, `The class '${provider.node.name
27863 .text}' cannot be created via dependency injection, as it does not have an Angular decorator. This will result in an error at runtime.
27864
27865Either add the @Injectable() decorator to '${provider.node.name
27866 .text}', or configure a different provider (such as a provider with 'useFactory').
27867`, [makeRelatedInformation(provider.node, `'${provider.node.name.text}' is declared here.`)]));
27868 }
27869 return diagnostics;
27870 }
27871 function getDirectiveDiagnostics(node, reader, evaluator, reflector, scopeRegistry, kind) {
27872 let diagnostics = [];
27873 const addDiagnostics = (more) => {
27874 if (more === null) {
27875 return;
27876 }
27877 else if (diagnostics === null) {
27878 diagnostics = Array.isArray(more) ? more : [more];
27879 }
27880 else if (Array.isArray(more)) {
27881 diagnostics.push(...more);
27882 }
27883 else {
27884 diagnostics.push(more);
27885 }
27886 };
27887 const duplicateDeclarations = scopeRegistry.getDuplicateDeclarations(node);
27888 if (duplicateDeclarations !== null) {
27889 addDiagnostics(makeDuplicateDeclarationError(node, duplicateDeclarations, kind));
27890 }
27891 addDiagnostics(checkInheritanceOfDirective(node, reader, reflector, evaluator));
27892 return diagnostics;
27893 }
27894 function getUndecoratedClassWithAngularFeaturesDiagnostic(node) {
27895 return makeDiagnostic(ErrorCode.UNDECORATED_CLASS_USING_ANGULAR_FEATURES, node.name, `Class is using Angular features but is not decorated. Please add an explicit ` +
27896 `Angular decorator.`);
27897 }
27898 function checkInheritanceOfDirective(node, reader, reflector, evaluator) {
27899 if (!reflector.isClass(node) || reflector.getConstructorParameters(node) !== null) {
27900 // We should skip nodes that aren't classes. If a constructor exists, then no base class
27901 // definition is required on the runtime side - it's legal to inherit from any class.
27902 return null;
27903 }
27904 // The extends clause is an expression which can be as dynamic as the user wants. Try to
27905 // evaluate it, but fall back on ignoring the clause if it can't be understood. This is a View
27906 // Engine compatibility hack: View Engine ignores 'extends' expressions that it cannot understand.
27907 let baseClass = readBaseClass$1(node, reflector, evaluator);
27908 while (baseClass !== null) {
27909 if (baseClass === 'dynamic') {
27910 return null;
27911 }
27912 // We can skip the base class if it has metadata.
27913 const baseClassMeta = reader.getDirectiveMetadata(baseClass);
27914 if (baseClassMeta !== null) {
27915 return null;
27916 }
27917 // If the base class has a blank constructor we can skip it since it can't be using DI.
27918 const baseClassConstructorParams = reflector.getConstructorParameters(baseClass.node);
27919 const newParentClass = readBaseClass$1(baseClass.node, reflector, evaluator);
27920 if (baseClassConstructorParams !== null && baseClassConstructorParams.length > 0) {
27921 // This class has a non-trivial constructor, that's an error!
27922 return getInheritedUndecoratedCtorDiagnostic(node, baseClass, reader);
27923 }
27924 else if (baseClassConstructorParams !== null || newParentClass === null) {
27925 // This class has a trivial constructor, or no constructor + is the
27926 // top of the inheritance chain, so it's okay.
27927 return null;
27928 }
27929 // Go up the chain and continue
27930 baseClass = newParentClass;
27931 }
27932 return null;
27933 }
27934 function getInheritedUndecoratedCtorDiagnostic(node, baseClass, reader) {
27935 const subclassMeta = reader.getDirectiveMetadata(new Reference$1(node));
27936 const dirOrComp = subclassMeta.isComponent ? 'Component' : 'Directive';
27937 const baseClassName = baseClass.debugName;
27938 return makeDiagnostic(ErrorCode.DIRECTIVE_INHERITS_UNDECORATED_CTOR, node.name, `The ${dirOrComp.toLowerCase()} ${node.name.text} inherits its constructor from ${baseClassName}, ` +
27939 `but the latter does not have an Angular decorator of its own. Dependency injection will not be able to ` +
27940 `resolve the parameters of ${baseClassName}'s constructor. Either add a @Directive decorator ` +
27941 `to ${baseClassName}, or add an explicit constructor to ${node.name.text}.`);
27942 }
27943
27944 /**
27945 * @license
27946 * Copyright Google LLC All Rights Reserved.
27947 *
27948 * Use of this source code is governed by an MIT-style license that can be
27949 * found in the LICENSE file at https://angular.io/license
27950 */
27951 function compileNgFactoryDefField(metadata) {
27952 const res = compileFactoryFunction(metadata);
27953 return { name: 'ɵfac', initializer: res.factory, statements: res.statements, type: res.type };
27954 }
27955
27956 /**
27957 * @license
27958 * Copyright Google LLC All Rights Reserved.
27959 *
27960 * Use of this source code is governed by an MIT-style license that can be
27961 * found in the LICENSE file at https://angular.io/license
27962 */
27963 /**
27964 * Given a class declaration, generate a call to `setClassMetadata` with the Angular metadata
27965 * present on the class or its member fields. An ngDevMode guard is used to allow the call to be
27966 * tree-shaken away, as the `setClassMetadata` invocation is only needed for testing purposes.
27967 *
27968 * If no such metadata is present, this function returns `null`. Otherwise, the call is returned
27969 * as a `Statement` for inclusion along with the class.
27970 */
27971 function generateSetClassMetadataCall(clazz, reflection, defaultImportRecorder, isCore, annotateForClosureCompiler) {
27972 if (!reflection.isClass(clazz)) {
27973 return null;
27974 }
27975 const id = reflection.getAdjacentNameOfClass(clazz);
27976 // Reflect over the class decorators. If none are present, or those that are aren't from
27977 // Angular, then return null. Otherwise, turn them into metadata.
27978 const classDecorators = reflection.getDecoratorsOfDeclaration(clazz);
27979 if (classDecorators === null) {
27980 return null;
27981 }
27982 const ngClassDecorators = classDecorators.filter(dec => isAngularDecorator$1(dec, isCore))
27983 .map(decorator => decoratorToMetadata(decorator, annotateForClosureCompiler))
27984 // Since the `setClassMetadata` call is intended to be emitted after the class
27985 // declaration, we have to strip references to the existing identifiers or
27986 // TypeScript might generate invalid code when it emits to JS. In particular
27987 // this can break when emitting a class to ES5 which has a custom decorator
27988 // and is referenced inside of its own metadata (see #39509 for more information).
27989 .map(decorator => removeIdentifierReferences(decorator, id.text));
27990 if (ngClassDecorators.length === 0) {
27991 return null;
27992 }
27993 const metaDecorators = ts$1.createArrayLiteral(ngClassDecorators);
27994 // Convert the constructor parameters to metadata, passing null if none are present.
27995 let metaCtorParameters = new LiteralExpr(null);
27996 const classCtorParameters = reflection.getConstructorParameters(clazz);
27997 if (classCtorParameters !== null) {
27998 const ctorParameters = classCtorParameters.map(param => ctorParameterToMetadata(param, defaultImportRecorder, isCore));
27999 metaCtorParameters = new FunctionExpr([], [
28000 new ReturnStatement(new LiteralArrayExpr(ctorParameters)),
28001 ]);
28002 }
28003 // Do the same for property decorators.
28004 let metaPropDecorators = ts$1.createNull();
28005 const classMembers = reflection.getMembersOfClass(clazz).filter(member => !member.isStatic && member.decorators !== null && member.decorators.length > 0);
28006 const duplicateDecoratedMemberNames = classMembers.map(member => member.name).filter((name, i, arr) => arr.indexOf(name) < i);
28007 if (duplicateDecoratedMemberNames.length > 0) {
28008 // This should theoretically never happen, because the only way to have duplicate instance
28009 // member names is getter/setter pairs and decorators cannot appear in both a getter and the
28010 // corresponding setter.
28011 throw new Error(`Duplicate decorated properties found on class '${clazz.name.text}': ` +
28012 duplicateDecoratedMemberNames.join(', '));
28013 }
28014 const decoratedMembers = classMembers.map(member => { var _a; return classMemberToMetadata((_a = member.nameNode) !== null && _a !== void 0 ? _a : member.name, member.decorators, isCore); });
28015 if (decoratedMembers.length > 0) {
28016 metaPropDecorators = ts$1.createObjectLiteral(decoratedMembers);
28017 }
28018 // Generate a pure call to setClassMetadata with the class identifier and its metadata.
28019 const setClassMetadata = new ExternalExpr(Identifiers.setClassMetadata);
28020 const fnCall = new InvokeFunctionExpr(
28021 /* fn */ setClassMetadata,
28022 /* args */
28023 [
28024 new WrappedNodeExpr(id),
28025 new WrappedNodeExpr(metaDecorators),
28026 metaCtorParameters,
28027 new WrappedNodeExpr(metaPropDecorators),
28028 ]);
28029 const iife = new FunctionExpr([], [devOnlyGuardedExpression(fnCall).toStmt()]);
28030 return iife.callFn([]).toStmt();
28031 }
28032 /**
28033 * Convert a reflected constructor parameter to metadata.
28034 */
28035 function ctorParameterToMetadata(param, defaultImportRecorder, isCore) {
28036 // Parameters sometimes have a type that can be referenced. If so, then use it, otherwise
28037 // its type is undefined.
28038 const type = param.typeValueReference.kind !== 2 /* UNAVAILABLE */ ?
28039 valueReferenceToExpression(param.typeValueReference, defaultImportRecorder) :
28040 new LiteralExpr(undefined);
28041 const mapEntries = [
28042 { key: 'type', value: type, quoted: false },
28043 ];
28044 // If the parameter has decorators, include the ones from Angular.
28045 if (param.decorators !== null) {
28046 const ngDecorators = param.decorators.filter(dec => isAngularDecorator$1(dec, isCore))
28047 .map((decorator) => decoratorToMetadata(decorator));
28048 const value = new WrappedNodeExpr(ts$1.createArrayLiteral(ngDecorators));
28049 mapEntries.push({ key: 'decorators', value, quoted: false });
28050 }
28051 return literalMap(mapEntries);
28052 }
28053 /**
28054 * Convert a reflected class member to metadata.
28055 */
28056 function classMemberToMetadata(name, decorators, isCore) {
28057 const ngDecorators = decorators.filter(dec => isAngularDecorator$1(dec, isCore))
28058 .map((decorator) => decoratorToMetadata(decorator));
28059 const decoratorMeta = ts$1.createArrayLiteral(ngDecorators);
28060 return ts$1.createPropertyAssignment(name, decoratorMeta);
28061 }
28062 /**
28063 * Convert a reflected decorator to metadata.
28064 */
28065 function decoratorToMetadata(decorator, wrapFunctionsInParens) {
28066 if (decorator.identifier === null) {
28067 throw new Error('Illegal state: synthesized decorator cannot be emitted in class metadata.');
28068 }
28069 // Decorators have a type.
28070 const properties = [
28071 ts$1.createPropertyAssignment('type', ts$1.getMutableClone(decorator.identifier)),
28072 ];
28073 // Sometimes they have arguments.
28074 if (decorator.args !== null && decorator.args.length > 0) {
28075 const args = decorator.args.map(arg => {
28076 const expr = ts$1.getMutableClone(arg);
28077 return wrapFunctionsInParens ? wrapFunctionExpressionsInParens(expr) : expr;
28078 });
28079 properties.push(ts$1.createPropertyAssignment('args', ts$1.createArrayLiteral(args)));
28080 }
28081 return ts$1.createObjectLiteral(properties, true);
28082 }
28083 /**
28084 * Whether a given decorator should be treated as an Angular decorator.
28085 *
28086 * Either it's used in @angular/core, or it's imported from there.
28087 */
28088 function isAngularDecorator$1(decorator, isCore) {
28089 return isCore || (decorator.import !== null && decorator.import.from === '@angular/core');
28090 }
28091 /**
28092 * Recursively recreates all of the `Identifier` descendant nodes with a particular name inside
28093 * of an AST node, thus removing any references to them. Useful if a particular node has to be t
28094 * aken from one place any emitted to another one exactly as it has been written.
28095 */
28096 function removeIdentifierReferences(node, name) {
28097 const result = ts$1.transform(node, [context => root => ts$1.visitNode(root, function walk(current) {
28098 return ts$1.isIdentifier(current) && current.text === name ?
28099 ts$1.createIdentifier(current.text) :
28100 ts$1.visitEachChild(current, walk, context);
28101 })]);
28102 return result.transformed[0];
28103 }
28104
28105 /**
28106 * @license
28107 * Copyright Google LLC All Rights Reserved.
28108 *
28109 * Use of this source code is governed by an MIT-style license that can be
28110 * found in the LICENSE file at https://angular.io/license
28111 */
28112 const EMPTY_OBJECT = {};
28113 const FIELD_DECORATORS = [
28114 'Input', 'Output', 'ViewChild', 'ViewChildren', 'ContentChild', 'ContentChildren', 'HostBinding',
28115 'HostListener'
28116 ];
28117 const LIFECYCLE_HOOKS = new Set([
28118 'ngOnChanges', 'ngOnInit', 'ngOnDestroy', 'ngDoCheck', 'ngAfterViewInit', 'ngAfterViewChecked',
28119 'ngAfterContentInit', 'ngAfterContentChecked'
28120 ]);
28121 class DirectiveDecoratorHandler {
28122 constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportRecorder, injectableRegistry, isCore, annotateForClosureCompiler, compileUndecoratedClassesWithAngularFeatures) {
28123 this.reflector = reflector;
28124 this.evaluator = evaluator;
28125 this.metaRegistry = metaRegistry;
28126 this.scopeRegistry = scopeRegistry;
28127 this.metaReader = metaReader;
28128 this.defaultImportRecorder = defaultImportRecorder;
28129 this.injectableRegistry = injectableRegistry;
28130 this.isCore = isCore;
28131 this.annotateForClosureCompiler = annotateForClosureCompiler;
28132 this.compileUndecoratedClassesWithAngularFeatures = compileUndecoratedClassesWithAngularFeatures;
28133 this.precedence = HandlerPrecedence.PRIMARY;
28134 this.name = DirectiveDecoratorHandler.name;
28135 }
28136 detect(node, decorators) {
28137 // If a class is undecorated but uses Angular features, we detect it as an
28138 // abstract directive. This is an unsupported pattern as of v10, but we want
28139 // to still detect these patterns so that we can report diagnostics, or compile
28140 // them for backwards compatibility in ngcc.
28141 if (!decorators) {
28142 const angularField = this.findClassFieldWithAngularFeatures(node);
28143 return angularField ? { trigger: angularField.node, decorator: null, metadata: null } :
28144 undefined;
28145 }
28146 else {
28147 const decorator = findAngularDecorator(decorators, 'Directive', this.isCore);
28148 return decorator ? { trigger: decorator.node, decorator, metadata: decorator } : undefined;
28149 }
28150 }
28151 analyze(node, decorator, flags = HandlerFlags.NONE) {
28152 // Skip processing of the class declaration if compilation of undecorated classes
28153 // with Angular features is disabled. Previously in ngtsc, such classes have always
28154 // been processed, but we want to enforce a consistent decorator mental model.
28155 // See: https://v9.angular.io/guide/migration-undecorated-classes.
28156 if (this.compileUndecoratedClassesWithAngularFeatures === false && decorator === null) {
28157 return { diagnostics: [getUndecoratedClassWithAngularFeaturesDiagnostic(node)] };
28158 }
28159 const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, flags, this.annotateForClosureCompiler);
28160 if (directiveResult === undefined) {
28161 return {};
28162 }
28163 const analysis = directiveResult.metadata;
28164 let providersRequiringFactory = null;
28165 if (directiveResult !== undefined && directiveResult.decorator.has('providers')) {
28166 providersRequiringFactory = resolveProvidersRequiringFactory(directiveResult.decorator.get('providers'), this.reflector, this.evaluator);
28167 }
28168 return {
28169 analysis: {
28170 inputs: directiveResult.inputs,
28171 outputs: directiveResult.outputs,
28172 meta: analysis,
28173 metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
28174 baseClass: readBaseClass$1(node, this.reflector, this.evaluator),
28175 typeCheckMeta: extractDirectiveTypeCheckMeta(node, directiveResult.inputs, this.reflector),
28176 providersRequiringFactory,
28177 isPoisoned: false,
28178 isStructural: directiveResult.isStructural,
28179 }
28180 };
28181 }
28182 register(node, analysis) {
28183 // Register this directive's information with the `MetadataRegistry`. This ensures that
28184 // the information about the directive is available during the compile() phase.
28185 const ref = new Reference$1(node);
28186 this.metaRegistry.registerDirectiveMetadata(Object.assign(Object.assign({ ref, name: node.name.text, selector: analysis.meta.selector, exportAs: analysis.meta.exportAs, inputs: analysis.inputs, outputs: analysis.outputs, queries: analysis.meta.queries.map(query => query.propertyName), isComponent: false, baseClass: analysis.baseClass }, analysis.typeCheckMeta), { isPoisoned: analysis.isPoisoned, isStructural: analysis.isStructural }));
28187 this.injectableRegistry.registerInjectable(node);
28188 }
28189 resolve(node, analysis) {
28190 const diagnostics = [];
28191 if (analysis.providersRequiringFactory !== null &&
28192 analysis.meta.providers instanceof WrappedNodeExpr) {
28193 const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.meta.providers.node, this.injectableRegistry);
28194 diagnostics.push(...providerDiagnostics);
28195 }
28196 const directiveDiagnostics = getDirectiveDiagnostics(node, this.metaReader, this.evaluator, this.reflector, this.scopeRegistry, 'Directive');
28197 if (directiveDiagnostics !== null) {
28198 diagnostics.push(...directiveDiagnostics);
28199 }
28200 return { diagnostics: diagnostics.length > 0 ? diagnostics : undefined };
28201 }
28202 compileFull(node, analysis, resolution, pool) {
28203 const def = compileDirectiveFromMetadata(analysis.meta, pool, makeBindingParser());
28204 return this.compileDirective(analysis, def);
28205 }
28206 compilePartial(node, analysis, resolution) {
28207 const def = compileDeclareDirectiveFromMetadata(analysis.meta);
28208 return this.compileDirective(analysis, def);
28209 }
28210 compileDirective(analysis, { expression: initializer, type }) {
28211 const factoryRes = compileNgFactoryDefField(Object.assign(Object.assign({}, analysis.meta), { injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Directive }));
28212 if (analysis.metadataStmt !== null) {
28213 factoryRes.statements.push(analysis.metadataStmt);
28214 }
28215 return [
28216 factoryRes, {
28217 name: 'ɵdir',
28218 initializer,
28219 statements: [],
28220 type,
28221 }
28222 ];
28223 }
28224 /**
28225 * Checks if a given class uses Angular features and returns the TypeScript node
28226 * that indicated the usage. Classes are considered using Angular features if they
28227 * contain class members that are either decorated with a known Angular decorator,
28228 * or if they correspond to a known Angular lifecycle hook.
28229 */
28230 findClassFieldWithAngularFeatures(node) {
28231 return this.reflector.getMembersOfClass(node).find(member => {
28232 if (!member.isStatic && member.kind === ClassMemberKind.Method &&
28233 LIFECYCLE_HOOKS.has(member.name)) {
28234 return true;
28235 }
28236 if (member.decorators) {
28237 return member.decorators.some(decorator => FIELD_DECORATORS.some(decoratorName => isAngularDecorator(decorator, decoratorName, this.isCore)));
28238 }
28239 return false;
28240 });
28241 }
28242 }
28243 /**
28244 * Helper function to extract metadata from a `Directive` or `Component`. `Directive`s without a
28245 * selector are allowed to be used for abstract base classes. These abstract directives should not
28246 * appear in the declarations of an `NgModule` and additional verification is done when processing
28247 * the module.
28248 */
28249 function extractDirectiveMetadata(clazz, decorator, reflector, evaluator, defaultImportRecorder, isCore, flags, annotateForClosureCompiler, defaultSelector = null) {
28250 let directive;
28251 if (decorator === null || decorator.args === null || decorator.args.length === 0) {
28252 directive = new Map();
28253 }
28254 else if (decorator.args.length !== 1) {
28255 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @${decorator.name} decorator`);
28256 }
28257 else {
28258 const meta = unwrapExpression(decorator.args[0]);
28259 if (!ts$1.isObjectLiteralExpression(meta)) {
28260 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, `@${decorator.name} argument must be an object literal`);
28261 }
28262 directive = reflectObjectLiteral(meta);
28263 }
28264 if (directive.has('jit')) {
28265 // The only allowed value is true, so there's no need to expand further.
28266 return undefined;
28267 }
28268 const members = reflector.getMembersOfClass(clazz);
28269 // Precompute a list of ts.ClassElements that have decorators. This includes things like @Input,
28270 // @Output, @HostBinding, etc.
28271 const decoratedElements = members.filter(member => !member.isStatic && member.decorators !== null);
28272 const coreModule = isCore ? undefined : '@angular/core';
28273 // Construct the map of inputs both from the @Directive/@Component
28274 // decorator, and the decorated
28275 // fields.
28276 const inputsFromMeta = parseFieldToPropertyMapping(directive, 'inputs', evaluator);
28277 const inputsFromFields = parseDecoratedFields(filterToMembersWithDecorator(decoratedElements, 'Input', coreModule), evaluator, resolveInput);
28278 // And outputs.
28279 const outputsFromMeta = parseFieldToPropertyMapping(directive, 'outputs', evaluator);
28280 const outputsFromFields = parseDecoratedFields(filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), evaluator, resolveOutput);
28281 // Construct the list of queries.
28282 const contentChildFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ContentChild', coreModule), reflector, evaluator);
28283 const contentChildrenFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ContentChildren', coreModule), reflector, evaluator);
28284 const queries = [...contentChildFromFields, ...contentChildrenFromFields];
28285 // Construct the list of view queries.
28286 const viewChildFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ViewChild', coreModule), reflector, evaluator);
28287 const viewChildrenFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ViewChildren', coreModule), reflector, evaluator);
28288 const viewQueries = [...viewChildFromFields, ...viewChildrenFromFields];
28289 if (directive.has('queries')) {
28290 const queriesFromDecorator = extractQueriesFromDecorator(directive.get('queries'), reflector, evaluator, isCore);
28291 queries.push(...queriesFromDecorator.content);
28292 viewQueries.push(...queriesFromDecorator.view);
28293 }
28294 // Parse the selector.
28295 let selector = defaultSelector;
28296 if (directive.has('selector')) {
28297 const expr = directive.get('selector');
28298 const resolved = evaluator.evaluate(expr);
28299 if (typeof resolved !== 'string') {
28300 throw createValueHasWrongTypeError(expr, resolved, `selector must be a string`);
28301 }
28302 // use default selector in case selector is an empty string
28303 selector = resolved === '' ? defaultSelector : resolved;
28304 if (!selector) {
28305 throw new FatalDiagnosticError(ErrorCode.DIRECTIVE_MISSING_SELECTOR, expr, `Directive ${clazz.name.text} has no selector, please add it!`);
28306 }
28307 }
28308 const host = extractHostBindings$1(decoratedElements, evaluator, coreModule, directive);
28309 const providers = directive.has('providers') ?
28310 new WrappedNodeExpr(annotateForClosureCompiler ?
28311 wrapFunctionExpressionsInParens(directive.get('providers')) :
28312 directive.get('providers')) :
28313 null;
28314 // Determine if `ngOnChanges` is a lifecycle hook defined on the component.
28315 const usesOnChanges = members.some(member => !member.isStatic && member.kind === ClassMemberKind.Method &&
28316 member.name === 'ngOnChanges');
28317 // Parse exportAs.
28318 let exportAs = null;
28319 if (directive.has('exportAs')) {
28320 const expr = directive.get('exportAs');
28321 const resolved = evaluator.evaluate(expr);
28322 if (typeof resolved !== 'string') {
28323 throw createValueHasWrongTypeError(expr, resolved, `exportAs must be a string`);
28324 }
28325 exportAs = resolved.split(',').map(part => part.trim());
28326 }
28327 const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
28328 let ctorDeps;
28329 // Non-abstract directives (those with a selector) require valid constructor dependencies, whereas
28330 // abstract directives are allowed to have invalid dependencies, given that a subclass may call
28331 // the constructor explicitly.
28332 if (selector !== null) {
28333 ctorDeps = validateConstructorDependencies(clazz, rawCtorDeps);
28334 }
28335 else {
28336 ctorDeps = unwrapConstructorDependencies(rawCtorDeps);
28337 }
28338 const isStructural = ctorDeps !== null && ctorDeps !== 'invalid' && ctorDeps.some(dep => {
28339 if (dep.resolved !== R3ResolvedDependencyType.Token || !(dep.token instanceof ExternalExpr)) {
28340 return false;
28341 }
28342 if (dep.token.value.moduleName !== '@angular/core' || dep.token.value.name !== 'TemplateRef') {
28343 return false;
28344 }
28345 return true;
28346 });
28347 // Detect if the component inherits from another class
28348 const usesInheritance = reflector.hasBaseClass(clazz);
28349 const type = wrapTypeReference(reflector, clazz);
28350 const internalType = new WrappedNodeExpr(reflector.getInternalNameOfClass(clazz));
28351 const inputs = ClassPropertyMapping.fromMappedObject(Object.assign(Object.assign({}, inputsFromMeta), inputsFromFields));
28352 const outputs = ClassPropertyMapping.fromMappedObject(Object.assign(Object.assign({}, outputsFromMeta), outputsFromFields));
28353 const metadata = {
28354 name: clazz.name.text,
28355 deps: ctorDeps,
28356 host,
28357 lifecycle: {
28358 usesOnChanges,
28359 },
28360 inputs: inputs.toJointMappedObject(),
28361 outputs: outputs.toDirectMappedObject(),
28362 queries,
28363 viewQueries,
28364 selector,
28365 fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE),
28366 type,
28367 internalType,
28368 typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
28369 typeSourceSpan: createSourceSpan(clazz.name),
28370 usesInheritance,
28371 exportAs,
28372 providers
28373 };
28374 return {
28375 decorator: directive,
28376 metadata,
28377 inputs,
28378 outputs,
28379 isStructural,
28380 };
28381 }
28382 function extractQueryMetadata(exprNode, name, args, propertyName, reflector, evaluator) {
28383 if (args.length === 0) {
28384 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, exprNode, `@${name} must have arguments`);
28385 }
28386 const first = name === 'ViewChild' || name === 'ContentChild';
28387 const node = unwrapForwardRef(args[0], reflector);
28388 const arg = evaluator.evaluate(node);
28389 /** Whether or not this query should collect only static results (see view/api.ts) */
28390 let isStatic = false;
28391 // Extract the predicate
28392 let predicate = null;
28393 if (arg instanceof Reference$1 || arg instanceof DynamicValue) {
28394 // References and predicates that could not be evaluated statically are emitted as is.
28395 predicate = new WrappedNodeExpr(node);
28396 }
28397 else if (typeof arg === 'string') {
28398 predicate = [arg];
28399 }
28400 else if (isStringArrayOrDie(arg, `@${name} predicate`, node)) {
28401 predicate = arg;
28402 }
28403 else {
28404 throw createValueHasWrongTypeError(node, arg, `@${name} predicate cannot be interpreted`);
28405 }
28406 // Extract the read and descendants options.
28407 let read = null;
28408 // The default value for descendants is true for every decorator except @ContentChildren.
28409 let descendants = name !== 'ContentChildren';
28410 let emitDistinctChangesOnly = emitDistinctChangesOnlyDefaultValue;
28411 if (args.length === 2) {
28412 const optionsExpr = unwrapExpression(args[1]);
28413 if (!ts$1.isObjectLiteralExpression(optionsExpr)) {
28414 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, optionsExpr, `@${name} options must be an object literal`);
28415 }
28416 const options = reflectObjectLiteral(optionsExpr);
28417 if (options.has('read')) {
28418 read = new WrappedNodeExpr(options.get('read'));
28419 }
28420 if (options.has('descendants')) {
28421 const descendantsExpr = options.get('descendants');
28422 const descendantsValue = evaluator.evaluate(descendantsExpr);
28423 if (typeof descendantsValue !== 'boolean') {
28424 throw createValueHasWrongTypeError(descendantsExpr, descendantsValue, `@${name} options.descendants must be a boolean`);
28425 }
28426 descendants = descendantsValue;
28427 }
28428 if (options.has('emitDistinctChangesOnly')) {
28429 const emitDistinctChangesOnlyExpr = options.get('emitDistinctChangesOnly');
28430 const emitDistinctChangesOnlyValue = evaluator.evaluate(emitDistinctChangesOnlyExpr);
28431 if (typeof emitDistinctChangesOnlyValue !== 'boolean') {
28432 throw createValueHasWrongTypeError(emitDistinctChangesOnlyExpr, emitDistinctChangesOnlyValue, `@${name} options.emitDistinctChangesOnlys must be a boolean`);
28433 }
28434 emitDistinctChangesOnly = emitDistinctChangesOnlyValue;
28435 }
28436 if (options.has('static')) {
28437 const staticValue = evaluator.evaluate(options.get('static'));
28438 if (typeof staticValue !== 'boolean') {
28439 throw createValueHasWrongTypeError(node, staticValue, `@${name} options.static must be a boolean`);
28440 }
28441 isStatic = staticValue;
28442 }
28443 }
28444 else if (args.length > 2) {
28445 // Too many arguments.
28446 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, node, `@${name} has too many arguments`);
28447 }
28448 return {
28449 propertyName,
28450 predicate,
28451 first,
28452 descendants,
28453 read,
28454 static: isStatic,
28455 emitDistinctChangesOnly,
28456 };
28457 }
28458 function extractQueriesFromDecorator(queryData, reflector, evaluator, isCore) {
28459 const content = [], view = [];
28460 if (!ts$1.isObjectLiteralExpression(queryData)) {
28461 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator queries metadata must be an object literal');
28462 }
28463 reflectObjectLiteral(queryData).forEach((queryExpr, propertyName) => {
28464 queryExpr = unwrapExpression(queryExpr);
28465 if (!ts$1.isNewExpression(queryExpr)) {
28466 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator query metadata must be an instance of a query type');
28467 }
28468 const queryType = ts$1.isPropertyAccessExpression(queryExpr.expression) ?
28469 queryExpr.expression.name :
28470 queryExpr.expression;
28471 if (!ts$1.isIdentifier(queryType)) {
28472 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator query metadata must be an instance of a query type');
28473 }
28474 const type = reflector.getImportOfIdentifier(queryType);
28475 if (type === null || (!isCore && type.from !== '@angular/core') ||
28476 !QUERY_TYPES.has(type.name)) {
28477 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator query metadata must be an instance of a query type');
28478 }
28479 const query = extractQueryMetadata(queryExpr, type.name, queryExpr.arguments || [], propertyName, reflector, evaluator);
28480 if (type.name.startsWith('Content')) {
28481 content.push(query);
28482 }
28483 else {
28484 view.push(query);
28485 }
28486 });
28487 return { content, view };
28488 }
28489 function isStringArrayOrDie(value, name, node) {
28490 if (!Array.isArray(value)) {
28491 return false;
28492 }
28493 for (let i = 0; i < value.length; i++) {
28494 if (typeof value[i] !== 'string') {
28495 throw createValueHasWrongTypeError(node, value[i], `Failed to resolve ${name} at position ${i} to a string`);
28496 }
28497 }
28498 return true;
28499 }
28500 function parseFieldArrayValue(directive, field, evaluator) {
28501 if (!directive.has(field)) {
28502 return null;
28503 }
28504 // Resolve the field of interest from the directive metadata to a string[].
28505 const expression = directive.get(field);
28506 const value = evaluator.evaluate(expression);
28507 if (!isStringArrayOrDie(value, field, expression)) {
28508 throw createValueHasWrongTypeError(expression, value, `Failed to resolve @Directive.${field} to a string array`);
28509 }
28510 return value;
28511 }
28512 /**
28513 * Interpret property mapping fields on the decorator (e.g. inputs or outputs) and return the
28514 * correctly shaped metadata object.
28515 */
28516 function parseFieldToPropertyMapping(directive, field, evaluator) {
28517 const metaValues = parseFieldArrayValue(directive, field, evaluator);
28518 if (!metaValues) {
28519 return EMPTY_OBJECT;
28520 }
28521 return metaValues.reduce((results, value) => {
28522 // Either the value is 'field' or 'field: property'. In the first case, `property` will
28523 // be undefined, in which case the field name should also be used as the property name.
28524 const [field, property] = value.split(':', 2).map(str => str.trim());
28525 results[field] = property || field;
28526 return results;
28527 }, {});
28528 }
28529 /**
28530 * Parse property decorators (e.g. `Input` or `Output`) and return the correctly shaped metadata
28531 * object.
28532 */
28533 function parseDecoratedFields(fields, evaluator, mapValueResolver) {
28534 return fields.reduce((results, field) => {
28535 const fieldName = field.member.name;
28536 field.decorators.forEach(decorator => {
28537 // The decorator either doesn't have an argument (@Input()) in which case the property
28538 // name is used, or it has one argument (@Output('named')).
28539 if (decorator.args == null || decorator.args.length === 0) {
28540 results[fieldName] = fieldName;
28541 }
28542 else if (decorator.args.length === 1) {
28543 const property = evaluator.evaluate(decorator.args[0]);
28544 if (typeof property !== 'string') {
28545 throw createValueHasWrongTypeError(Decorator.nodeForError(decorator), property, `@${decorator.name} decorator argument must resolve to a string`);
28546 }
28547 results[fieldName] = mapValueResolver(property, fieldName);
28548 }
28549 else {
28550 // Too many arguments.
28551 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `@${decorator.name} can have at most one argument, got ${decorator.args.length} argument(s)`);
28552 }
28553 });
28554 return results;
28555 }, {});
28556 }
28557 function resolveInput(publicName, internalName) {
28558 return [publicName, internalName];
28559 }
28560 function resolveOutput(publicName, internalName) {
28561 return publicName;
28562 }
28563 function queriesFromFields(fields, reflector, evaluator) {
28564 return fields.map(({ member, decorators }) => {
28565 const decorator = decorators[0];
28566 const node = member.node || Decorator.nodeForError(decorator);
28567 // Throw in case of `@Input() @ContentChild('foo') foo: any`, which is not supported in Ivy
28568 if (member.decorators.some(v => v.name === 'Input')) {
28569 throw new FatalDiagnosticError(ErrorCode.DECORATOR_COLLISION, node, 'Cannot combine @Input decorators with query decorators');
28570 }
28571 if (decorators.length !== 1) {
28572 throw new FatalDiagnosticError(ErrorCode.DECORATOR_COLLISION, node, 'Cannot have multiple query decorators on the same class member');
28573 }
28574 else if (!isPropertyTypeMember(member)) {
28575 throw new FatalDiagnosticError(ErrorCode.DECORATOR_UNEXPECTED, node, 'Query decorator must go on a property-type member');
28576 }
28577 return extractQueryMetadata(node, decorator.name, decorator.args || [], member.name, reflector, evaluator);
28578 });
28579 }
28580 function isPropertyTypeMember(member) {
28581 return member.kind === ClassMemberKind.Getter || member.kind === ClassMemberKind.Setter ||
28582 member.kind === ClassMemberKind.Property;
28583 }
28584 function evaluateHostExpressionBindings(hostExpr, evaluator) {
28585 const hostMetaMap = evaluator.evaluate(hostExpr);
28586 if (!(hostMetaMap instanceof Map)) {
28587 throw createValueHasWrongTypeError(hostExpr, hostMetaMap, `Decorator host metadata must be an object`);
28588 }
28589 const hostMetadata = {};
28590 hostMetaMap.forEach((value, key) => {
28591 // Resolve Enum references to their declared value.
28592 if (value instanceof EnumValue) {
28593 value = value.resolved;
28594 }
28595 if (typeof key !== 'string') {
28596 throw createValueHasWrongTypeError(hostExpr, key, `Decorator host metadata must be a string -> string object, but found unparseable key`);
28597 }
28598 if (typeof value == 'string') {
28599 hostMetadata[key] = value;
28600 }
28601 else if (value instanceof DynamicValue) {
28602 hostMetadata[key] = new WrappedNodeExpr(value.node);
28603 }
28604 else {
28605 throw createValueHasWrongTypeError(hostExpr, value, `Decorator host metadata must be a string -> string object, but found unparseable value`);
28606 }
28607 });
28608 const bindings = parseHostBindings(hostMetadata);
28609 const errors = verifyHostBindings(bindings, createSourceSpan(hostExpr));
28610 if (errors.length > 0) {
28611 throw new FatalDiagnosticError(
28612 // TODO: provide more granular diagnostic and output specific host expression that
28613 // triggered an error instead of the whole host object.
28614 ErrorCode.HOST_BINDING_PARSE_ERROR, hostExpr, errors.map((error) => error.msg).join('\n'));
28615 }
28616 return bindings;
28617 }
28618 function extractHostBindings$1(members, evaluator, coreModule, metadata) {
28619 let bindings;
28620 if (metadata && metadata.has('host')) {
28621 bindings = evaluateHostExpressionBindings(metadata.get('host'), evaluator);
28622 }
28623 else {
28624 bindings = parseHostBindings({});
28625 }
28626 filterToMembersWithDecorator(members, 'HostBinding', coreModule)
28627 .forEach(({ member, decorators }) => {
28628 decorators.forEach(decorator => {
28629 let hostPropertyName = member.name;
28630 if (decorator.args !== null && decorator.args.length > 0) {
28631 if (decorator.args.length !== 1) {
28632 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `@HostBinding can have at most one argument, got ${decorator.args.length} argument(s)`);
28633 }
28634 const resolved = evaluator.evaluate(decorator.args[0]);
28635 if (typeof resolved !== 'string') {
28636 throw createValueHasWrongTypeError(Decorator.nodeForError(decorator), resolved, `@HostBinding's argument must be a string`);
28637 }
28638 hostPropertyName = resolved;
28639 }
28640 // Since this is a decorator, we know that the value is a class member. Always access it
28641 // through `this` so that further down the line it can't be confused for a literal value
28642 // (e.g. if there's a property called `true`). There is no size penalty, because all
28643 // values (except literals) are converted to `ctx.propName` eventually.
28644 bindings.properties[hostPropertyName] = getSafePropertyAccessString('this', member.name);
28645 });
28646 });
28647 filterToMembersWithDecorator(members, 'HostListener', coreModule)
28648 .forEach(({ member, decorators }) => {
28649 decorators.forEach(decorator => {
28650 let eventName = member.name;
28651 let args = [];
28652 if (decorator.args !== null && decorator.args.length > 0) {
28653 if (decorator.args.length > 2) {
28654 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, decorator.args[2], `@HostListener can have at most two arguments`);
28655 }
28656 const resolved = evaluator.evaluate(decorator.args[0]);
28657 if (typeof resolved !== 'string') {
28658 throw createValueHasWrongTypeError(decorator.args[0], resolved, `@HostListener's event name argument must be a string`);
28659 }
28660 eventName = resolved;
28661 if (decorator.args.length === 2) {
28662 const expression = decorator.args[1];
28663 const resolvedArgs = evaluator.evaluate(decorator.args[1]);
28664 if (!isStringArrayOrDie(resolvedArgs, '@HostListener.args', expression)) {
28665 throw createValueHasWrongTypeError(decorator.args[1], resolvedArgs, `@HostListener's second argument must be a string array`);
28666 }
28667 args = resolvedArgs;
28668 }
28669 }
28670 bindings.listeners[eventName] = `${member.name}(${args.join(',')})`;
28671 });
28672 });
28673 return bindings;
28674 }
28675 const QUERY_TYPES = new Set([
28676 'ContentChild',
28677 'ContentChildren',
28678 'ViewChild',
28679 'ViewChildren',
28680 ]);
28681
28682 /**
28683 * @license
28684 * Copyright Google LLC All Rights Reserved.
28685 *
28686 * Use of this source code is governed by an MIT-style license that can be
28687 * found in the LICENSE file at https://angular.io/license
28688 */
28689 const EMPTY_MAP = new Map();
28690 const EMPTY_ARRAY = [];
28691 /**
28692 * `DecoratorHandler` which handles the `@Component` annotation.
28693 */
28694 class ComponentDecoratorHandler {
28695 constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, cycleHandlingStrategy, refEmitter, defaultImportRecorder, depTracker, injectableRegistry, annotateForClosureCompiler) {
28696 this.reflector = reflector;
28697 this.evaluator = evaluator;
28698 this.metaRegistry = metaRegistry;
28699 this.metaReader = metaReader;
28700 this.scopeReader = scopeReader;
28701 this.scopeRegistry = scopeRegistry;
28702 this.typeCheckScopeRegistry = typeCheckScopeRegistry;
28703 this.resourceRegistry = resourceRegistry;
28704 this.isCore = isCore;
28705 this.resourceLoader = resourceLoader;
28706 this.rootDirs = rootDirs;
28707 this.defaultPreserveWhitespaces = defaultPreserveWhitespaces;
28708 this.i18nUseExternalIds = i18nUseExternalIds;
28709 this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
28710 this.usePoisonedData = usePoisonedData;
28711 this.i18nNormalizeLineEndingsInICUs = i18nNormalizeLineEndingsInICUs;
28712 this.moduleResolver = moduleResolver;
28713 this.cycleAnalyzer = cycleAnalyzer;
28714 this.cycleHandlingStrategy = cycleHandlingStrategy;
28715 this.refEmitter = refEmitter;
28716 this.defaultImportRecorder = defaultImportRecorder;
28717 this.depTracker = depTracker;
28718 this.injectableRegistry = injectableRegistry;
28719 this.annotateForClosureCompiler = annotateForClosureCompiler;
28720 this.literalCache = new Map();
28721 this.elementSchemaRegistry = new DomElementSchemaRegistry();
28722 /**
28723 * During the asynchronous preanalyze phase, it's necessary to parse the template to extract
28724 * any potential <link> tags which might need to be loaded. This cache ensures that work is not
28725 * thrown away, and the parsed template is reused during the analyze phase.
28726 */
28727 this.preanalyzeTemplateCache = new Map();
28728 this.precedence = HandlerPrecedence.PRIMARY;
28729 this.name = ComponentDecoratorHandler.name;
28730 }
28731 detect(node, decorators) {
28732 if (!decorators) {
28733 return undefined;
28734 }
28735 const decorator = findAngularDecorator(decorators, 'Component', this.isCore);
28736 if (decorator !== undefined) {
28737 return {
28738 trigger: decorator.node,
28739 decorator,
28740 metadata: decorator,
28741 };
28742 }
28743 else {
28744 return undefined;
28745 }
28746 }
28747 preanalyze(node, decorator) {
28748 // In preanalyze, resource URLs associated with the component are asynchronously preloaded via
28749 // the resourceLoader. This is the only time async operations are allowed for a component.
28750 // These resources are:
28751 //
28752 // - the templateUrl, if there is one
28753 // - any styleUrls if present
28754 // - any stylesheets referenced from <link> tags in the template itself
28755 //
28756 // As a result of the last one, the template must be parsed as part of preanalysis to extract
28757 // <link> tags, which may involve waiting for the templateUrl to be resolved first.
28758 // If preloading isn't possible, then skip this step.
28759 if (!this.resourceLoader.canPreload) {
28760 return undefined;
28761 }
28762 const meta = this._resolveLiteral(decorator);
28763 const component = reflectObjectLiteral(meta);
28764 const containingFile = node.getSourceFile().fileName;
28765 const resolveStyleUrl = (styleUrl, nodeForError, resourceType) => {
28766 const resourceUrl = this._resolveResourceOrThrow(styleUrl, containingFile, nodeForError, resourceType);
28767 return this.resourceLoader.preload(resourceUrl);
28768 };
28769 // A Promise that waits for the template and all <link>ed styles within it to be preloaded.
28770 const templateAndTemplateStyleResources = this._preloadAndParseTemplate(node, decorator, component, containingFile)
28771 .then((template) => {
28772 if (template === null) {
28773 return undefined;
28774 }
28775 const nodeForError = getTemplateDeclarationNodeForError(template.declaration);
28776 return Promise
28777 .all(template.styleUrls.map(styleUrl => resolveStyleUrl(styleUrl, nodeForError, 1 /* StylesheetFromTemplate */)))
28778 .then(() => undefined);
28779 });
28780 // Extract all the styleUrls in the decorator.
28781 const componentStyleUrls = this._extractComponentStyleUrls(component);
28782 if (componentStyleUrls === null) {
28783 // A fast path exists if there are no styleUrls, to just wait for
28784 // templateAndTemplateStyleResources.
28785 return templateAndTemplateStyleResources;
28786 }
28787 else {
28788 // Wait for both the template and all styleUrl resources to resolve.
28789 return Promise
28790 .all([
28791 templateAndTemplateStyleResources,
28792 ...componentStyleUrls.map(styleUrl => resolveStyleUrl(styleUrl.url, styleUrl.nodeForError, 2 /* StylesheetFromDecorator */))
28793 ])
28794 .then(() => undefined);
28795 }
28796 }
28797 analyze(node, decorator, flags = HandlerFlags.NONE) {
28798 var _a;
28799 const containingFile = node.getSourceFile().fileName;
28800 this.literalCache.delete(decorator);
28801 // @Component inherits @Directive, so begin by extracting the @Directive metadata and building
28802 // on it.
28803 const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, flags, this.annotateForClosureCompiler, this.elementSchemaRegistry.getDefaultComponentElementName());
28804 if (directiveResult === undefined) {
28805 // `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
28806 // case, compilation of the decorator is skipped. Returning an empty object signifies
28807 // that no analysis was produced.
28808 return {};
28809 }
28810 // Next, read the `@Component`-specific fields.
28811 const { decorator: component, metadata, inputs, outputs } = directiveResult;
28812 // Go through the root directories for this project, and select the one with the smallest
28813 // relative path representation.
28814 const relativeContextFilePath = this.rootDirs.reduce((previous, rootDir) => {
28815 const candidate = relative(absoluteFrom(rootDir), absoluteFrom(containingFile));
28816 if (previous === undefined || candidate.length < previous.length) {
28817 return candidate;
28818 }
28819 else {
28820 return previous;
28821 }
28822 }, undefined);
28823 // Note that we could technically combine the `viewProvidersRequiringFactory` and
28824 // `providersRequiringFactory` into a single set, but we keep the separate so that
28825 // we can distinguish where an error is coming from when logging the diagnostics in `resolve`.
28826 let viewProvidersRequiringFactory = null;
28827 let providersRequiringFactory = null;
28828 let wrappedViewProviders = null;
28829 if (component.has('viewProviders')) {
28830 const viewProviders = component.get('viewProviders');
28831 viewProvidersRequiringFactory =
28832 resolveProvidersRequiringFactory(viewProviders, this.reflector, this.evaluator);
28833 wrappedViewProviders = new WrappedNodeExpr(this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(viewProviders) :
28834 viewProviders);
28835 }
28836 if (component.has('providers')) {
28837 providersRequiringFactory = resolveProvidersRequiringFactory(component.get('providers'), this.reflector, this.evaluator);
28838 }
28839 // Parse the template.
28840 // If a preanalyze phase was executed, the template may already exist in parsed form, so check
28841 // the preanalyzeTemplateCache.
28842 // Extract a closure of the template parsing code so that it can be reparsed with different
28843 // options if needed, like in the indexing pipeline.
28844 let template;
28845 if (this.preanalyzeTemplateCache.has(node)) {
28846 // The template was parsed in preanalyze. Use it and delete it to save memory.
28847 const preanalyzed = this.preanalyzeTemplateCache.get(node);
28848 this.preanalyzeTemplateCache.delete(node);
28849 template = preanalyzed;
28850 }
28851 else {
28852 const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
28853 template = this.extractTemplate(node, templateDecl);
28854 }
28855 const templateResource = template.isInline ? { path: null, expression: component.get('template') } : {
28856 path: absoluteFrom(template.declaration.resolvedTemplateUrl),
28857 expression: template.sourceMapping.node
28858 };
28859 // Figure out the set of styles. The ordering here is important: external resources (styleUrls)
28860 // precede inline styles, and styles defined in the template override styles defined in the
28861 // component.
28862 let styles = [];
28863 const styleResources = this._extractStyleResources(component, containingFile);
28864 const styleUrls = [
28865 ...this._extractComponentStyleUrls(component), ...this._extractTemplateStyleUrls(template)
28866 ];
28867 for (const styleUrl of styleUrls) {
28868 const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
28869 2 /* StylesheetFromDecorator */ :
28870 1 /* StylesheetFromTemplate */;
28871 const resourceUrl = this._resolveResourceOrThrow(styleUrl.url, containingFile, styleUrl.nodeForError, resourceType);
28872 const resourceStr = this.resourceLoader.load(resourceUrl);
28873 styles.push(resourceStr);
28874 if (this.depTracker !== null) {
28875 this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl));
28876 }
28877 }
28878 let inlineStyles = null;
28879 if (component.has('styles')) {
28880 const litStyles = parseFieldArrayValue(component, 'styles', this.evaluator);
28881 if (litStyles !== null) {
28882 inlineStyles = [...litStyles];
28883 styles.push(...litStyles);
28884 }
28885 }
28886 if (template.styles.length > 0) {
28887 styles.push(...template.styles);
28888 }
28889 const encapsulation = this._resolveEnumValue(component, 'encapsulation', 'ViewEncapsulation') || 0;
28890 const changeDetection = this._resolveEnumValue(component, 'changeDetection', 'ChangeDetectionStrategy');
28891 let animations = null;
28892 if (component.has('animations')) {
28893 animations = new WrappedNodeExpr(component.get('animations'));
28894 }
28895 const output = {
28896 analysis: {
28897 baseClass: readBaseClass$1(node, this.reflector, this.evaluator),
28898 inputs,
28899 outputs,
28900 meta: Object.assign(Object.assign({}, metadata), { template: {
28901 nodes: template.nodes,
28902 ngContentSelectors: template.ngContentSelectors,
28903 }, encapsulation, interpolation: (_a = template.interpolationConfig) !== null && _a !== void 0 ? _a : DEFAULT_INTERPOLATION_CONFIG, styles,
28904 // These will be replaced during the compilation step, after all `NgModule`s have been
28905 // analyzed and the full compilation scope for the component can be realized.
28906 animations, viewProviders: wrappedViewProviders, i18nUseExternalIds: this.i18nUseExternalIds, relativeContextFilePath }),
28907 typeCheckMeta: extractDirectiveTypeCheckMeta(node, inputs, this.reflector),
28908 metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
28909 template,
28910 providersRequiringFactory,
28911 viewProvidersRequiringFactory,
28912 inlineStyles,
28913 styleUrls,
28914 resources: {
28915 styles: styleResources,
28916 template: templateResource,
28917 },
28918 isPoisoned: false,
28919 },
28920 };
28921 if (changeDetection !== null) {
28922 output.analysis.meta.changeDetection = changeDetection;
28923 }
28924 return output;
28925 }
28926 register(node, analysis) {
28927 // Register this component's information with the `MetadataRegistry`. This ensures that
28928 // the information about the component is available during the compile() phase.
28929 const ref = new Reference$1(node);
28930 this.metaRegistry.registerDirectiveMetadata(Object.assign(Object.assign({ ref, name: node.name.text, selector: analysis.meta.selector, exportAs: analysis.meta.exportAs, inputs: analysis.inputs, outputs: analysis.outputs, queries: analysis.meta.queries.map(query => query.propertyName), isComponent: true, baseClass: analysis.baseClass }, analysis.typeCheckMeta), { isPoisoned: analysis.isPoisoned, isStructural: false }));
28931 this.resourceRegistry.registerResources(analysis.resources, node);
28932 this.injectableRegistry.registerInjectable(node);
28933 }
28934 index(context, node, analysis) {
28935 if (analysis.isPoisoned && !this.usePoisonedData) {
28936 return null;
28937 }
28938 const scope = this.scopeReader.getScopeForComponent(node);
28939 const selector = analysis.meta.selector;
28940 const matcher = new SelectorMatcher();
28941 if (scope !== null) {
28942 if ((scope.compilation.isPoisoned || scope.exported.isPoisoned) && !this.usePoisonedData) {
28943 // Don't bother indexing components which had erroneous scopes, unless specifically
28944 // requested.
28945 return null;
28946 }
28947 for (const directive of scope.compilation.directives) {
28948 if (directive.selector !== null) {
28949 matcher.addSelectables(CssSelector.parse(directive.selector), directive);
28950 }
28951 }
28952 }
28953 const binder = new R3TargetBinder(matcher);
28954 const boundTemplate = binder.bind({ template: analysis.template.diagNodes });
28955 context.addComponent({
28956 declaration: node,
28957 selector,
28958 boundTemplate,
28959 templateMeta: {
28960 isInline: analysis.template.isInline,
28961 file: analysis.template.file,
28962 },
28963 });
28964 }
28965 typeCheck(ctx, node, meta) {
28966 if (this.typeCheckScopeRegistry === null || !ts$1.isClassDeclaration(node)) {
28967 return;
28968 }
28969 if (meta.isPoisoned && !this.usePoisonedData) {
28970 return;
28971 }
28972 const scope = this.typeCheckScopeRegistry.getTypeCheckScope(node);
28973 if (scope.isPoisoned && !this.usePoisonedData) {
28974 // Don't type-check components that had errors in their scopes, unless requested.
28975 return;
28976 }
28977 const binder = new R3TargetBinder(scope.matcher);
28978 ctx.addTemplate(new Reference$1(node), binder, meta.template.diagNodes, scope.pipes, scope.schemas, meta.template.sourceMapping, meta.template.file, meta.template.errors);
28979 }
28980 resolve(node, analysis) {
28981 if (analysis.isPoisoned && !this.usePoisonedData) {
28982 return {};
28983 }
28984 const context = node.getSourceFile();
28985 // Check whether this component was registered with an NgModule. If so, it should be compiled
28986 // under that module's compilation scope.
28987 const scope = this.scopeReader.getScopeForComponent(node);
28988 let metadata = analysis.meta;
28989 const data = {
28990 directives: EMPTY_ARRAY,
28991 pipes: EMPTY_MAP,
28992 declarationListEmitMode: 0 /* Direct */,
28993 };
28994 if (scope !== null && (!scope.compilation.isPoisoned || this.usePoisonedData)) {
28995 const matcher = new SelectorMatcher();
28996 for (const dir of scope.compilation.directives) {
28997 if (dir.selector !== null) {
28998 matcher.addSelectables(CssSelector.parse(dir.selector), dir);
28999 }
29000 }
29001 const pipes = new Map();
29002 for (const pipe of scope.compilation.pipes) {
29003 pipes.set(pipe.name, pipe.ref);
29004 }
29005 // Next, the component template AST is bound using the R3TargetBinder. This produces a
29006 // BoundTarget, which is similar to a ts.TypeChecker.
29007 const binder = new R3TargetBinder(matcher);
29008 const bound = binder.bind({ template: metadata.template.nodes });
29009 const usedDirectives = bound.getUsedDirectives().map(directive => {
29010 return {
29011 ref: directive.ref,
29012 type: this.refEmitter.emit(directive.ref, context),
29013 selector: directive.selector,
29014 inputs: directive.inputs.propertyNames,
29015 outputs: directive.outputs.propertyNames,
29016 exportAs: directive.exportAs,
29017 isComponent: directive.isComponent,
29018 };
29019 });
29020 const usedPipes = [];
29021 for (const pipeName of bound.getUsedPipes()) {
29022 if (!pipes.has(pipeName)) {
29023 continue;
29024 }
29025 const pipe = pipes.get(pipeName);
29026 usedPipes.push({
29027 ref: pipe,
29028 pipeName,
29029 expression: this.refEmitter.emit(pipe, context),
29030 });
29031 }
29032 // Scan through the directives/pipes actually used in the template and check whether any
29033 // import which needs to be generated would create a cycle.
29034 const cyclesFromDirectives = new Map();
29035 for (const usedDirective of usedDirectives) {
29036 const cycle = this._checkForCyclicImport(usedDirective.ref, usedDirective.type, context);
29037 if (cycle !== null) {
29038 cyclesFromDirectives.set(usedDirective, cycle);
29039 }
29040 }
29041 const cyclesFromPipes = new Map();
29042 for (const usedPipe of usedPipes) {
29043 const cycle = this._checkForCyclicImport(usedPipe.ref, usedPipe.expression, context);
29044 if (cycle !== null) {
29045 cyclesFromPipes.set(usedPipe, cycle);
29046 }
29047 }
29048 if (cyclesFromDirectives.size === 0 && cyclesFromPipes.size === 0) {
29049 // No cycle was detected. Record the imports that need to be created in the cycle detector
29050 // so that future cyclic import checks consider their production.
29051 for (const { type } of usedDirectives) {
29052 this._recordSyntheticImport(type, context);
29053 }
29054 for (const { expression } of usedPipes) {
29055 this._recordSyntheticImport(expression, context);
29056 }
29057 // Check whether the directive/pipe arrays in ɵcmp need to be wrapped in closures.
29058 // This is required if any directive/pipe reference is to a declaration in the same file
29059 // but declared after this component.
29060 const wrapDirectivesAndPipesInClosure = usedDirectives.some(dir => isExpressionForwardReference(dir.type, node.name, context)) ||
29061 usedPipes.some(pipe => isExpressionForwardReference(pipe.expression, node.name, context));
29062 data.directives = usedDirectives;
29063 data.pipes = new Map(usedPipes.map(pipe => [pipe.pipeName, pipe.expression]));
29064 data.declarationListEmitMode = wrapDirectivesAndPipesInClosure ?
29065 1 /* Closure */ :
29066 0 /* Direct */;
29067 }
29068 else {
29069 if (this.cycleHandlingStrategy === 0 /* UseRemoteScoping */) {
29070 // Declaring the directiveDefs/pipeDefs arrays directly would require imports that would
29071 // create a cycle. Instead, mark this component as requiring remote scoping, so that the
29072 // NgModule file will take care of setting the directives for the component.
29073 this.scopeRegistry.setComponentRemoteScope(node, usedDirectives.map(dir => dir.ref), usedPipes.map(pipe => pipe.ref));
29074 }
29075 else {
29076 // We are not able to handle this cycle so throw an error.
29077 const relatedMessages = [];
29078 for (const [dir, cycle] of cyclesFromDirectives) {
29079 relatedMessages.push(makeCyclicImportInfo(dir.ref, dir.isComponent ? 'component' : 'directive', cycle));
29080 }
29081 for (const [pipe, cycle] of cyclesFromPipes) {
29082 relatedMessages.push(makeCyclicImportInfo(pipe.ref, 'pipe', cycle));
29083 }
29084 throw new FatalDiagnosticError(ErrorCode.IMPORT_CYCLE_DETECTED, node, 'One or more import cycles would need to be created to compile this component, ' +
29085 'which is not supported by the current compiler configuration.', relatedMessages);
29086 }
29087 }
29088 }
29089 const diagnostics = [];
29090 if (analysis.providersRequiringFactory !== null &&
29091 analysis.meta.providers instanceof WrappedNodeExpr) {
29092 const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.meta.providers.node, this.injectableRegistry);
29093 diagnostics.push(...providerDiagnostics);
29094 }
29095 if (analysis.viewProvidersRequiringFactory !== null &&
29096 analysis.meta.viewProviders instanceof WrappedNodeExpr) {
29097 const viewProviderDiagnostics = getProviderDiagnostics(analysis.viewProvidersRequiringFactory, analysis.meta.viewProviders.node, this.injectableRegistry);
29098 diagnostics.push(...viewProviderDiagnostics);
29099 }
29100 const directiveDiagnostics = getDirectiveDiagnostics(node, this.metaReader, this.evaluator, this.reflector, this.scopeRegistry, 'Component');
29101 if (directiveDiagnostics !== null) {
29102 diagnostics.push(...directiveDiagnostics);
29103 }
29104 if (diagnostics.length > 0) {
29105 return { diagnostics };
29106 }
29107 return { data };
29108 }
29109 updateResources(node, analysis) {
29110 const containingFile = node.getSourceFile().fileName;
29111 // If the template is external, re-parse it.
29112 const templateDecl = analysis.template.declaration;
29113 if (!templateDecl.isInline) {
29114 analysis.template = this.extractTemplate(node, templateDecl);
29115 }
29116 // Update any external stylesheets and rebuild the combined 'styles' list.
29117 // TODO(alxhub): write tests for styles when the primary compiler uses the updateResources path
29118 let styles = [];
29119 if (analysis.styleUrls !== null) {
29120 for (const styleUrl of analysis.styleUrls) {
29121 const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
29122 2 /* StylesheetFromDecorator */ :
29123 1 /* StylesheetFromTemplate */;
29124 const resolvedStyleUrl = this._resolveResourceOrThrow(styleUrl.url, containingFile, styleUrl.nodeForError, resourceType);
29125 const styleText = this.resourceLoader.load(resolvedStyleUrl);
29126 styles.push(styleText);
29127 }
29128 }
29129 if (analysis.inlineStyles !== null) {
29130 for (const styleText of analysis.inlineStyles) {
29131 styles.push(styleText);
29132 }
29133 }
29134 for (const styleText of analysis.template.styles) {
29135 styles.push(styleText);
29136 }
29137 analysis.meta.styles = styles;
29138 }
29139 compileFull(node, analysis, resolution, pool) {
29140 if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
29141 return [];
29142 }
29143 const meta = Object.assign(Object.assign({}, analysis.meta), resolution);
29144 const def = compileComponentFromMetadata(meta, pool, makeBindingParser());
29145 return this.compileComponent(analysis, def);
29146 }
29147 compilePartial(node, analysis, resolution) {
29148 if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
29149 return [];
29150 }
29151 const meta = Object.assign(Object.assign({}, analysis.meta), resolution);
29152 const def = compileDeclareComponentFromMetadata(meta, analysis.template);
29153 return this.compileComponent(analysis, def);
29154 }
29155 compileComponent(analysis, { expression: initializer, type }) {
29156 const factoryRes = compileNgFactoryDefField(Object.assign(Object.assign({}, analysis.meta), { injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Component }));
29157 if (analysis.metadataStmt !== null) {
29158 factoryRes.statements.push(analysis.metadataStmt);
29159 }
29160 return [
29161 factoryRes, {
29162 name: 'ɵcmp',
29163 initializer,
29164 statements: [],
29165 type,
29166 }
29167 ];
29168 }
29169 _resolveLiteral(decorator) {
29170 if (this.literalCache.has(decorator)) {
29171 return this.literalCache.get(decorator);
29172 }
29173 if (decorator.args === null || decorator.args.length !== 1) {
29174 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @Component decorator`);
29175 }
29176 const meta = unwrapExpression(decorator.args[0]);
29177 if (!ts$1.isObjectLiteralExpression(meta)) {
29178 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, `Decorator argument must be literal.`);
29179 }
29180 this.literalCache.set(decorator, meta);
29181 return meta;
29182 }
29183 _resolveEnumValue(component, field, enumSymbolName) {
29184 let resolved = null;
29185 if (component.has(field)) {
29186 const expr = component.get(field);
29187 const value = this.evaluator.evaluate(expr);
29188 if (value instanceof EnumValue && isAngularCoreReference(value.enumRef, enumSymbolName)) {
29189 resolved = value.resolved;
29190 }
29191 else {
29192 throw createValueHasWrongTypeError(expr, value, `${field} must be a member of ${enumSymbolName} enum from @angular/core`);
29193 }
29194 }
29195 return resolved;
29196 }
29197 _extractComponentStyleUrls(component) {
29198 if (!component.has('styleUrls')) {
29199 return [];
29200 }
29201 return this._extractStyleUrlsFromExpression(component.get('styleUrls'));
29202 }
29203 _extractStyleUrlsFromExpression(styleUrlsExpr) {
29204 const styleUrls = [];
29205 if (ts$1.isArrayLiteralExpression(styleUrlsExpr)) {
29206 for (const styleUrlExpr of styleUrlsExpr.elements) {
29207 if (ts$1.isSpreadElement(styleUrlExpr)) {
29208 styleUrls.push(...this._extractStyleUrlsFromExpression(styleUrlExpr.expression));
29209 }
29210 else {
29211 const styleUrl = this.evaluator.evaluate(styleUrlExpr);
29212 if (typeof styleUrl !== 'string') {
29213 throw createValueHasWrongTypeError(styleUrlExpr, styleUrl, 'styleUrl must be a string');
29214 }
29215 styleUrls.push({
29216 url: styleUrl,
29217 source: 2 /* StylesheetFromDecorator */,
29218 nodeForError: styleUrlExpr,
29219 });
29220 }
29221 }
29222 }
29223 else {
29224 const evaluatedStyleUrls = this.evaluator.evaluate(styleUrlsExpr);
29225 if (!isStringArray(evaluatedStyleUrls)) {
29226 throw createValueHasWrongTypeError(styleUrlsExpr, evaluatedStyleUrls, 'styleUrls must be an array of strings');
29227 }
29228 for (const styleUrl of evaluatedStyleUrls) {
29229 styleUrls.push({
29230 url: styleUrl,
29231 source: 2 /* StylesheetFromDecorator */,
29232 nodeForError: styleUrlsExpr,
29233 });
29234 }
29235 }
29236 return styleUrls;
29237 }
29238 _extractStyleResources(component, containingFile) {
29239 const styles = new Set();
29240 function stringLiteralElements(array) {
29241 return array.elements.filter((e) => ts$1.isStringLiteralLike(e));
29242 }
29243 // If styleUrls is a literal array, process each resource url individually and
29244 // register ones that are string literals.
29245 const styleUrlsExpr = component.get('styleUrls');
29246 if (styleUrlsExpr !== undefined && ts$1.isArrayLiteralExpression(styleUrlsExpr)) {
29247 for (const expression of stringLiteralElements(styleUrlsExpr)) {
29248 const resourceUrl = this._resolveResourceOrThrow(expression.text, containingFile, expression, 2 /* StylesheetFromDecorator */);
29249 styles.add({ path: absoluteFrom(resourceUrl), expression });
29250 }
29251 }
29252 const stylesExpr = component.get('styles');
29253 if (stylesExpr !== undefined && ts$1.isArrayLiteralExpression(stylesExpr)) {
29254 for (const expression of stringLiteralElements(stylesExpr)) {
29255 styles.add({ path: null, expression });
29256 }
29257 }
29258 return styles;
29259 }
29260 _preloadAndParseTemplate(node, decorator, component, containingFile) {
29261 if (component.has('templateUrl')) {
29262 // Extract the templateUrl and preload it.
29263 const templateUrlExpr = component.get('templateUrl');
29264 const templateUrl = this.evaluator.evaluate(templateUrlExpr);
29265 if (typeof templateUrl !== 'string') {
29266 throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
29267 }
29268 const resourceUrl = this._resolveResourceOrThrow(templateUrl, containingFile, templateUrlExpr, 0 /* Template */);
29269 const templatePromise = this.resourceLoader.preload(resourceUrl);
29270 // If the preload worked, then actually load and parse the template, and wait for any style
29271 // URLs to resolve.
29272 if (templatePromise !== undefined) {
29273 return templatePromise.then(() => {
29274 const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
29275 const template = this.extractTemplate(node, templateDecl);
29276 this.preanalyzeTemplateCache.set(node, template);
29277 return template;
29278 });
29279 }
29280 else {
29281 return Promise.resolve(null);
29282 }
29283 }
29284 else {
29285 const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
29286 const template = this.extractTemplate(node, templateDecl);
29287 this.preanalyzeTemplateCache.set(node, template);
29288 return Promise.resolve(template);
29289 }
29290 }
29291 extractTemplate(node, template) {
29292 if (template.isInline) {
29293 let templateStr;
29294 let templateLiteral = null;
29295 let templateUrl = '';
29296 let templateRange = null;
29297 let sourceMapping;
29298 let escapedString = false;
29299 // We only support SourceMaps for inline templates that are simple string literals.
29300 if (ts$1.isStringLiteral(template.expression) ||
29301 ts$1.isNoSubstitutionTemplateLiteral(template.expression)) {
29302 // the start and end of the `templateExpr` node includes the quotation marks, which we must
29303 // strip
29304 templateRange = getTemplateRange(template.expression);
29305 templateStr = template.expression.getSourceFile().text;
29306 templateLiteral = template.expression;
29307 templateUrl = template.templateUrl;
29308 escapedString = true;
29309 sourceMapping = {
29310 type: 'direct',
29311 node: template.expression,
29312 };
29313 }
29314 else {
29315 const resolvedTemplate = this.evaluator.evaluate(template.expression);
29316 if (typeof resolvedTemplate !== 'string') {
29317 throw createValueHasWrongTypeError(template.expression, resolvedTemplate, 'template must be a string');
29318 }
29319 templateStr = resolvedTemplate;
29320 sourceMapping = {
29321 type: 'indirect',
29322 node: template.expression,
29323 componentClass: node,
29324 template: templateStr,
29325 };
29326 }
29327 return Object.assign(Object.assign({}, this._parseTemplate(template, templateStr, templateRange, escapedString)), { sourceMapping, declaration: template });
29328 }
29329 else {
29330 const templateStr = this.resourceLoader.load(template.resolvedTemplateUrl);
29331 if (this.depTracker !== null) {
29332 this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(template.resolvedTemplateUrl));
29333 }
29334 return Object.assign(Object.assign({}, this._parseTemplate(template, templateStr, /* templateRange */ null,
29335 /* escapedString */ false)), { sourceMapping: {
29336 type: 'external',
29337 componentClass: node,
29338 // TODO(alxhub): TS in g3 is unable to make this inference on its own, so cast it here
29339 // until g3 is able to figure this out.
29340 node: template.templateUrlExpression,
29341 template: templateStr,
29342 templateUrl: template.resolvedTemplateUrl,
29343 }, declaration: template });
29344 }
29345 }
29346 _parseTemplate(template, templateStr, templateRange, escapedString) {
29347 // We always normalize line endings if the template has been escaped (i.e. is inline).
29348 const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs;
29349 const parsedTemplate = parseTemplate(templateStr, template.sourceMapUrl, {
29350 preserveWhitespaces: template.preserveWhitespaces,
29351 interpolationConfig: template.interpolationConfig,
29352 range: templateRange !== null && templateRange !== void 0 ? templateRange : undefined,
29353 escapedString,
29354 enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
29355 i18nNormalizeLineEndingsInICUs,
29356 isInline: template.isInline,
29357 alwaysAttemptHtmlToR3AstConversion: this.usePoisonedData,
29358 });
29359 // Unfortunately, the primary parse of the template above may not contain accurate source map
29360 // information. If used directly, it would result in incorrect code locations in template
29361 // errors, etc. There are three main problems:
29362 //
29363 // 1. `preserveWhitespaces: false` annihilates the correctness of template source mapping, as
29364 // the whitespace transformation changes the contents of HTML text nodes before they're
29365 // parsed into Angular expressions.
29366 // 2. `preserveLineEndings: false` causes growing misalignments in templates that use '\r\n'
29367 // line endings, by normalizing them to '\n'.
29368 // 3. By default, the template parser strips leading trivia characters (like spaces, tabs, and
29369 // newlines). This also destroys source mapping information.
29370 //
29371 // In order to guarantee the correctness of diagnostics, templates are parsed a second time
29372 // with the above options set to preserve source mappings.
29373 const { nodes: diagNodes } = parseTemplate(templateStr, template.sourceMapUrl, {
29374 preserveWhitespaces: true,
29375 preserveLineEndings: true,
29376 interpolationConfig: template.interpolationConfig,
29377 range: templateRange !== null && templateRange !== void 0 ? templateRange : undefined,
29378 escapedString,
29379 enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
29380 i18nNormalizeLineEndingsInICUs,
29381 leadingTriviaChars: [],
29382 isInline: template.isInline,
29383 alwaysAttemptHtmlToR3AstConversion: this.usePoisonedData,
29384 });
29385 return Object.assign(Object.assign({}, parsedTemplate), { diagNodes, template: template.isInline ? new WrappedNodeExpr(template.expression) : templateStr, templateUrl: template.resolvedTemplateUrl, isInline: template.isInline, file: new ParseSourceFile(templateStr, template.resolvedTemplateUrl) });
29386 }
29387 parseTemplateDeclaration(decorator, component, containingFile) {
29388 let preserveWhitespaces = this.defaultPreserveWhitespaces;
29389 if (component.has('preserveWhitespaces')) {
29390 const expr = component.get('preserveWhitespaces');
29391 const value = this.evaluator.evaluate(expr);
29392 if (typeof value !== 'boolean') {
29393 throw createValueHasWrongTypeError(expr, value, 'preserveWhitespaces must be a boolean');
29394 }
29395 preserveWhitespaces = value;
29396 }
29397 let interpolationConfig = DEFAULT_INTERPOLATION_CONFIG;
29398 if (component.has('interpolation')) {
29399 const expr = component.get('interpolation');
29400 const value = this.evaluator.evaluate(expr);
29401 if (!Array.isArray(value) || value.length !== 2 ||
29402 !value.every(element => typeof element === 'string')) {
29403 throw createValueHasWrongTypeError(expr, value, 'interpolation must be an array with 2 elements of string type');
29404 }
29405 interpolationConfig = InterpolationConfig.fromArray(value);
29406 }
29407 if (component.has('templateUrl')) {
29408 const templateUrlExpr = component.get('templateUrl');
29409 const templateUrl = this.evaluator.evaluate(templateUrlExpr);
29410 if (typeof templateUrl !== 'string') {
29411 throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
29412 }
29413 const resourceUrl = this._resolveResourceOrThrow(templateUrl, containingFile, templateUrlExpr, 0 /* Template */);
29414 return {
29415 isInline: false,
29416 interpolationConfig,
29417 preserveWhitespaces,
29418 templateUrl,
29419 templateUrlExpression: templateUrlExpr,
29420 resolvedTemplateUrl: resourceUrl,
29421 sourceMapUrl: sourceMapUrl(resourceUrl),
29422 };
29423 }
29424 else if (component.has('template')) {
29425 return {
29426 isInline: true,
29427 interpolationConfig,
29428 preserveWhitespaces,
29429 expression: component.get('template'),
29430 templateUrl: containingFile,
29431 resolvedTemplateUrl: containingFile,
29432 sourceMapUrl: containingFile,
29433 };
29434 }
29435 else {
29436 throw new FatalDiagnosticError(ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), 'component is missing a template');
29437 }
29438 }
29439 _expressionToImportedFile(expr, origin) {
29440 if (!(expr instanceof ExternalExpr)) {
29441 return null;
29442 }
29443 // Figure out what file is being imported.
29444 return this.moduleResolver.resolveModule(expr.value.moduleName, origin.fileName);
29445 }
29446 /**
29447 * Check whether adding an import from `origin` to the source-file corresponding to `expr` would
29448 * create a cyclic import.
29449 *
29450 * @returns a `Cycle` object if a cycle would be created, otherwise `null`.
29451 */
29452 _checkForCyclicImport(ref, expr, origin) {
29453 const importedFile = this._expressionToImportedFile(expr, origin);
29454 if (importedFile === null) {
29455 return null;
29456 }
29457 // Check whether the import is legal.
29458 return this.cycleAnalyzer.wouldCreateCycle(origin, importedFile);
29459 }
29460 _recordSyntheticImport(expr, origin) {
29461 const imported = this._expressionToImportedFile(expr, origin);
29462 if (imported === null) {
29463 return;
29464 }
29465 this.cycleAnalyzer.recordSyntheticImport(origin, imported);
29466 }
29467 /**
29468 * Resolve the url of a resource relative to the file that contains the reference to it.
29469 *
29470 * Throws a FatalDiagnosticError when unable to resolve the file.
29471 */
29472 _resolveResourceOrThrow(file, basePath, nodeForError, resourceType) {
29473 try {
29474 return this.resourceLoader.resolve(file, basePath);
29475 }
29476 catch (e) {
29477 let errorText;
29478 switch (resourceType) {
29479 case 0 /* Template */:
29480 errorText = `Could not find template file '${file}'.`;
29481 break;
29482 case 1 /* StylesheetFromTemplate */:
29483 errorText = `Could not find stylesheet file '${file}' linked from the template.`;
29484 break;
29485 case 2 /* StylesheetFromDecorator */:
29486 errorText = `Could not find stylesheet file '${file}'.`;
29487 break;
29488 }
29489 throw new FatalDiagnosticError(ErrorCode.COMPONENT_RESOURCE_NOT_FOUND, nodeForError, errorText);
29490 }
29491 }
29492 _extractTemplateStyleUrls(template) {
29493 if (template.styleUrls === null) {
29494 return [];
29495 }
29496 const nodeForError = getTemplateDeclarationNodeForError(template.declaration);
29497 return template.styleUrls.map(url => ({ url, source: 1 /* StylesheetFromTemplate */, nodeForError }));
29498 }
29499 }
29500 function getTemplateRange(templateExpr) {
29501 const startPos = templateExpr.getStart() + 1;
29502 const { line, character } = ts$1.getLineAndCharacterOfPosition(templateExpr.getSourceFile(), startPos);
29503 return {
29504 startPos,
29505 startLine: line,
29506 startCol: character,
29507 endPos: templateExpr.getEnd() - 1,
29508 };
29509 }
29510 function sourceMapUrl(resourceUrl) {
29511 if (!tsSourceMapBug29300Fixed()) {
29512 // By removing the template URL we are telling the translator not to try to
29513 // map the external source file to the generated code, since the version
29514 // of TS that is running does not support it.
29515 return '';
29516 }
29517 else {
29518 return resourceUrl;
29519 }
29520 }
29521 /** Determines if the result of an evaluation is a string array. */
29522 function isStringArray(resolvedValue) {
29523 return Array.isArray(resolvedValue) && resolvedValue.every(elem => typeof elem === 'string');
29524 }
29525 /** Determines the node to use for debugging purposes for the given TemplateDeclaration. */
29526 function getTemplateDeclarationNodeForError(declaration) {
29527 // TODO(zarend): Change this to if/else when that is compatible with g3. This uses a switch
29528 // because if/else fails to compile on g3. That is because g3 compiles this in non-strict mode
29529 // where type inference does not work correctly.
29530 switch (declaration.isInline) {
29531 case true:
29532 return declaration.expression;
29533 case false:
29534 return declaration.templateUrlExpression;
29535 }
29536 }
29537 /**
29538 * Generate a diagnostic related information object that describes a potential cyclic import path.
29539 */
29540 function makeCyclicImportInfo(ref, type, cycle) {
29541 const name = ref.debugName || '(unknown)';
29542 const path = cycle.getPath().map(sf => sf.fileName).join(' -> ');
29543 const message = `The ${type} '${name}' is used in the template but importing it would create a cycle: `;
29544 return makeRelatedInformation(ref.node, message + path);
29545 }
29546
29547 /**
29548 * @license
29549 * Copyright Google LLC All Rights Reserved.
29550 *
29551 * Use of this source code is governed by an MIT-style license that can be
29552 * found in the LICENSE file at https://angular.io/license
29553 */
29554 /**
29555 * Adapts the `compileIvyInjectable` compiler for `@Injectable` decorators to the Ivy compiler.
29556 */
29557 class InjectableDecoratorHandler {
29558 constructor(reflector, defaultImportRecorder, isCore, strictCtorDeps, injectableRegistry,
29559 /**
29560 * What to do if the injectable already contains a ɵprov property.
29561 *
29562 * If true then an error diagnostic is reported.
29563 * If false then there is no error and a new ɵprov property is not added.
29564 */
29565 errorOnDuplicateProv = true) {
29566 this.reflector = reflector;
29567 this.defaultImportRecorder = defaultImportRecorder;
29568 this.isCore = isCore;
29569 this.strictCtorDeps = strictCtorDeps;
29570 this.injectableRegistry = injectableRegistry;
29571 this.errorOnDuplicateProv = errorOnDuplicateProv;
29572 this.precedence = HandlerPrecedence.SHARED;
29573 this.name = InjectableDecoratorHandler.name;
29574 }
29575 detect(node, decorators) {
29576 if (!decorators) {
29577 return undefined;
29578 }
29579 const decorator = findAngularDecorator(decorators, 'Injectable', this.isCore);
29580 if (decorator !== undefined) {
29581 return {
29582 trigger: decorator.node,
29583 decorator: decorator,
29584 metadata: decorator,
29585 };
29586 }
29587 else {
29588 return undefined;
29589 }
29590 }
29591 analyze(node, decorator) {
29592 const meta = extractInjectableMetadata(node, decorator, this.reflector);
29593 const decorators = this.reflector.getDecoratorsOfDeclaration(node);
29594 return {
29595 analysis: {
29596 meta,
29597 ctorDeps: extractInjectableCtorDeps(node, meta, decorator, this.reflector, this.defaultImportRecorder, this.isCore, this.strictCtorDeps),
29598 metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore),
29599 // Avoid generating multiple factories if a class has
29600 // more Angular decorators, apart from Injectable.
29601 needsFactory: !decorators ||
29602 decorators.every(current => !isAngularCore(current) || current.name === 'Injectable')
29603 },
29604 };
29605 }
29606 register(node) {
29607 this.injectableRegistry.registerInjectable(node);
29608 }
29609 compileFull(node, analysis) {
29610 const res = compileInjectable(analysis.meta);
29611 const statements = res.statements;
29612 const results = [];
29613 if (analysis.needsFactory) {
29614 const meta = analysis.meta;
29615 const factoryRes = compileNgFactoryDefField({
29616 name: meta.name,
29617 type: meta.type,
29618 internalType: meta.internalType,
29619 typeArgumentCount: meta.typeArgumentCount,
29620 deps: analysis.ctorDeps,
29621 injectFn: Identifiers.inject,
29622 target: R3FactoryTarget.Injectable,
29623 });
29624 if (analysis.metadataStmt !== null) {
29625 factoryRes.statements.push(analysis.metadataStmt);
29626 }
29627 results.push(factoryRes);
29628 }
29629 const ɵprov = this.reflector.getMembersOfClass(node).find(member => member.name === 'ɵprov');
29630 if (ɵprov !== undefined && this.errorOnDuplicateProv) {
29631 throw new FatalDiagnosticError(ErrorCode.INJECTABLE_DUPLICATE_PROV, ɵprov.nameNode || ɵprov.node || node, 'Injectables cannot contain a static ɵprov property, because the compiler is going to generate one.');
29632 }
29633 if (ɵprov === undefined) {
29634 // Only add a new ɵprov if there is not one already
29635 results.push({ name: 'ɵprov', initializer: res.expression, statements, type: res.type });
29636 }
29637 return results;
29638 }
29639 }
29640 /**
29641 * Read metadata from the `@Injectable` decorator and produce the `IvyInjectableMetadata`, the
29642 * input metadata needed to run `compileIvyInjectable`.
29643 *
29644 * A `null` return value indicates this is @Injectable has invalid data.
29645 */
29646 function extractInjectableMetadata(clazz, decorator, reflector) {
29647 const name = clazz.name.text;
29648 const type = wrapTypeReference(reflector, clazz);
29649 const internalType = new WrappedNodeExpr(reflector.getInternalNameOfClass(clazz));
29650 const typeArgumentCount = reflector.getGenericArityOfClass(clazz) || 0;
29651 if (decorator.args === null) {
29652 throw new FatalDiagnosticError(ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), '@Injectable must be called');
29653 }
29654 if (decorator.args.length === 0) {
29655 return {
29656 name,
29657 type,
29658 typeArgumentCount,
29659 internalType,
29660 providedIn: new LiteralExpr(null),
29661 };
29662 }
29663 else if (decorator.args.length === 1) {
29664 const metaNode = decorator.args[0];
29665 // Firstly make sure the decorator argument is an inline literal - if not, it's illegal to
29666 // transport references from one location to another. This is the problem that lowering
29667 // used to solve - if this restriction proves too undesirable we can re-implement lowering.
29668 if (!ts$1.isObjectLiteralExpression(metaNode)) {
29669 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, metaNode, `@Injectable argument must be an object literal`);
29670 }
29671 // Resolve the fields of the literal into a map of field name to expression.
29672 const meta = reflectObjectLiteral(metaNode);
29673 let providedIn = new LiteralExpr(null);
29674 if (meta.has('providedIn')) {
29675 providedIn = new WrappedNodeExpr(meta.get('providedIn'));
29676 }
29677 let userDeps = undefined;
29678 if ((meta.has('useClass') || meta.has('useFactory')) && meta.has('deps')) {
29679 const depsExpr = meta.get('deps');
29680 if (!ts$1.isArrayLiteralExpression(depsExpr)) {
29681 throw new FatalDiagnosticError(ErrorCode.VALUE_NOT_LITERAL, depsExpr, `@Injectable deps metadata must be an inline array`);
29682 }
29683 userDeps = depsExpr.elements.map(dep => getDep(dep, reflector));
29684 }
29685 if (meta.has('useValue')) {
29686 return {
29687 name,
29688 type,
29689 typeArgumentCount,
29690 internalType,
29691 providedIn,
29692 useValue: new WrappedNodeExpr(unwrapForwardRef(meta.get('useValue'), reflector)),
29693 };
29694 }
29695 else if (meta.has('useExisting')) {
29696 return {
29697 name,
29698 type,
29699 typeArgumentCount,
29700 internalType,
29701 providedIn,
29702 useExisting: new WrappedNodeExpr(unwrapForwardRef(meta.get('useExisting'), reflector)),
29703 };
29704 }
29705 else if (meta.has('useClass')) {
29706 return {
29707 name,
29708 type,
29709 typeArgumentCount,
29710 internalType,
29711 providedIn,
29712 useClass: new WrappedNodeExpr(unwrapForwardRef(meta.get('useClass'), reflector)),
29713 userDeps,
29714 };
29715 }
29716 else if (meta.has('useFactory')) {
29717 // useFactory is special - the 'deps' property must be analyzed.
29718 const factory = new WrappedNodeExpr(meta.get('useFactory'));
29719 return {
29720 name,
29721 type,
29722 typeArgumentCount,
29723 internalType,
29724 providedIn,
29725 useFactory: factory,
29726 userDeps,
29727 };
29728 }
29729 else {
29730 return { name, type, typeArgumentCount, internalType, providedIn };
29731 }
29732 }
29733 else {
29734 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, decorator.args[2], 'Too many arguments to @Injectable');
29735 }
29736 }
29737 function extractInjectableCtorDeps(clazz, meta, decorator, reflector, defaultImportRecorder, isCore, strictCtorDeps) {
29738 if (decorator.args === null) {
29739 throw new FatalDiagnosticError(ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), '@Injectable must be called');
29740 }
29741 let ctorDeps = null;
29742 if (decorator.args.length === 0) {
29743 // Ideally, using @Injectable() would have the same effect as using @Injectable({...}), and be
29744 // subject to the same validation. However, existing Angular code abuses @Injectable, applying
29745 // it to things like abstract classes with constructors that were never meant for use with
29746 // Angular's DI.
29747 //
29748 // To deal with this, @Injectable() without an argument is more lenient, and if the
29749 // constructor signature does not work for DI then a factory definition (ɵfac) that throws is
29750 // generated.
29751 if (strictCtorDeps) {
29752 ctorDeps = getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
29753 }
29754 else {
29755 ctorDeps = unwrapConstructorDependencies(getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore));
29756 }
29757 return ctorDeps;
29758 }
29759 else if (decorator.args.length === 1) {
29760 const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
29761 if (strictCtorDeps && meta.useValue === undefined && meta.useExisting === undefined &&
29762 meta.useClass === undefined && meta.useFactory === undefined) {
29763 // Since use* was not provided, validate the deps according to strictCtorDeps.
29764 ctorDeps = validateConstructorDependencies(clazz, rawCtorDeps);
29765 }
29766 else {
29767 ctorDeps = unwrapConstructorDependencies(rawCtorDeps);
29768 }
29769 }
29770 return ctorDeps;
29771 }
29772 function getDep(dep, reflector) {
29773 const meta = {
29774 token: new WrappedNodeExpr(dep),
29775 attribute: null,
29776 host: false,
29777 resolved: R3ResolvedDependencyType.Token,
29778 optional: false,
29779 self: false,
29780 skipSelf: false,
29781 };
29782 function maybeUpdateDecorator(dec, reflector, token) {
29783 const source = reflector.getImportOfIdentifier(dec);
29784 if (source === null || source.from !== '@angular/core') {
29785 return;
29786 }
29787 switch (source.name) {
29788 case 'Inject':
29789 if (token !== undefined) {
29790 meta.token = new WrappedNodeExpr(token);
29791 }
29792 break;
29793 case 'Optional':
29794 meta.optional = true;
29795 break;
29796 case 'SkipSelf':
29797 meta.skipSelf = true;
29798 break;
29799 case 'Self':
29800 meta.self = true;
29801 break;
29802 }
29803 }
29804 if (ts$1.isArrayLiteralExpression(dep)) {
29805 dep.elements.forEach(el => {
29806 if (ts$1.isIdentifier(el)) {
29807 maybeUpdateDecorator(el, reflector);
29808 }
29809 else if (ts$1.isNewExpression(el) && ts$1.isIdentifier(el.expression)) {
29810 const token = el.arguments && el.arguments.length > 0 && el.arguments[0] || undefined;
29811 maybeUpdateDecorator(el.expression, reflector, token);
29812 }
29813 });
29814 }
29815 return meta;
29816 }
29817
29818 /**
29819 * @license
29820 * Copyright Google LLC All Rights Reserved.
29821 *
29822 * Use of this source code is governed by an MIT-style license that can be
29823 * found in the LICENSE file at https://angular.io/license
29824 */
29825 /**
29826 * Compiles @NgModule annotations to ngModuleDef fields.
29827 *
29828 * TODO(alxhub): handle injector side of things as well.
29829 */
29830 class NgModuleDecoratorHandler {
29831 constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, factoryTracker, defaultImportRecorder, annotateForClosureCompiler, injectableRegistry, localeId) {
29832 this.reflector = reflector;
29833 this.evaluator = evaluator;
29834 this.metaReader = metaReader;
29835 this.metaRegistry = metaRegistry;
29836 this.scopeRegistry = scopeRegistry;
29837 this.referencesRegistry = referencesRegistry;
29838 this.isCore = isCore;
29839 this.routeAnalyzer = routeAnalyzer;
29840 this.refEmitter = refEmitter;
29841 this.factoryTracker = factoryTracker;
29842 this.defaultImportRecorder = defaultImportRecorder;
29843 this.annotateForClosureCompiler = annotateForClosureCompiler;
29844 this.injectableRegistry = injectableRegistry;
29845 this.localeId = localeId;
29846 this.precedence = HandlerPrecedence.PRIMARY;
29847 this.name = NgModuleDecoratorHandler.name;
29848 }
29849 detect(node, decorators) {
29850 if (!decorators) {
29851 return undefined;
29852 }
29853 const decorator = findAngularDecorator(decorators, 'NgModule', this.isCore);
29854 if (decorator !== undefined) {
29855 return {
29856 trigger: decorator.node,
29857 decorator: decorator,
29858 metadata: decorator,
29859 };
29860 }
29861 else {
29862 return undefined;
29863 }
29864 }
29865 analyze(node, decorator) {
29866 const name = node.name.text;
29867 if (decorator.args === null || decorator.args.length > 1) {
29868 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @NgModule decorator`);
29869 }
29870 // @NgModule can be invoked without arguments. In case it is, pretend as if a blank object
29871 // literal was specified. This simplifies the code below.
29872 const meta = decorator.args.length === 1 ? unwrapExpression(decorator.args[0]) :
29873 ts$1.createObjectLiteral([]);
29874 if (!ts$1.isObjectLiteralExpression(meta)) {
29875 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@NgModule argument must be an object literal');
29876 }
29877 const ngModule = reflectObjectLiteral(meta);
29878 if (ngModule.has('jit')) {
29879 // The only allowed value is true, so there's no need to expand further.
29880 return {};
29881 }
29882 const moduleResolvers = combineResolvers([
29883 ref => this._extractModuleFromModuleWithProvidersFn(ref.node),
29884 forwardRefResolver,
29885 ]);
29886 const diagnostics = [];
29887 // Extract the module declarations, imports, and exports.
29888 let declarationRefs = [];
29889 let rawDeclarations = null;
29890 if (ngModule.has('declarations')) {
29891 rawDeclarations = ngModule.get('declarations');
29892 const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver);
29893 declarationRefs =
29894 this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations');
29895 // Look through the declarations to make sure they're all a part of the current compilation.
29896 for (const ref of declarationRefs) {
29897 if (ref.node.getSourceFile().isDeclarationFile) {
29898 const errorNode = ref.getOriginForDiagnostics(rawDeclarations);
29899 diagnostics.push(makeDiagnostic(ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, `Cannot declare '${ref.node.name
29900 .text}' in an NgModule as it's not a part of the current compilation.`, [makeRelatedInformation(ref.node.name, `'${ref.node.name.text}' is declared here.`)]));
29901 }
29902 }
29903 }
29904 if (diagnostics.length > 0) {
29905 return { diagnostics };
29906 }
29907 let importRefs = [];
29908 let rawImports = null;
29909 if (ngModule.has('imports')) {
29910 rawImports = ngModule.get('imports');
29911 const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers);
29912 importRefs = this.resolveTypeList(rawImports, importsMeta, name, 'imports');
29913 }
29914 let exportRefs = [];
29915 let rawExports = null;
29916 if (ngModule.has('exports')) {
29917 rawExports = ngModule.get('exports');
29918 const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers);
29919 exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports');
29920 this.referencesRegistry.add(node, ...exportRefs);
29921 }
29922 let bootstrapRefs = [];
29923 if (ngModule.has('bootstrap')) {
29924 const expr = ngModule.get('bootstrap');
29925 const bootstrapMeta = this.evaluator.evaluate(expr, forwardRefResolver);
29926 bootstrapRefs = this.resolveTypeList(expr, bootstrapMeta, name, 'bootstrap');
29927 }
29928 const schemas = [];
29929 if (ngModule.has('schemas')) {
29930 const rawExpr = ngModule.get('schemas');
29931 const result = this.evaluator.evaluate(rawExpr);
29932 if (!Array.isArray(result)) {
29933 throw createValueHasWrongTypeError(rawExpr, result, `NgModule.schemas must be an array`);
29934 }
29935 for (const schemaRef of result) {
29936 if (!(schemaRef instanceof Reference$1)) {
29937 throw createValueHasWrongTypeError(rawExpr, result, 'NgModule.schemas must be an array of schemas');
29938 }
29939 const id = schemaRef.getIdentityIn(schemaRef.node.getSourceFile());
29940 if (id === null || schemaRef.ownedByModuleGuess !== '@angular/core') {
29941 throw createValueHasWrongTypeError(rawExpr, result, 'NgModule.schemas must be an array of schemas');
29942 }
29943 // Since `id` is the `ts.Identifer` within the schema ref's declaration file, it's safe to
29944 // use `id.text` here to figure out which schema is in use. Even if the actual reference was
29945 // renamed when the user imported it, these names will match.
29946 switch (id.text) {
29947 case 'CUSTOM_ELEMENTS_SCHEMA':
29948 schemas.push(CUSTOM_ELEMENTS_SCHEMA);
29949 break;
29950 case 'NO_ERRORS_SCHEMA':
29951 schemas.push(NO_ERRORS_SCHEMA);
29952 break;
29953 default:
29954 throw createValueHasWrongTypeError(rawExpr, schemaRef, `'${schemaRef.debugName}' is not a valid NgModule schema`);
29955 }
29956 }
29957 }
29958 const id = ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id')) : null;
29959 const valueContext = node.getSourceFile();
29960 let typeContext = valueContext;
29961 const typeNode = this.reflector.getDtsDeclaration(node);
29962 if (typeNode !== null) {
29963 typeContext = typeNode.getSourceFile();
29964 }
29965 const bootstrap = bootstrapRefs.map(bootstrap => this._toR3Reference(bootstrap, valueContext, typeContext));
29966 const declarations = declarationRefs.map(decl => this._toR3Reference(decl, valueContext, typeContext));
29967 const imports = importRefs.map(imp => this._toR3Reference(imp, valueContext, typeContext));
29968 const exports = exportRefs.map(exp => this._toR3Reference(exp, valueContext, typeContext));
29969 const isForwardReference = (ref) => isExpressionForwardReference(ref.value, node.name, valueContext);
29970 const containsForwardDecls = bootstrap.some(isForwardReference) ||
29971 declarations.some(isForwardReference) || imports.some(isForwardReference) ||
29972 exports.some(isForwardReference);
29973 const type = wrapTypeReference(this.reflector, node);
29974 const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(node));
29975 const adjacentType = new WrappedNodeExpr(this.reflector.getAdjacentNameOfClass(node));
29976 const ngModuleDef = {
29977 type,
29978 internalType,
29979 adjacentType,
29980 bootstrap,
29981 declarations,
29982 exports,
29983 imports,
29984 containsForwardDecls,
29985 id,
29986 emitInline: false,
29987 // TODO: to be implemented as a part of FW-1004.
29988 schemas: [],
29989 };
29990 const rawProviders = ngModule.has('providers') ? ngModule.get('providers') : null;
29991 const wrapperProviders = rawProviders !== null ?
29992 new WrappedNodeExpr(this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) :
29993 rawProviders) :
29994 null;
29995 // At this point, only add the module's imports as the injectors' imports. Any exported modules
29996 // are added during `resolve`, as we need scope information to be able to filter out directives
29997 // and pipes from the module exports.
29998 const injectorImports = [];
29999 if (ngModule.has('imports')) {
30000 injectorImports.push(new WrappedNodeExpr(ngModule.get('imports')));
30001 }
30002 if (this.routeAnalyzer !== null) {
30003 this.routeAnalyzer.add(node.getSourceFile(), name, rawImports, rawExports, rawProviders);
30004 }
30005 const ngInjectorDef = {
30006 name,
30007 type,
30008 internalType,
30009 deps: getValidConstructorDependencies(node, this.reflector, this.defaultImportRecorder, this.isCore),
30010 providers: wrapperProviders,
30011 imports: injectorImports,
30012 };
30013 return {
30014 analysis: {
30015 id,
30016 schemas: schemas,
30017 mod: ngModuleDef,
30018 inj: ngInjectorDef,
30019 declarations: declarationRefs,
30020 rawDeclarations,
30021 imports: importRefs,
30022 exports: exportRefs,
30023 providers: rawProviders,
30024 providersRequiringFactory: rawProviders ?
30025 resolveProvidersRequiringFactory(rawProviders, this.reflector, this.evaluator) :
30026 null,
30027 metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
30028 factorySymbolName: node.name.text,
30029 },
30030 };
30031 }
30032 register(node, analysis) {
30033 // Register this module's information with the LocalModuleScopeRegistry. This ensures that
30034 // during the compile() phase, the module's metadata is available for selector scope
30035 // computation.
30036 this.metaRegistry.registerNgModuleMetadata({
30037 ref: new Reference$1(node),
30038 schemas: analysis.schemas,
30039 declarations: analysis.declarations,
30040 imports: analysis.imports,
30041 exports: analysis.exports,
30042 rawDeclarations: analysis.rawDeclarations,
30043 });
30044 if (this.factoryTracker !== null) {
30045 this.factoryTracker.track(node.getSourceFile(), {
30046 name: analysis.factorySymbolName,
30047 hasId: analysis.id !== null,
30048 });
30049 }
30050 this.injectableRegistry.registerInjectable(node);
30051 }
30052 resolve(node, analysis) {
30053 const scope = this.scopeRegistry.getScopeOfModule(node);
30054 const diagnostics = [];
30055 const scopeDiagnostics = this.scopeRegistry.getDiagnosticsOfModule(node);
30056 if (scopeDiagnostics !== null) {
30057 diagnostics.push(...scopeDiagnostics);
30058 }
30059 if (analysis.providersRequiringFactory !== null) {
30060 const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.providers, this.injectableRegistry);
30061 diagnostics.push(...providerDiagnostics);
30062 }
30063 const data = {
30064 injectorImports: [],
30065 };
30066 if (scope !== null && !scope.compilation.isPoisoned) {
30067 // Using the scope information, extend the injector's imports using the modules that are
30068 // specified as module exports.
30069 const context = getSourceFile(node);
30070 for (const exportRef of analysis.exports) {
30071 if (isNgModule(exportRef.node, scope.compilation)) {
30072 data.injectorImports.push(this.refEmitter.emit(exportRef, context));
30073 }
30074 }
30075 for (const decl of analysis.declarations) {
30076 const metadata = this.metaReader.getDirectiveMetadata(decl);
30077 if (metadata !== null && metadata.selector === null) {
30078 throw new FatalDiagnosticError(ErrorCode.DIRECTIVE_MISSING_SELECTOR, decl.node, `Directive ${decl.node.name.text} has no selector, please add it!`);
30079 }
30080 }
30081 }
30082 if (diagnostics.length > 0) {
30083 return { diagnostics };
30084 }
30085 if (scope === null || scope.compilation.isPoisoned || scope.exported.isPoisoned ||
30086 scope.reexports === null) {
30087 return { data };
30088 }
30089 else {
30090 return {
30091 data,
30092 reexports: scope.reexports,
30093 };
30094 }
30095 }
30096 compileFull(node, analysis, resolution) {
30097 // Merge the injector imports (which are 'exports' that were later found to be NgModules)
30098 // computed during resolution with the ones from analysis.
30099 const ngInjectorDef = compileInjector(Object.assign(Object.assign({}, analysis.inj), { imports: [...analysis.inj.imports, ...resolution.injectorImports] }));
30100 const ngModuleDef = compileNgModule(analysis.mod);
30101 const ngModuleStatements = ngModuleDef.additionalStatements;
30102 if (analysis.metadataStmt !== null) {
30103 ngModuleStatements.push(analysis.metadataStmt);
30104 }
30105 const context = getSourceFile(node);
30106 for (const decl of analysis.declarations) {
30107 const remoteScope = this.scopeRegistry.getRemoteScope(decl.node);
30108 if (remoteScope !== null) {
30109 const directives = remoteScope.directives.map(directive => this.refEmitter.emit(directive, context));
30110 const pipes = remoteScope.pipes.map(pipe => this.refEmitter.emit(pipe, context));
30111 const directiveArray = new LiteralArrayExpr(directives);
30112 const pipesArray = new LiteralArrayExpr(pipes);
30113 const declExpr = this.refEmitter.emit(decl, context);
30114 const setComponentScope = new ExternalExpr(Identifiers$1.setComponentScope);
30115 const callExpr = new InvokeFunctionExpr(setComponentScope, [declExpr, directiveArray, pipesArray]);
30116 ngModuleStatements.push(callExpr.toStmt());
30117 }
30118 }
30119 const res = [
30120 {
30121 name: 'ɵmod',
30122 initializer: ngModuleDef.expression,
30123 statements: ngModuleStatements,
30124 type: ngModuleDef.type,
30125 },
30126 {
30127 name: 'ɵinj',
30128 initializer: ngInjectorDef.expression,
30129 statements: ngInjectorDef.statements,
30130 type: ngInjectorDef.type,
30131 }
30132 ];
30133 if (this.localeId) {
30134 res.push({
30135 name: 'ɵloc',
30136 initializer: new LiteralExpr(this.localeId),
30137 statements: [],
30138 type: STRING_TYPE
30139 });
30140 }
30141 return res;
30142 }
30143 _toR3Reference(valueRef, valueContext, typeContext) {
30144 if (valueRef.hasOwningModuleGuess) {
30145 return toR3Reference(valueRef, valueRef, valueContext, valueContext, this.refEmitter);
30146 }
30147 else {
30148 let typeRef = valueRef;
30149 let typeNode = this.reflector.getDtsDeclaration(typeRef.node);
30150 if (typeNode !== null && isNamedClassDeclaration(typeNode)) {
30151 typeRef = new Reference$1(typeNode);
30152 }
30153 return toR3Reference(valueRef, typeRef, valueContext, typeContext, this.refEmitter);
30154 }
30155 }
30156 /**
30157 * Given a `FunctionDeclaration`, `MethodDeclaration` or `FunctionExpression`, check if it is
30158 * typed as a `ModuleWithProviders` and return an expression referencing the module if available.
30159 */
30160 _extractModuleFromModuleWithProvidersFn(node) {
30161 const type = node.type || null;
30162 return type &&
30163 (this._reflectModuleFromTypeParam(type, node) || this._reflectModuleFromLiteralType(type));
30164 }
30165 /**
30166 * Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
30167 * `ModuleWithProviders<T>`
30168 * @param type The type to reflect on.
30169 * @returns the identifier of the NgModule type if found, or null otherwise.
30170 */
30171 _reflectModuleFromTypeParam(type, node) {
30172 // Examine the type of the function to see if it's a ModuleWithProviders reference.
30173 if (!ts$1.isTypeReferenceNode(type)) {
30174 return null;
30175 }
30176 const typeName = type &&
30177 (ts$1.isIdentifier(type.typeName) && type.typeName ||
30178 ts$1.isQualifiedName(type.typeName) && type.typeName.right) ||
30179 null;
30180 if (typeName === null) {
30181 return null;
30182 }
30183 // Look at the type itself to see where it comes from.
30184 const id = this.reflector.getImportOfIdentifier(typeName);
30185 // If it's not named ModuleWithProviders, bail.
30186 if (id === null || id.name !== 'ModuleWithProviders') {
30187 return null;
30188 }
30189 // If it's not from @angular/core, bail.
30190 if (!this.isCore && id.from !== '@angular/core') {
30191 return null;
30192 }
30193 // If there's no type parameter specified, bail.
30194 if (type.typeArguments === undefined || type.typeArguments.length !== 1) {
30195 const parent = ts$1.isMethodDeclaration(node) && ts$1.isClassDeclaration(node.parent) ? node.parent : null;
30196 const symbolName = (parent && parent.name ? parent.name.getText() + '.' : '') +
30197 (node.name ? node.name.getText() : 'anonymous');
30198 throw new FatalDiagnosticError(ErrorCode.NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC, type, `${symbolName} returns a ModuleWithProviders type without a generic type argument. ` +
30199 `Please add a generic type argument to the ModuleWithProviders type. If this ` +
30200 `occurrence is in library code you don't control, please contact the library authors.`);
30201 }
30202 const arg = type.typeArguments[0];
30203 return typeNodeToValueExpr(arg);
30204 }
30205 /**
30206 * Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
30207 * `A|B|{ngModule: T}|C`.
30208 * @param type The type to reflect on.
30209 * @returns the identifier of the NgModule type if found, or null otherwise.
30210 */
30211 _reflectModuleFromLiteralType(type) {
30212 if (!ts$1.isIntersectionTypeNode(type)) {
30213 return null;
30214 }
30215 for (const t of type.types) {
30216 if (ts$1.isTypeLiteralNode(t)) {
30217 for (const m of t.members) {
30218 const ngModuleType = ts$1.isPropertySignature(m) && ts$1.isIdentifier(m.name) &&
30219 m.name.text === 'ngModule' && m.type ||
30220 null;
30221 const ngModuleExpression = ngModuleType && typeNodeToValueExpr(ngModuleType);
30222 if (ngModuleExpression) {
30223 return ngModuleExpression;
30224 }
30225 }
30226 }
30227 }
30228 return null;
30229 }
30230 // Verify that a "Declaration" reference is a `ClassDeclaration` reference.
30231 isClassDeclarationReference(ref) {
30232 return this.reflector.isClass(ref.node);
30233 }
30234 /**
30235 * Compute a list of `Reference`s from a resolved metadata value.
30236 */
30237 resolveTypeList(expr, resolvedList, className, arrayName) {
30238 const refList = [];
30239 if (!Array.isArray(resolvedList)) {
30240 throw createValueHasWrongTypeError(expr, resolvedList, `Expected array when reading the NgModule.${arrayName} of ${className}`);
30241 }
30242 resolvedList.forEach((entry, idx) => {
30243 // Unwrap ModuleWithProviders for modules that are locally declared (and thus static
30244 // resolution was able to descend into the function and return an object literal, a Map).
30245 if (entry instanceof Map && entry.has('ngModule')) {
30246 entry = entry.get('ngModule');
30247 }
30248 if (Array.isArray(entry)) {
30249 // Recurse into nested arrays.
30250 refList.push(...this.resolveTypeList(expr, entry, className, arrayName));
30251 }
30252 else if (entry instanceof Reference$1) {
30253 if (!this.isClassDeclarationReference(entry)) {
30254 throw createValueHasWrongTypeError(entry.node, entry, `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a class`);
30255 }
30256 refList.push(entry);
30257 }
30258 else {
30259 // TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array.
30260 throw createValueHasWrongTypeError(expr, entry, `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a reference`);
30261 }
30262 });
30263 return refList;
30264 }
30265 }
30266 function isNgModule(node, compilation) {
30267 return !compilation.directives.some(directive => directive.ref.node === node) &&
30268 !compilation.pipes.some(pipe => pipe.ref.node === node);
30269 }
30270
30271 /**
30272 * @license
30273 * Copyright Google LLC All Rights Reserved.
30274 *
30275 * Use of this source code is governed by an MIT-style license that can be
30276 * found in the LICENSE file at https://angular.io/license
30277 */
30278 class PipeDecoratorHandler {
30279 constructor(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportRecorder, injectableRegistry, isCore) {
30280 this.reflector = reflector;
30281 this.evaluator = evaluator;
30282 this.metaRegistry = metaRegistry;
30283 this.scopeRegistry = scopeRegistry;
30284 this.defaultImportRecorder = defaultImportRecorder;
30285 this.injectableRegistry = injectableRegistry;
30286 this.isCore = isCore;
30287 this.precedence = HandlerPrecedence.PRIMARY;
30288 this.name = PipeDecoratorHandler.name;
30289 }
30290 detect(node, decorators) {
30291 if (!decorators) {
30292 return undefined;
30293 }
30294 const decorator = findAngularDecorator(decorators, 'Pipe', this.isCore);
30295 if (decorator !== undefined) {
30296 return {
30297 trigger: decorator.node,
30298 decorator: decorator,
30299 metadata: decorator,
30300 };
30301 }
30302 else {
30303 return undefined;
30304 }
30305 }
30306 analyze(clazz, decorator) {
30307 const name = clazz.name.text;
30308 const type = wrapTypeReference(this.reflector, clazz);
30309 const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(clazz));
30310 if (decorator.args === null) {
30311 throw new FatalDiagnosticError(ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), `@Pipe must be called`);
30312 }
30313 if (decorator.args.length !== 1) {
30314 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), '@Pipe must have exactly one argument');
30315 }
30316 const meta = unwrapExpression(decorator.args[0]);
30317 if (!ts$1.isObjectLiteralExpression(meta)) {
30318 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@Pipe must have a literal argument');
30319 }
30320 const pipe = reflectObjectLiteral(meta);
30321 if (!pipe.has('name')) {
30322 throw new FatalDiagnosticError(ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`);
30323 }
30324 const pipeNameExpr = pipe.get('name');
30325 const pipeName = this.evaluator.evaluate(pipeNameExpr);
30326 if (typeof pipeName !== 'string') {
30327 throw createValueHasWrongTypeError(pipeNameExpr, pipeName, `@Pipe.name must be a string`);
30328 }
30329 let pure = true;
30330 if (pipe.has('pure')) {
30331 const expr = pipe.get('pure');
30332 const pureValue = this.evaluator.evaluate(expr);
30333 if (typeof pureValue !== 'boolean') {
30334 throw createValueHasWrongTypeError(expr, pureValue, `@Pipe.pure must be a boolean`);
30335 }
30336 pure = pureValue;
30337 }
30338 return {
30339 analysis: {
30340 meta: {
30341 name,
30342 type,
30343 internalType,
30344 typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0,
30345 pipeName,
30346 deps: getValidConstructorDependencies(clazz, this.reflector, this.defaultImportRecorder, this.isCore),
30347 pure,
30348 },
30349 metadataStmt: generateSetClassMetadataCall(clazz, this.reflector, this.defaultImportRecorder, this.isCore),
30350 },
30351 };
30352 }
30353 register(node, analysis) {
30354 const ref = new Reference$1(node);
30355 this.metaRegistry.registerPipeMetadata({ ref, name: analysis.meta.pipeName });
30356 this.injectableRegistry.registerInjectable(node);
30357 }
30358 resolve(node) {
30359 const duplicateDeclData = this.scopeRegistry.getDuplicateDeclarations(node);
30360 if (duplicateDeclData !== null) {
30361 // This pipe was declared twice (or more).
30362 return {
30363 diagnostics: [makeDuplicateDeclarationError(node, duplicateDeclData, 'Pipe')],
30364 };
30365 }
30366 return {};
30367 }
30368 compileFull(node, analysis) {
30369 const res = compilePipeFromMetadata(analysis.meta);
30370 return this.compilePipe(analysis, res);
30371 }
30372 compilePartial(node, analysis) {
30373 const res = compileDeclarePipeFromMetadata(analysis.meta);
30374 return this.compilePipe(analysis, res);
30375 }
30376 compilePipe(analysis, def) {
30377 const factoryRes = compileNgFactoryDefField(Object.assign(Object.assign({}, analysis.meta), { injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Pipe }));
30378 if (analysis.metadataStmt !== null) {
30379 factoryRes.statements.push(analysis.metadataStmt);
30380 }
30381 return [
30382 factoryRes, {
30383 name: 'ɵpipe',
30384 initializer: def.expression,
30385 statements: [],
30386 type: def.type,
30387 }
30388 ];
30389 }
30390 }
30391
30392 /**
30393 * @license
30394 * Copyright Google LLC All Rights Reserved.
30395 *
30396 * Use of this source code is governed by an MIT-style license that can be
30397 * found in the LICENSE file at https://angular.io/license
30398 */
30399 /**
30400 * This registry does nothing, since ngtsc does not currently need
30401 * this functionality.
30402 * The ngcc tool implements a working version for its purposes.
30403 */
30404 class NoopReferencesRegistry {
30405 add(source, ...references) { }
30406 }
30407
30408 /**
30409 * @license
30410 * Copyright Google LLC All Rights Reserved.
30411 *
30412 * Use of this source code is governed by an MIT-style license that can be
30413 * found in the LICENSE file at https://angular.io/license
30414 */
30415 /**
30416 * Analyzes a `ts.Program` for cycles.
30417 */
30418 class CycleAnalyzer {
30419 constructor(importGraph) {
30420 this.importGraph = importGraph;
30421 }
30422 /**
30423 * Check for a cycle to be created in the `ts.Program` by adding an import between `from` and
30424 * `to`.
30425 *
30426 * @returns a `Cycle` object if an import between `from` and `to` would create a cycle; `null`
30427 * otherwise.
30428 */
30429 wouldCreateCycle(from, to) {
30430 // Import of 'from' -> 'to' is illegal if an edge 'to' -> 'from' already exists.
30431 return this.importGraph.transitiveImportsOf(to).has(from) ?
30432 new Cycle(this.importGraph, from, to) :
30433 null;
30434 }
30435 /**
30436 * Record a synthetic import from `from` to `to`.
30437 *
30438 * This is an import that doesn't exist in the `ts.Program` but will be considered as part of the
30439 * import graph for cycle creation.
30440 */
30441 recordSyntheticImport(from, to) {
30442 this.importGraph.addSyntheticImport(from, to);
30443 }
30444 }
30445 /**
30446 * Represents an import cycle between `from` and `to` in the program.
30447 *
30448 * This class allows us to do the work to compute the cyclic path between `from` and `to` only if
30449 * needed.
30450 */
30451 class Cycle {
30452 constructor(importGraph, from, to) {
30453 this.importGraph = importGraph;
30454 this.from = from;
30455 this.to = to;
30456 }
30457 /**
30458 * Compute an array of source-files that illustrates the cyclic path between `from` and `to`.
30459 *
30460 * Note that a `Cycle` will not be created unless a path is available between `to` and `from`,
30461 * so `findPath()` will never return `null`.
30462 */
30463 getPath() {
30464 return [this.from, ...this.importGraph.findPath(this.to, this.from)];
30465 }
30466 }
30467
30468 /**
30469 * @license
30470 * Copyright Google LLC All Rights Reserved.
30471 *
30472 * Use of this source code is governed by an MIT-style license that can be
30473 * found in the LICENSE file at https://angular.io/license
30474 */
30475 /**
30476 * A cached graph of imports in the `ts.Program`.
30477 *
30478 * The `ImportGraph` keeps track of dependencies (imports) of individual `ts.SourceFile`s. Only
30479 * dependencies within the same program are tracked; imports into packages on NPM are not.
30480 */
30481 class ImportGraph {
30482 constructor(resolver) {
30483 this.resolver = resolver;
30484 this.map = new Map();
30485 }
30486 /**
30487 * List the direct (not transitive) imports of a given `ts.SourceFile`.
30488 *
30489 * This operation is cached.
30490 */
30491 importsOf(sf) {
30492 if (!this.map.has(sf)) {
30493 this.map.set(sf, this.scanImports(sf));
30494 }
30495 return this.map.get(sf);
30496 }
30497 /**
30498 * Lists the transitive imports of a given `ts.SourceFile`.
30499 */
30500 transitiveImportsOf(sf) {
30501 const imports = new Set();
30502 this.transitiveImportsOfHelper(sf, imports);
30503 return imports;
30504 }
30505 transitiveImportsOfHelper(sf, results) {
30506 if (results.has(sf)) {
30507 return;
30508 }
30509 results.add(sf);
30510 this.importsOf(sf).forEach(imported => {
30511 this.transitiveImportsOfHelper(imported, results);
30512 });
30513 }
30514 /**
30515 * Find an import path from the `start` SourceFile to the `end` SourceFile.
30516 *
30517 * This function implements a breadth first search that results in finding the
30518 * shortest path between the `start` and `end` points.
30519 *
30520 * @param start the starting point of the path.
30521 * @param end the ending point of the path.
30522 * @returns an array of source files that connect the `start` and `end` source files, or `null` if
30523 * no path could be found.
30524 */
30525 findPath(start, end) {
30526 if (start === end) {
30527 // Escape early for the case where `start` and `end` are the same.
30528 return [start];
30529 }
30530 const found = new Set([start]);
30531 const queue = [new Found(start, null)];
30532 while (queue.length > 0) {
30533 const current = queue.shift();
30534 const imports = this.importsOf(current.sourceFile);
30535 for (const importedFile of imports) {
30536 if (!found.has(importedFile)) {
30537 const next = new Found(importedFile, current);
30538 if (next.sourceFile === end) {
30539 // We have hit the target `end` path so we can stop here.
30540 return next.toPath();
30541 }
30542 found.add(importedFile);
30543 queue.push(next);
30544 }
30545 }
30546 }
30547 return null;
30548 }
30549 /**
30550 * Add a record of an import from `sf` to `imported`, that's not present in the original
30551 * `ts.Program` but will be remembered by the `ImportGraph`.
30552 */
30553 addSyntheticImport(sf, imported) {
30554 if (isLocalFile(imported)) {
30555 this.importsOf(sf).add(imported);
30556 }
30557 }
30558 scanImports(sf) {
30559 const imports = new Set();
30560 // Look through the source file for import statements.
30561 sf.statements.forEach(stmt => {
30562 if ((ts$1.isImportDeclaration(stmt) || ts$1.isExportDeclaration(stmt)) &&
30563 stmt.moduleSpecifier !== undefined && ts$1.isStringLiteral(stmt.moduleSpecifier)) {
30564 // Resolve the module to a file, and check whether that file is in the ts.Program.
30565 const moduleName = stmt.moduleSpecifier.text;
30566 const moduleFile = this.resolver.resolveModule(moduleName, sf.fileName);
30567 if (moduleFile !== null && isLocalFile(moduleFile)) {
30568 // Record this local import.
30569 imports.add(moduleFile);
30570 }
30571 }
30572 });
30573 return imports;
30574 }
30575 }
30576 function isLocalFile(sf) {
30577 return !sf.fileName.endsWith('.d.ts');
30578 }
30579 /**
30580 * A helper class to track which SourceFiles are being processed when searching for a path in
30581 * `getPath()` above.
30582 */
30583 class Found {
30584 constructor(sourceFile, parent) {
30585 this.sourceFile = sourceFile;
30586 this.parent = parent;
30587 }
30588 /**
30589 * Back track through this found SourceFile and its ancestors to generate an array of
30590 * SourceFiles that form am import path between two SourceFiles.
30591 */
30592 toPath() {
30593 const array = [];
30594 let current = this;
30595 while (current !== null) {
30596 array.push(current.sourceFile);
30597 current = current.parent;
30598 }
30599 // Pushing and then reversing, O(n), rather than unshifting repeatedly, O(n^2), avoids
30600 // manipulating the array on every iteration: https://stackoverflow.com/a/26370620
30601 return array.reverse();
30602 }
30603 }
30604
30605 /**
30606 * @license
30607 * Copyright Google LLC All Rights Reserved.
30608 *
30609 * Use of this source code is governed by an MIT-style license that can be
30610 * found in the LICENSE file at https://angular.io/license
30611 */
30612 /**
30613 * Produce `ts.Diagnostic`s for classes that are visible from exported types (e.g. directives
30614 * exposed by exported `NgModule`s) that are not themselves exported.
30615 *
30616 * This function reconciles two concepts:
30617 *
30618 * A class is Exported if it's exported from the main library `entryPoint` file.
30619 * A class is Visible if, via Angular semantics, a downstream consumer can import an Exported class
30620 * and be affected by the class in question. For example, an Exported NgModule may expose a
30621 * directive class to its consumers. Consumers that import the NgModule may have the directive
30622 * applied to elements in their templates. In this case, the directive is considered Visible.
30623 *
30624 * `checkForPrivateExports` attempts to verify that all Visible classes are Exported, and report
30625 * `ts.Diagnostic`s for those that aren't.
30626 *
30627 * @param entryPoint `ts.SourceFile` of the library's entrypoint, which should export the library's
30628 * public API.
30629 * @param checker `ts.TypeChecker` for the current program.
30630 * @param refGraph `ReferenceGraph` tracking the visibility of Angular types.
30631 * @returns an array of `ts.Diagnostic`s representing errors when visible classes are not exported
30632 * properly.
30633 */
30634 function checkForPrivateExports(entryPoint, checker, refGraph) {
30635 const diagnostics = [];
30636 // Firstly, compute the exports of the entry point. These are all the Exported classes.
30637 const topLevelExports = new Set();
30638 // Do this via `ts.TypeChecker.getExportsOfModule`.
30639 const moduleSymbol = checker.getSymbolAtLocation(entryPoint);
30640 if (moduleSymbol === undefined) {
30641 throw new Error(`Internal error: failed to get symbol for entrypoint`);
30642 }
30643 const exportedSymbols = checker.getExportsOfModule(moduleSymbol);
30644 // Loop through the exported symbols, de-alias if needed, and add them to `topLevelExports`.
30645 // TODO(alxhub): use proper iteration when build.sh is removed. (#27762)
30646 exportedSymbols.forEach(symbol => {
30647 if (symbol.flags & ts$1.SymbolFlags.Alias) {
30648 symbol = checker.getAliasedSymbol(symbol);
30649 }
30650 const decl = symbol.valueDeclaration;
30651 if (decl !== undefined) {
30652 topLevelExports.add(decl);
30653 }
30654 });
30655 // Next, go through each exported class and expand it to the set of classes it makes Visible,
30656 // using the `ReferenceGraph`. For each Visible class, verify that it's also Exported, and queue
30657 // an error if it isn't. `checkedSet` ensures only one error is queued per class.
30658 const checkedSet = new Set();
30659 // Loop through each Exported class.
30660 // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
30661 topLevelExports.forEach(mainExport => {
30662 // Loop through each class made Visible by the Exported class.
30663 refGraph.transitiveReferencesOf(mainExport).forEach(transitiveReference => {
30664 // Skip classes which have already been checked.
30665 if (checkedSet.has(transitiveReference)) {
30666 return;
30667 }
30668 checkedSet.add(transitiveReference);
30669 // Verify that the Visible class is also Exported.
30670 if (!topLevelExports.has(transitiveReference)) {
30671 // This is an error, `mainExport` makes `transitiveReference` Visible, but
30672 // `transitiveReference` is not Exported from the entrypoint. Construct a diagnostic to
30673 // give to the user explaining the situation.
30674 const descriptor = getDescriptorOfDeclaration(transitiveReference);
30675 const name = getNameOfDeclaration(transitiveReference);
30676 // Construct the path of visibility, from `mainExport` to `transitiveReference`.
30677 let visibleVia = 'NgModule exports';
30678 const transitivePath = refGraph.pathFrom(mainExport, transitiveReference);
30679 if (transitivePath !== null) {
30680 visibleVia = transitivePath.map(seg => getNameOfDeclaration(seg)).join(' -> ');
30681 }
30682 const diagnostic = Object.assign(Object.assign({ category: ts$1.DiagnosticCategory.Error, code: ngErrorCode(ErrorCode.SYMBOL_NOT_EXPORTED), file: transitiveReference.getSourceFile() }, getPosOfDeclaration(transitiveReference)), { messageText: `Unsupported private ${descriptor} ${name}. This ${descriptor} is visible to consumers via ${visibleVia}, but is not exported from the top-level library entrypoint.` });
30683 diagnostics.push(diagnostic);
30684 }
30685 });
30686 });
30687 return diagnostics;
30688 }
30689 function getPosOfDeclaration(decl) {
30690 const node = getIdentifierOfDeclaration(decl) || decl;
30691 return {
30692 start: node.getStart(),
30693 length: node.getEnd() + 1 - node.getStart(),
30694 };
30695 }
30696 function getIdentifierOfDeclaration(decl) {
30697 if ((ts$1.isClassDeclaration(decl) || ts$1.isVariableDeclaration(decl) ||
30698 ts$1.isFunctionDeclaration(decl)) &&
30699 decl.name !== undefined && ts$1.isIdentifier(decl.name)) {
30700 return decl.name;
30701 }
30702 else {
30703 return null;
30704 }
30705 }
30706 function getNameOfDeclaration(decl) {
30707 const id = getIdentifierOfDeclaration(decl);
30708 return id !== null ? id.text : '(unnamed)';
30709 }
30710 function getDescriptorOfDeclaration(decl) {
30711 switch (decl.kind) {
30712 case ts$1.SyntaxKind.ClassDeclaration:
30713 return 'class';
30714 case ts$1.SyntaxKind.FunctionDeclaration:
30715 return 'function';
30716 case ts$1.SyntaxKind.VariableDeclaration:
30717 return 'variable';
30718 case ts$1.SyntaxKind.EnumDeclaration:
30719 return 'enum';
30720 default:
30721 return 'declaration';
30722 }
30723 }
30724
30725 /**
30726 * @license
30727 * Copyright Google LLC All Rights Reserved.
30728 *
30729 * Use of this source code is governed by an MIT-style license that can be
30730 * found in the LICENSE file at https://angular.io/license
30731 */
30732 class ReferenceGraph {
30733 constructor() {
30734 this.references = new Map();
30735 }
30736 add(from, to) {
30737 if (!this.references.has(from)) {
30738 this.references.set(from, new Set());
30739 }
30740 this.references.get(from).add(to);
30741 }
30742 transitiveReferencesOf(target) {
30743 const set = new Set();
30744 this.collectTransitiveReferences(set, target);
30745 return set;
30746 }
30747 pathFrom(source, target) {
30748 return this.collectPathFrom(source, target, new Set());
30749 }
30750 collectPathFrom(source, target, seen) {
30751 if (source === target) {
30752 // Looking for a path from the target to itself - that path is just the target. This is the
30753 // "base case" of the search.
30754 return [target];
30755 }
30756 else if (seen.has(source)) {
30757 // The search has already looked through this source before.
30758 return null;
30759 }
30760 // Consider outgoing edges from `source`.
30761 seen.add(source);
30762 if (!this.references.has(source)) {
30763 // There are no outgoing edges from `source`.
30764 return null;
30765 }
30766 else {
30767 // Look through the outgoing edges of `source`.
30768 // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
30769 let candidatePath = null;
30770 this.references.get(source).forEach(edge => {
30771 // Early exit if a path has already been found.
30772 if (candidatePath !== null) {
30773 return;
30774 }
30775 // Look for a path from this outgoing edge to `target`.
30776 const partialPath = this.collectPathFrom(edge, target, seen);
30777 if (partialPath !== null) {
30778 // A path exists from `edge` to `target`. Insert `source` at the beginning.
30779 candidatePath = [source, ...partialPath];
30780 }
30781 });
30782 return candidatePath;
30783 }
30784 }
30785 collectTransitiveReferences(set, decl) {
30786 if (this.references.has(decl)) {
30787 // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
30788 this.references.get(decl).forEach(ref => {
30789 if (!set.has(ref)) {
30790 set.add(ref);
30791 this.collectTransitiveReferences(set, ref);
30792 }
30793 });
30794 }
30795 }
30796 }
30797
30798 /**
30799 * @license
30800 * Copyright Google LLC All Rights Reserved.
30801 *
30802 * Use of this source code is governed by an MIT-style license that can be
30803 * found in the LICENSE file at https://angular.io/license
30804 */
30805 /**
30806 * An implementation of the `DependencyTracker` dependency graph API.
30807 *
30808 * The `FileDependencyGraph`'s primary job is to determine whether a given file has "logically"
30809 * changed, given the set of physical changes (direct changes to files on disk).
30810 *
30811 * A file is logically changed if at least one of three conditions is met:
30812 *
30813 * 1. The file itself has physically changed.
30814 * 2. One of its dependencies has physically changed.
30815 * 3. One of its resource dependencies has physically changed.
30816 */
30817 class FileDependencyGraph {
30818 constructor() {
30819 this.nodes = new Map();
30820 }
30821 addDependency(from, on) {
30822 this.nodeFor(from).dependsOn.add(on.fileName);
30823 }
30824 addResourceDependency(from, resource) {
30825 this.nodeFor(from).usesResources.add(resource);
30826 }
30827 addTransitiveDependency(from, on) {
30828 const nodeFrom = this.nodeFor(from);
30829 nodeFrom.dependsOn.add(on.fileName);
30830 const nodeOn = this.nodeFor(on);
30831 for (const dep of nodeOn.dependsOn) {
30832 nodeFrom.dependsOn.add(dep);
30833 }
30834 }
30835 addTransitiveResources(from, resourcesOf) {
30836 const nodeFrom = this.nodeFor(from);
30837 const nodeOn = this.nodeFor(resourcesOf);
30838 for (const dep of nodeOn.usesResources) {
30839 nodeFrom.usesResources.add(dep);
30840 }
30841 }
30842 recordDependencyAnalysisFailure(file) {
30843 this.nodeFor(file).failedAnalysis = true;
30844 }
30845 getResourceDependencies(from) {
30846 const node = this.nodes.get(from);
30847 return node ? [...node.usesResources] : [];
30848 }
30849 isStale(sf, changedTsPaths, changedResources) {
30850 return isLogicallyChanged(sf, this.nodeFor(sf), changedTsPaths, EMPTY_SET, changedResources);
30851 }
30852 /**
30853 * Update the current dependency graph from a previous one, incorporating a set of physical
30854 * changes.
30855 *
30856 * This method performs two tasks:
30857 *
30858 * 1. For files which have not logically changed, their dependencies from `previous` are added to
30859 * `this` graph.
30860 * 2. For files which have logically changed, they're added to a set of logically changed files
30861 * which is eventually returned.
30862 *
30863 * In essence, for build `n`, this method performs:
30864 *
30865 * G(n) + L(n) = G(n - 1) + P(n)
30866 *
30867 * where:
30868 *
30869 * G(n) = the dependency graph of build `n`
30870 * L(n) = the logically changed files from build n - 1 to build n.
30871 * P(n) = the physically changed files from build n - 1 to build n.
30872 */
30873 updateWithPhysicalChanges(previous, changedTsPaths, deletedTsPaths, changedResources) {
30874 const logicallyChanged = new Set();
30875 for (const sf of previous.nodes.keys()) {
30876 const node = previous.nodeFor(sf);
30877 if (isLogicallyChanged(sf, node, changedTsPaths, deletedTsPaths, changedResources)) {
30878 logicallyChanged.add(sf.fileName);
30879 }
30880 else if (!deletedTsPaths.has(sf.fileName)) {
30881 this.nodes.set(sf, {
30882 dependsOn: new Set(node.dependsOn),
30883 usesResources: new Set(node.usesResources),
30884 failedAnalysis: false,
30885 });
30886 }
30887 }
30888 return logicallyChanged;
30889 }
30890 nodeFor(sf) {
30891 if (!this.nodes.has(sf)) {
30892 this.nodes.set(sf, {
30893 dependsOn: new Set(),
30894 usesResources: new Set(),
30895 failedAnalysis: false,
30896 });
30897 }
30898 return this.nodes.get(sf);
30899 }
30900 }
30901 /**
30902 * Determine whether `sf` has logically changed, given its dependencies and the set of physically
30903 * changed files and resources.
30904 */
30905 function isLogicallyChanged(sf, node, changedTsPaths, deletedTsPaths, changedResources) {
30906 // A file is assumed to have logically changed if its dependencies could not be determined
30907 // accurately.
30908 if (node.failedAnalysis) {
30909 return true;
30910 }
30911 // A file is logically changed if it has physically changed itself (including being deleted).
30912 if (changedTsPaths.has(sf.fileName) || deletedTsPaths.has(sf.fileName)) {
30913 return true;
30914 }
30915 // A file is logically changed if one of its dependencies has physically changed.
30916 for (const dep of node.dependsOn) {
30917 if (changedTsPaths.has(dep) || deletedTsPaths.has(dep)) {
30918 return true;
30919 }
30920 }
30921 // A file is logically changed if one of its resources has physically changed.
30922 for (const dep of node.usesResources) {
30923 if (changedResources.has(dep)) {
30924 return true;
30925 }
30926 }
30927 return false;
30928 }
30929 const EMPTY_SET = new Set();
30930
30931 /**
30932 * @license
30933 * Copyright Google LLC All Rights Reserved.
30934 *
30935 * Use of this source code is governed by an MIT-style license that can be
30936 * found in the LICENSE file at https://angular.io/license
30937 */
30938 /**
30939 * Drives an incremental build, by tracking changes and determining which files need to be emitted.
30940 */
30941 class IncrementalDriver {
30942 constructor(state, allTsFiles, depGraph, logicalChanges) {
30943 this.allTsFiles = allTsFiles;
30944 this.depGraph = depGraph;
30945 this.logicalChanges = logicalChanges;
30946 this.state = state;
30947 }
30948 /**
30949 * Construct an `IncrementalDriver` with a starting state that incorporates the results of a
30950 * previous build.
30951 *
30952 * The previous build's `BuildState` is reconciled with the new program's changes, and the results
30953 * are merged into the new build's `PendingBuildState`.
30954 */
30955 static reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles) {
30956 // Initialize the state of the current build based on the previous one.
30957 let state;
30958 if (oldDriver.state.kind === BuildStateKind.Pending) {
30959 // The previous build never made it past the pending state. Reuse it as the starting state for
30960 // this build.
30961 state = oldDriver.state;
30962 }
30963 else {
30964 // The previous build was successfully analyzed. `pendingEmit` is the only state carried
30965 // forward into this build.
30966 state = {
30967 kind: BuildStateKind.Pending,
30968 pendingEmit: oldDriver.state.pendingEmit,
30969 changedResourcePaths: new Set(),
30970 changedTsPaths: new Set(),
30971 lastGood: oldDriver.state.lastGood,
30972 };
30973 }
30974 // Merge the freshly modified resource files with any prior ones.
30975 if (modifiedResourceFiles !== null) {
30976 for (const resFile of modifiedResourceFiles) {
30977 state.changedResourcePaths.add(absoluteFrom(resFile));
30978 }
30979 }
30980 // Next, process the files in the new program, with a couple of goals:
30981 // 1) Determine which TS files have changed, if any, and merge them into `changedTsFiles`.
30982 // 2) Produce a list of TS files which no longer exist in the program (they've been deleted
30983 // since the previous compilation). These need to be removed from the state tracking to avoid
30984 // leaking memory.
30985 // All files in the old program, for easy detection of changes.
30986 const oldFiles = new Set(oldProgram.getSourceFiles());
30987 // Assume all the old files were deleted to begin with. Only TS files are tracked.
30988 const deletedTsPaths = new Set(tsOnlyFiles(oldProgram).map(sf => sf.fileName));
30989 for (const newFile of newProgram.getSourceFiles()) {
30990 if (!newFile.isDeclarationFile) {
30991 // This file exists in the new program, so remove it from `deletedTsPaths`.
30992 deletedTsPaths.delete(newFile.fileName);
30993 }
30994 if (oldFiles.has(newFile)) {
30995 // This file hasn't changed; no need to look at it further.
30996 continue;
30997 }
30998 // The file has changed since the last successful build. The appropriate reaction depends on
30999 // what kind of file it is.
31000 if (!newFile.isDeclarationFile) {
31001 // It's a .ts file, so track it as a change.
31002 state.changedTsPaths.add(newFile.fileName);
31003 }
31004 else {
31005 // It's a .d.ts file. Currently the compiler does not do a great job of tracking
31006 // dependencies on .d.ts files, so bail out of incremental builds here and do a full build.
31007 // This usually only happens if something in node_modules changes.
31008 return IncrementalDriver.fresh(newProgram);
31009 }
31010 }
31011 // The next step is to remove any deleted files from the state.
31012 for (const filePath of deletedTsPaths) {
31013 state.pendingEmit.delete(filePath);
31014 // Even if the file doesn't exist in the current compilation, it still might have been changed
31015 // in a previous one, so delete it from the set of changed TS files, just in case.
31016 state.changedTsPaths.delete(filePath);
31017 }
31018 // Now, changedTsPaths contains physically changed TS paths. Use the previous program's logical
31019 // dependency graph to determine logically changed files.
31020 const depGraph = new FileDependencyGraph();
31021 // If a previous compilation exists, use its dependency graph to determine the set of logically
31022 // changed files.
31023 let logicalChanges = null;
31024 if (state.lastGood !== null) {
31025 // Extract the set of logically changed files. At the same time, this operation populates the
31026 // current (fresh) dependency graph with information about those files which have not
31027 // logically changed.
31028 logicalChanges = depGraph.updateWithPhysicalChanges(state.lastGood.depGraph, state.changedTsPaths, deletedTsPaths, state.changedResourcePaths);
31029 for (const fileName of state.changedTsPaths) {
31030 logicalChanges.add(fileName);
31031 }
31032 // Any logically changed files need to be re-emitted. Most of the time this would happen
31033 // regardless because the new dependency graph would _also_ identify the file as stale.
31034 // However there are edge cases such as removing a component from an NgModule without adding
31035 // it to another one, where the previous graph identifies the file as logically changed, but
31036 // the new graph (which does not have that edge) fails to identify that the file should be
31037 // re-emitted.
31038 for (const change of logicalChanges) {
31039 state.pendingEmit.add(change);
31040 }
31041 }
31042 // `state` now reflects the initial pending state of the current compilation.
31043 return new IncrementalDriver(state, new Set(tsOnlyFiles(newProgram)), depGraph, logicalChanges);
31044 }
31045 static fresh(program) {
31046 // Initialize the set of files which need to be emitted to the set of all TS files in the
31047 // program.
31048 const tsFiles = tsOnlyFiles(program);
31049 const state = {
31050 kind: BuildStateKind.Pending,
31051 pendingEmit: new Set(tsFiles.map(sf => sf.fileName)),
31052 changedResourcePaths: new Set(),
31053 changedTsPaths: new Set(),
31054 lastGood: null,
31055 };
31056 return new IncrementalDriver(state, new Set(tsFiles), new FileDependencyGraph(), /* logicalChanges */ null);
31057 }
31058 recordSuccessfulAnalysis(traitCompiler) {
31059 if (this.state.kind !== BuildStateKind.Pending) {
31060 // Changes have already been incorporated.
31061 return;
31062 }
31063 const pendingEmit = this.state.pendingEmit;
31064 const state = this.state;
31065 for (const sf of this.allTsFiles) {
31066 if (this.depGraph.isStale(sf, state.changedTsPaths, state.changedResourcePaths)) {
31067 // Something has changed which requires this file be re-emitted.
31068 pendingEmit.add(sf.fileName);
31069 }
31070 }
31071 // Update the state to an `AnalyzedBuildState`.
31072 this.state = {
31073 kind: BuildStateKind.Analyzed,
31074 pendingEmit,
31075 // Since this compilation was successfully analyzed, update the "last good" artifacts to the
31076 // ones from the current compilation.
31077 lastGood: {
31078 depGraph: this.depGraph,
31079 traitCompiler: traitCompiler,
31080 typeCheckingResults: null,
31081 },
31082 priorTypeCheckingResults: this.state.lastGood !== null ? this.state.lastGood.typeCheckingResults : null,
31083 };
31084 }
31085 recordSuccessfulTypeCheck(results) {
31086 if (this.state.lastGood === null || this.state.kind !== BuildStateKind.Analyzed) {
31087 return;
31088 }
31089 this.state.lastGood.typeCheckingResults = results;
31090 }
31091 recordSuccessfulEmit(sf) {
31092 this.state.pendingEmit.delete(sf.fileName);
31093 }
31094 safeToSkipEmit(sf) {
31095 return !this.state.pendingEmit.has(sf.fileName);
31096 }
31097 priorWorkFor(sf) {
31098 if (this.state.lastGood === null || this.logicalChanges === null) {
31099 // There is no previous good build, so no prior work exists.
31100 return null;
31101 }
31102 else if (this.logicalChanges.has(sf.fileName)) {
31103 // Prior work might exist, but would be stale as the file in question has logically changed.
31104 return null;
31105 }
31106 else {
31107 // Prior work might exist, and if it does then it's usable!
31108 return this.state.lastGood.traitCompiler.recordsFor(sf);
31109 }
31110 }
31111 priorTypeCheckingResultsFor(sf) {
31112 if (this.state.kind !== BuildStateKind.Analyzed ||
31113 this.state.priorTypeCheckingResults === null || this.logicalChanges === null) {
31114 return null;
31115 }
31116 if (this.logicalChanges.has(sf.fileName)) {
31117 return null;
31118 }
31119 const fileName = absoluteFromSourceFile(sf);
31120 if (!this.state.priorTypeCheckingResults.has(fileName)) {
31121 return null;
31122 }
31123 const data = this.state.priorTypeCheckingResults.get(fileName);
31124 if (data.hasInlines) {
31125 return null;
31126 }
31127 return data;
31128 }
31129 }
31130 var BuildStateKind;
31131 (function (BuildStateKind) {
31132 BuildStateKind[BuildStateKind["Pending"] = 0] = "Pending";
31133 BuildStateKind[BuildStateKind["Analyzed"] = 1] = "Analyzed";
31134 })(BuildStateKind || (BuildStateKind = {}));
31135 function tsOnlyFiles(program) {
31136 return program.getSourceFiles().filter(sf => !sf.isDeclarationFile);
31137 }
31138
31139 /**
31140 * @license
31141 * Copyright Google LLC All Rights Reserved.
31142 *
31143 * Use of this source code is governed by an MIT-style license that can be
31144 * found in the LICENSE file at https://angular.io/license
31145 */
31146 /**
31147 * Tracks an `IncrementalDriver` within the strategy itself.
31148 */
31149 class TrackedIncrementalBuildStrategy {
31150 constructor() {
31151 this.driver = null;
31152 this.isSet = false;
31153 }
31154 getIncrementalDriver() {
31155 return this.driver;
31156 }
31157 setIncrementalDriver(driver) {
31158 this.driver = driver;
31159 this.isSet = true;
31160 }
31161 toNextBuildStrategy() {
31162 const strategy = new TrackedIncrementalBuildStrategy();
31163 // Only reuse a driver that was explicitly set via `setIncrementalDriver`.
31164 strategy.driver = this.isSet ? this.driver : null;
31165 return strategy;
31166 }
31167 }
31168
31169 /**
31170 * @license
31171 * Copyright Google LLC All Rights Reserved.
31172 *
31173 * Use of this source code is governed by an MIT-style license that can be
31174 * found in the LICENSE file at https://angular.io/license
31175 */
31176 /**
31177 * Describes the kind of identifier found in a template.
31178 */
31179 var IdentifierKind;
31180 (function (IdentifierKind) {
31181 IdentifierKind[IdentifierKind["Property"] = 0] = "Property";
31182 IdentifierKind[IdentifierKind["Method"] = 1] = "Method";
31183 IdentifierKind[IdentifierKind["Element"] = 2] = "Element";
31184 IdentifierKind[IdentifierKind["Template"] = 3] = "Template";
31185 IdentifierKind[IdentifierKind["Attribute"] = 4] = "Attribute";
31186 IdentifierKind[IdentifierKind["Reference"] = 5] = "Reference";
31187 IdentifierKind[IdentifierKind["Variable"] = 6] = "Variable";
31188 })(IdentifierKind || (IdentifierKind = {}));
31189 /**
31190 * Describes the absolute byte offsets of a text anchor in a source code.
31191 */
31192 class AbsoluteSourceSpan$1 {
31193 constructor(start, end) {
31194 this.start = start;
31195 this.end = end;
31196 }
31197 }
31198
31199 /**
31200 * @license
31201 * Copyright Google LLC All Rights Reserved.
31202 *
31203 * Use of this source code is governed by an MIT-style license that can be
31204 * found in the LICENSE file at https://angular.io/license
31205 */
31206 /**
31207 * A context for storing indexing infromation about components of a program.
31208 *
31209 * An `IndexingContext` collects component and template analysis information from
31210 * `DecoratorHandler`s and exposes them to be indexed.
31211 */
31212 class IndexingContext {
31213 constructor() {
31214 this.components = new Set();
31215 }
31216 /**
31217 * Adds a component to the context.
31218 */
31219 addComponent(info) {
31220 this.components.add(info);
31221 }
31222 }
31223
31224 /**
31225 * @license
31226 * Copyright Google LLC All Rights Reserved.
31227 *
31228 * Use of this source code is governed by an MIT-style license that can be
31229 * found in the LICENSE file at https://angular.io/license
31230 */
31231 /**
31232 * Visits the AST of an Angular template syntax expression, finding interesting
31233 * entities (variable references, etc.). Creates an array of Entities found in
31234 * the expression, with the location of the Entities being relative to the
31235 * expression.
31236 *
31237 * Visiting `text {{prop}}` will return
31238 * `[TopLevelIdentifier {name: 'prop', span: {start: 7, end: 11}}]`.
31239 */
31240 class ExpressionVisitor extends RecursiveAstVisitor {
31241 constructor(expressionStr, absoluteOffset, boundTemplate, targetToIdentifier) {
31242 super();
31243 this.expressionStr = expressionStr;
31244 this.absoluteOffset = absoluteOffset;
31245 this.boundTemplate = boundTemplate;
31246 this.targetToIdentifier = targetToIdentifier;
31247 this.identifiers = [];
31248 }
31249 /**
31250 * Returns identifiers discovered in an expression.
31251 *
31252 * @param ast expression AST to visit
31253 * @param source expression AST source code
31254 * @param absoluteOffset absolute byte offset from start of the file to the start of the AST
31255 * source code.
31256 * @param boundTemplate bound target of the entire template, which can be used to query for the
31257 * entities expressions target.
31258 * @param targetToIdentifier closure converting a template target node to its identifier.
31259 */
31260 static getIdentifiers(ast, source, absoluteOffset, boundTemplate, targetToIdentifier) {
31261 const visitor = new ExpressionVisitor(source, absoluteOffset, boundTemplate, targetToIdentifier);
31262 visitor.visit(ast);
31263 return visitor.identifiers;
31264 }
31265 visit(ast) {
31266 ast.visit(this);
31267 }
31268 visitMethodCall(ast, context) {
31269 this.visitIdentifier(ast, IdentifierKind.Method);
31270 super.visitMethodCall(ast, context);
31271 }
31272 visitPropertyRead(ast, context) {
31273 this.visitIdentifier(ast, IdentifierKind.Property);
31274 super.visitPropertyRead(ast, context);
31275 }
31276 visitPropertyWrite(ast, context) {
31277 this.visitIdentifier(ast, IdentifierKind.Property);
31278 super.visitPropertyWrite(ast, context);
31279 }
31280 /**
31281 * Visits an identifier, adding it to the identifier store if it is useful for indexing.
31282 *
31283 * @param ast expression AST the identifier is in
31284 * @param kind identifier kind
31285 */
31286 visitIdentifier(ast, kind) {
31287 // The definition of a non-top-level property such as `bar` in `{{foo.bar}}` is currently
31288 // impossible to determine by an indexer and unsupported by the indexing module.
31289 // The indexing module also does not currently support references to identifiers declared in the
31290 // template itself, which have a non-null expression target.
31291 if (!(ast.receiver instanceof ImplicitReceiver)) {
31292 return;
31293 }
31294 // The source span of the requested AST starts at a location that is offset from the expression.
31295 const identifierStart = ast.sourceSpan.start - this.absoluteOffset;
31296 if (!this.expressionStr.substring(identifierStart).startsWith(ast.name)) {
31297 throw new Error(`Impossible state: "${ast.name}" not found in "${this.expressionStr}" at location ${identifierStart}`);
31298 }
31299 // Join the relative position of the expression within a node with the absolute position
31300 // of the node to get the absolute position of the expression in the source code.
31301 const absoluteStart = this.absoluteOffset + identifierStart;
31302 const span = new AbsoluteSourceSpan$1(absoluteStart, absoluteStart + ast.name.length);
31303 const targetAst = this.boundTemplate.getExpressionTarget(ast);
31304 const target = targetAst ? this.targetToIdentifier(targetAst) : null;
31305 const identifier = {
31306 name: ast.name,
31307 span,
31308 kind,
31309 target,
31310 };
31311 this.identifiers.push(identifier);
31312 }
31313 }
31314 /**
31315 * Visits the AST of a parsed Angular template. Discovers and stores
31316 * identifiers of interest, deferring to an `ExpressionVisitor` as needed.
31317 */
31318 class TemplateVisitor extends RecursiveVisitor {
31319 /**
31320 * Creates a template visitor for a bound template target. The bound target can be used when
31321 * deferred to the expression visitor to get information about the target of an expression.
31322 *
31323 * @param boundTemplate bound template target
31324 */
31325 constructor(boundTemplate) {
31326 super();
31327 this.boundTemplate = boundTemplate;
31328 // Identifiers of interest found in the template.
31329 this.identifiers = new Set();
31330 // Map of targets in a template to their identifiers.
31331 this.targetIdentifierCache = new Map();
31332 // Map of elements and templates to their identifiers.
31333 this.elementAndTemplateIdentifierCache = new Map();
31334 }
31335 /**
31336 * Visits a node in the template.
31337 *
31338 * @param node node to visit
31339 */
31340 visit(node) {
31341 node.visit(this);
31342 }
31343 visitAll(nodes) {
31344 nodes.forEach(node => this.visit(node));
31345 }
31346 /**
31347 * Add an identifier for an HTML element and visit its children recursively.
31348 *
31349 * @param element
31350 */
31351 visitElement(element) {
31352 const elementIdentifier = this.elementOrTemplateToIdentifier(element);
31353 this.identifiers.add(elementIdentifier);
31354 this.visitAll(element.references);
31355 this.visitAll(element.inputs);
31356 this.visitAll(element.attributes);
31357 this.visitAll(element.children);
31358 this.visitAll(element.outputs);
31359 }
31360 visitTemplate(template) {
31361 const templateIdentifier = this.elementOrTemplateToIdentifier(template);
31362 this.identifiers.add(templateIdentifier);
31363 this.visitAll(template.variables);
31364 this.visitAll(template.attributes);
31365 this.visitAll(template.templateAttrs);
31366 this.visitAll(template.children);
31367 this.visitAll(template.references);
31368 }
31369 visitBoundAttribute(attribute) {
31370 // If the bound attribute has no value, it cannot have any identifiers in the value expression.
31371 if (attribute.valueSpan === undefined) {
31372 return;
31373 }
31374 const identifiers = ExpressionVisitor.getIdentifiers(attribute.value, attribute.valueSpan.toString(), attribute.valueSpan.start.offset, this.boundTemplate, this.targetToIdentifier.bind(this));
31375 identifiers.forEach(id => this.identifiers.add(id));
31376 }
31377 visitBoundEvent(attribute) {
31378 this.visitExpression(attribute.handler);
31379 }
31380 visitBoundText(text) {
31381 this.visitExpression(text.value);
31382 }
31383 visitReference(reference) {
31384 const referenceIdentifer = this.targetToIdentifier(reference);
31385 this.identifiers.add(referenceIdentifer);
31386 }
31387 visitVariable(variable) {
31388 const variableIdentifier = this.targetToIdentifier(variable);
31389 this.identifiers.add(variableIdentifier);
31390 }
31391 /** Creates an identifier for a template element or template node. */
31392 elementOrTemplateToIdentifier(node) {
31393 // If this node has already been seen, return the cached result.
31394 if (this.elementAndTemplateIdentifierCache.has(node)) {
31395 return this.elementAndTemplateIdentifierCache.get(node);
31396 }
31397 let name;
31398 let kind;
31399 if (node instanceof Template) {
31400 name = node.tagName;
31401 kind = IdentifierKind.Template;
31402 }
31403 else {
31404 name = node.name;
31405 kind = IdentifierKind.Element;
31406 }
31407 const sourceSpan = node.startSourceSpan;
31408 // An element's or template's source span can be of the form `<element>`, `<element />`, or
31409 // `<element></element>`. Only the selector is interesting to the indexer, so the source is
31410 // searched for the first occurrence of the element (selector) name.
31411 const start = this.getStartLocation(name, sourceSpan);
31412 const absoluteSpan = new AbsoluteSourceSpan$1(start, start + name.length);
31413 // Record the nodes's attributes, which an indexer can later traverse to see if any of them
31414 // specify a used directive on the node.
31415 const attributes = node.attributes.map(({ name, sourceSpan }) => {
31416 return {
31417 name,
31418 span: new AbsoluteSourceSpan$1(sourceSpan.start.offset, sourceSpan.end.offset),
31419 kind: IdentifierKind.Attribute,
31420 };
31421 });
31422 const usedDirectives = this.boundTemplate.getDirectivesOfNode(node) || [];
31423 const identifier = {
31424 name,
31425 span: absoluteSpan,
31426 kind,
31427 attributes: new Set(attributes),
31428 usedDirectives: new Set(usedDirectives.map(dir => {
31429 return {
31430 node: dir.ref.node,
31431 selector: dir.selector,
31432 };
31433 })),
31434 };
31435 this.elementAndTemplateIdentifierCache.set(node, identifier);
31436 return identifier;
31437 }
31438 /** Creates an identifier for a template reference or template variable target. */
31439 targetToIdentifier(node) {
31440 // If this node has already been seen, return the cached result.
31441 if (this.targetIdentifierCache.has(node)) {
31442 return this.targetIdentifierCache.get(node);
31443 }
31444 const { name, sourceSpan } = node;
31445 const start = this.getStartLocation(name, sourceSpan);
31446 const span = new AbsoluteSourceSpan$1(start, start + name.length);
31447 let identifier;
31448 if (node instanceof Reference) {
31449 // If the node is a reference, we care about its target. The target can be an element, a
31450 // template, a directive applied on a template or element (in which case the directive field
31451 // is non-null), or nothing at all.
31452 const refTarget = this.boundTemplate.getReferenceTarget(node);
31453 let target = null;
31454 if (refTarget) {
31455 if (refTarget instanceof Element || refTarget instanceof Template) {
31456 target = {
31457 node: this.elementOrTemplateToIdentifier(refTarget),
31458 directive: null,
31459 };
31460 }
31461 else {
31462 target = {
31463 node: this.elementOrTemplateToIdentifier(refTarget.node),
31464 directive: refTarget.directive.ref.node,
31465 };
31466 }
31467 }
31468 identifier = {
31469 name,
31470 span,
31471 kind: IdentifierKind.Reference,
31472 target,
31473 };
31474 }
31475 else {
31476 identifier = {
31477 name,
31478 span,
31479 kind: IdentifierKind.Variable,
31480 };
31481 }
31482 this.targetIdentifierCache.set(node, identifier);
31483 return identifier;
31484 }
31485 /** Gets the start location of a string in a SourceSpan */
31486 getStartLocation(name, context) {
31487 const localStr = context.toString();
31488 if (!localStr.includes(name)) {
31489 throw new Error(`Impossible state: "${name}" not found in "${localStr}"`);
31490 }
31491 return context.start.offset + localStr.indexOf(name);
31492 }
31493 /**
31494 * Visits a node's expression and adds its identifiers, if any, to the visitor's state.
31495 * Only ASTs with information about the expression source and its location are visited.
31496 *
31497 * @param node node whose expression to visit
31498 */
31499 visitExpression(ast) {
31500 // Only include ASTs that have information about their source and absolute source spans.
31501 if (ast instanceof ASTWithSource && ast.source !== null) {
31502 // Make target to identifier mapping closure stateful to this visitor instance.
31503 const targetToIdentifier = this.targetToIdentifier.bind(this);
31504 const absoluteOffset = ast.sourceSpan.start;
31505 const identifiers = ExpressionVisitor.getIdentifiers(ast, ast.source, absoluteOffset, this.boundTemplate, targetToIdentifier);
31506 identifiers.forEach(id => this.identifiers.add(id));
31507 }
31508 }
31509 }
31510 /**
31511 * Traverses a template AST and builds identifiers discovered in it.
31512 *
31513 * @param boundTemplate bound template target, which can be used for querying expression targets.
31514 * @return identifiers in template
31515 */
31516 function getTemplateIdentifiers(boundTemplate) {
31517 const visitor = new TemplateVisitor(boundTemplate);
31518 if (boundTemplate.target.template !== undefined) {
31519 visitor.visitAll(boundTemplate.target.template);
31520 }
31521 return visitor.identifiers;
31522 }
31523
31524 /**
31525 * @license
31526 * Copyright Google LLC All Rights Reserved.
31527 *
31528 * Use of this source code is governed by an MIT-style license that can be
31529 * found in the LICENSE file at https://angular.io/license
31530 */
31531 /**
31532 * Generates `IndexedComponent` entries from a `IndexingContext`, which has information
31533 * about components discovered in the program registered in it.
31534 *
31535 * The context must be populated before `generateAnalysis` is called.
31536 */
31537 function generateAnalysis(context) {
31538 const analysis = new Map();
31539 context.components.forEach(({ declaration, selector, boundTemplate, templateMeta }) => {
31540 const name = declaration.name.getText();
31541 const usedComponents = new Set();
31542 const usedDirs = boundTemplate.getUsedDirectives();
31543 usedDirs.forEach(dir => {
31544 if (dir.isComponent) {
31545 usedComponents.add(dir.ref.node);
31546 }
31547 });
31548 // Get source files for the component and the template. If the template is inline, its source
31549 // file is the component's.
31550 const componentFile = new ParseSourceFile(declaration.getSourceFile().getFullText(), declaration.getSourceFile().fileName);
31551 let templateFile;
31552 if (templateMeta.isInline) {
31553 templateFile = componentFile;
31554 }
31555 else {
31556 templateFile = templateMeta.file;
31557 }
31558 analysis.set(declaration, {
31559 name,
31560 selector,
31561 file: componentFile,
31562 template: {
31563 identifiers: getTemplateIdentifiers(boundTemplate),
31564 usedComponents,
31565 isInline: templateMeta.isInline,
31566 file: templateFile,
31567 },
31568 });
31569 });
31570 return analysis;
31571 }
31572
31573 /**
31574 * @license
31575 * Copyright Google LLC All Rights Reserved.
31576 *
31577 * Use of this source code is governed by an MIT-style license that can be
31578 * found in the LICENSE file at https://angular.io/license
31579 */
31580 class ModuleWithProvidersScanner {
31581 constructor(host, evaluator, emitter) {
31582 this.host = host;
31583 this.evaluator = evaluator;
31584 this.emitter = emitter;
31585 }
31586 scan(sf, dts) {
31587 for (const stmt of sf.statements) {
31588 this.visitStatement(dts, stmt);
31589 }
31590 }
31591 visitStatement(dts, stmt) {
31592 // Detect whether a statement is exported, which is used as one of the hints whether to look
31593 // more closely at possible MWP functions within. This is a syntactic check, not a semantic
31594 // check, so it won't detect cases like:
31595 //
31596 // var X = ...;
31597 // export {X}
31598 //
31599 // This is intentional, because the alternative is slow and this will catch 99% of the cases we
31600 // need to handle.
31601 const isExported = stmt.modifiers !== undefined &&
31602 stmt.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.ExportKeyword);
31603 if (!isExported) {
31604 return;
31605 }
31606 if (ts$1.isClassDeclaration(stmt)) {
31607 for (const member of stmt.members) {
31608 if (!ts$1.isMethodDeclaration(member) || !isStatic(member)) {
31609 continue;
31610 }
31611 this.visitFunctionOrMethodDeclaration(dts, member);
31612 }
31613 }
31614 else if (ts$1.isFunctionDeclaration(stmt)) {
31615 this.visitFunctionOrMethodDeclaration(dts, stmt);
31616 }
31617 }
31618 visitFunctionOrMethodDeclaration(dts, decl) {
31619 // First, some sanity. This should have a method body with a single return statement.
31620 if (decl.body === undefined || decl.body.statements.length !== 1) {
31621 return;
31622 }
31623 const retStmt = decl.body.statements[0];
31624 if (!ts$1.isReturnStatement(retStmt) || retStmt.expression === undefined) {
31625 return;
31626 }
31627 const retValue = retStmt.expression;
31628 // Now, look at the return type of the method. Maybe bail if the type is already marked, or if
31629 // it's incompatible with a MWP function.
31630 const returnType = this.returnTypeOf(decl);
31631 if (returnType === ReturnType.OTHER || returnType === ReturnType.MWP_WITH_TYPE) {
31632 // Don't process this declaration, it either already declares the right return type, or an
31633 // incompatible one.
31634 return;
31635 }
31636 const value = this.evaluator.evaluate(retValue);
31637 if (!(value instanceof Map) || !value.has('ngModule')) {
31638 // The return value does not provide sufficient information to be able to add a generic type.
31639 return;
31640 }
31641 if (returnType === ReturnType.INFERRED && !isModuleWithProvidersType(value)) {
31642 // The return type is inferred but the returned object is not of the correct shape, so we
31643 // shouldn's modify the return type to become `ModuleWithProviders`.
31644 return;
31645 }
31646 // The return type has been verified to represent the `ModuleWithProviders` type, but either the
31647 // return type is inferred or the generic type argument is missing. In both cases, a new return
31648 // type is created where the `ngModule` type is included as generic type argument.
31649 const ngModule = value.get('ngModule');
31650 if (!(ngModule instanceof Reference$1) || !ts$1.isClassDeclaration(ngModule.node)) {
31651 return;
31652 }
31653 const ngModuleExpr = this.emitter.emit(ngModule, decl.getSourceFile(), ImportFlags.ForceNewImport);
31654 const ngModuleType = new ExpressionType(ngModuleExpr);
31655 const mwpNgType = new ExpressionType(new ExternalExpr(Identifiers$1.ModuleWithProviders), [ /* modifiers */], [ngModuleType]);
31656 dts.addTypeReplacement(decl, mwpNgType);
31657 }
31658 returnTypeOf(decl) {
31659 if (decl.type === undefined) {
31660 return ReturnType.INFERRED;
31661 }
31662 else if (!ts$1.isTypeReferenceNode(decl.type)) {
31663 return ReturnType.OTHER;
31664 }
31665 // Try to figure out if the type is of a familiar form, something that looks like it was
31666 // imported.
31667 let typeId;
31668 if (ts$1.isIdentifier(decl.type.typeName)) {
31669 // def: ModuleWithProviders
31670 typeId = decl.type.typeName;
31671 }
31672 else if (ts$1.isQualifiedName(decl.type.typeName) && ts$1.isIdentifier(decl.type.typeName.left)) {
31673 // def: i0.ModuleWithProviders
31674 typeId = decl.type.typeName.right;
31675 }
31676 else {
31677 return ReturnType.OTHER;
31678 }
31679 const importDecl = this.host.getImportOfIdentifier(typeId);
31680 if (importDecl === null || importDecl.from !== '@angular/core' ||
31681 importDecl.name !== 'ModuleWithProviders') {
31682 return ReturnType.OTHER;
31683 }
31684 if (decl.type.typeArguments === undefined || decl.type.typeArguments.length === 0) {
31685 // The return type is indeed ModuleWithProviders, but no generic type parameter was found.
31686 return ReturnType.MWP_NO_TYPE;
31687 }
31688 else {
31689 // The return type is ModuleWithProviders, and the user has already specified a generic type.
31690 return ReturnType.MWP_WITH_TYPE;
31691 }
31692 }
31693 }
31694 var ReturnType;
31695 (function (ReturnType) {
31696 ReturnType[ReturnType["INFERRED"] = 0] = "INFERRED";
31697 ReturnType[ReturnType["MWP_NO_TYPE"] = 1] = "MWP_NO_TYPE";
31698 ReturnType[ReturnType["MWP_WITH_TYPE"] = 2] = "MWP_WITH_TYPE";
31699 ReturnType[ReturnType["OTHER"] = 3] = "OTHER";
31700 })(ReturnType || (ReturnType = {}));
31701 /** Whether the resolved value map represents a ModuleWithProviders object */
31702 function isModuleWithProvidersType(value) {
31703 const ngModule = value.has('ngModule');
31704 const providers = value.has('providers');
31705 return ngModule && (value.size === 1 || (providers && value.size === 2));
31706 }
31707 function isStatic(node) {
31708 return node.modifiers !== undefined &&
31709 node.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.StaticKeyword);
31710 }
31711
31712 const NOOP_PERF_RECORDER = {
31713 enabled: false,
31714 mark: (name, node, category, detail) => { },
31715 start: (name, node, category, detail) => {
31716 return 0;
31717 },
31718 stop: (span) => { },
31719 };
31720
31721 /**
31722 * @license
31723 * Copyright Google LLC All Rights Reserved.
31724 *
31725 * Use of this source code is governed by an MIT-style license that can be
31726 * found in the LICENSE file at https://angular.io/license
31727 */
31728 var PerfLogEventType;
31729 (function (PerfLogEventType) {
31730 PerfLogEventType[PerfLogEventType["SPAN_OPEN"] = 0] = "SPAN_OPEN";
31731 PerfLogEventType[PerfLogEventType["SPAN_CLOSE"] = 1] = "SPAN_CLOSE";
31732 PerfLogEventType[PerfLogEventType["MARK"] = 2] = "MARK";
31733 })(PerfLogEventType || (PerfLogEventType = {}));
31734
31735 /**
31736 * @license
31737 * Copyright Google LLC All Rights Reserved.
31738 *
31739 * Use of this source code is governed by an MIT-style license that can be
31740 * found in the LICENSE file at https://angular.io/license
31741 */
31742 const CSS_PREPROCESSOR_EXT = /(\.scss|\.sass|\.less|\.styl)$/;
31743 const RESOURCE_MARKER = '.$ngresource$';
31744 const RESOURCE_MARKER_TS = RESOURCE_MARKER + '.ts';
31745 /**
31746 * `ResourceLoader` which delegates to an `NgCompilerAdapter`'s resource loading methods.
31747 */
31748 class AdapterResourceLoader {
31749 constructor(adapter, options) {
31750 this.adapter = adapter;
31751 this.options = options;
31752 this.cache = new Map();
31753 this.fetching = new Map();
31754 this.lookupResolutionHost = createLookupResolutionHost(this.adapter);
31755 this.canPreload = !!this.adapter.readResource;
31756 }
31757 /**
31758 * Resolve the url of a resource relative to the file that contains the reference to it.
31759 * The return value of this method can be used in the `load()` and `preload()` methods.
31760 *
31761 * Uses the provided CompilerHost if it supports mapping resources to filenames.
31762 * Otherwise, uses a fallback mechanism that searches the module resolution candidates.
31763 *
31764 * @param url The, possibly relative, url of the resource.
31765 * @param fromFile The path to the file that contains the URL of the resource.
31766 * @returns A resolved url of resource.
31767 * @throws An error if the resource cannot be resolved.
31768 */
31769 resolve(url, fromFile) {
31770 let resolvedUrl = null;
31771 if (this.adapter.resourceNameToFileName) {
31772 resolvedUrl = this.adapter.resourceNameToFileName(url, fromFile);
31773 }
31774 else {
31775 resolvedUrl = this.fallbackResolve(url, fromFile);
31776 }
31777 if (resolvedUrl === null) {
31778 throw new Error(`HostResourceResolver: could not resolve ${url} in context of ${fromFile})`);
31779 }
31780 return resolvedUrl;
31781 }
31782 /**
31783 * Preload the specified resource, asynchronously.
31784 *
31785 * Once the resource is loaded, its value is cached so it can be accessed synchronously via the
31786 * `load()` method.
31787 *
31788 * @param resolvedUrl The url (resolved by a call to `resolve()`) of the resource to preload.
31789 * @returns A Promise that is resolved once the resource has been loaded or `undefined` if the
31790 * file has already been loaded.
31791 * @throws An Error if pre-loading is not available.
31792 */
31793 preload(resolvedUrl) {
31794 if (!this.adapter.readResource) {
31795 throw new Error('HostResourceLoader: the CompilerHost provided does not support pre-loading resources.');
31796 }
31797 if (this.cache.has(resolvedUrl)) {
31798 return undefined;
31799 }
31800 else if (this.fetching.has(resolvedUrl)) {
31801 return this.fetching.get(resolvedUrl);
31802 }
31803 const result = this.adapter.readResource(resolvedUrl);
31804 if (typeof result === 'string') {
31805 this.cache.set(resolvedUrl, result);
31806 return undefined;
31807 }
31808 else {
31809 const fetchCompletion = result.then(str => {
31810 this.fetching.delete(resolvedUrl);
31811 this.cache.set(resolvedUrl, str);
31812 });
31813 this.fetching.set(resolvedUrl, fetchCompletion);
31814 return fetchCompletion;
31815 }
31816 }
31817 /**
31818 * Load the resource at the given url, synchronously.
31819 *
31820 * The contents of the resource may have been cached by a previous call to `preload()`.
31821 *
31822 * @param resolvedUrl The url (resolved by a call to `resolve()`) of the resource to load.
31823 * @returns The contents of the resource.
31824 */
31825 load(resolvedUrl) {
31826 if (this.cache.has(resolvedUrl)) {
31827 return this.cache.get(resolvedUrl);
31828 }
31829 const result = this.adapter.readResource ? this.adapter.readResource(resolvedUrl) :
31830 this.adapter.readFile(resolvedUrl);
31831 if (typeof result !== 'string') {
31832 throw new Error(`HostResourceLoader: loader(${resolvedUrl}) returned a Promise`);
31833 }
31834 this.cache.set(resolvedUrl, result);
31835 return result;
31836 }
31837 /**
31838 * Invalidate the entire resource cache.
31839 */
31840 invalidate() {
31841 this.cache.clear();
31842 }
31843 /**
31844 * Attempt to resolve `url` in the context of `fromFile`, while respecting the rootDirs
31845 * option from the tsconfig. First, normalize the file name.
31846 */
31847 fallbackResolve(url, fromFile) {
31848 let candidateLocations;
31849 if (url.startsWith('/')) {
31850 // This path is not really an absolute path, but instead the leading '/' means that it's
31851 // rooted in the project rootDirs. So look for it according to the rootDirs.
31852 candidateLocations = this.getRootedCandidateLocations(url);
31853 }
31854 else {
31855 // This path is a "relative" path and can be resolved as such. To make this easier on the
31856 // downstream resolver, the './' prefix is added if missing to distinguish these paths from
31857 // absolute node_modules paths.
31858 if (!url.startsWith('.')) {
31859 url = `./${url}`;
31860 }
31861 candidateLocations = this.getResolvedCandidateLocations(url, fromFile);
31862 }
31863 for (const candidate of candidateLocations) {
31864 if (this.adapter.fileExists(candidate)) {
31865 return candidate;
31866 }
31867 else if (CSS_PREPROCESSOR_EXT.test(candidate)) {
31868 /**
31869 * If the user specified styleUrl points to *.scss, but the Sass compiler was run before
31870 * Angular, then the resource may have been generated as *.css. Simply try the resolution
31871 * again.
31872 */
31873 const cssFallbackUrl = candidate.replace(CSS_PREPROCESSOR_EXT, '.css');
31874 if (this.adapter.fileExists(cssFallbackUrl)) {
31875 return cssFallbackUrl;
31876 }
31877 }
31878 }
31879 return null;
31880 }
31881 getRootedCandidateLocations(url) {
31882 // The path already starts with '/', so add a '.' to make it relative.
31883 const segment = ('.' + url);
31884 return this.adapter.rootDirs.map(rootDir => join(rootDir, segment));
31885 }
31886 /**
31887 * TypeScript provides utilities to resolve module names, but not resource files (which aren't
31888 * a part of the ts.Program). However, TypeScript's module resolution can be used creatively
31889 * to locate where resource files should be expected to exist. Since module resolution returns
31890 * a list of file names that were considered, the loader can enumerate the possible locations
31891 * for the file by setting up a module resolution for it that will fail.
31892 */
31893 getResolvedCandidateLocations(url, fromFile) {
31894 // clang-format off
31895 const failedLookup = ts$1.resolveModuleName(url + RESOURCE_MARKER, fromFile, this.options, this.lookupResolutionHost);
31896 // clang-format on
31897 if (failedLookup.failedLookupLocations === undefined) {
31898 throw new Error(`Internal error: expected to find failedLookupLocations during resolution of resource '${url}' in context of ${fromFile}`);
31899 }
31900 return failedLookup.failedLookupLocations
31901 .filter(candidate => candidate.endsWith(RESOURCE_MARKER_TS))
31902 .map(candidate => candidate.slice(0, -RESOURCE_MARKER_TS.length));
31903 }
31904 }
31905 /**
31906 * Derives a `ts.ModuleResolutionHost` from a compiler adapter that recognizes the special resource
31907 * marker and does not go to the filesystem for these requests, as they are known not to exist.
31908 */
31909 function createLookupResolutionHost(adapter) {
31910 var _a, _b, _c;
31911 return {
31912 directoryExists(directoryName) {
31913 if (directoryName.includes(RESOURCE_MARKER)) {
31914 return false;
31915 }
31916 else if (adapter.directoryExists !== undefined) {
31917 return adapter.directoryExists(directoryName);
31918 }
31919 else {
31920 // TypeScript's module resolution logic assumes that the directory exists when no host
31921 // implementation is available.
31922 return true;
31923 }
31924 },
31925 fileExists(fileName) {
31926 if (fileName.includes(RESOURCE_MARKER)) {
31927 return false;
31928 }
31929 else {
31930 return adapter.fileExists(fileName);
31931 }
31932 },
31933 readFile: adapter.readFile.bind(adapter),
31934 getCurrentDirectory: adapter.getCurrentDirectory.bind(adapter),
31935 getDirectories: (_a = adapter.getDirectories) === null || _a === void 0 ? void 0 : _a.bind(adapter),
31936 realpath: (_b = adapter.realpath) === null || _b === void 0 ? void 0 : _b.bind(adapter),
31937 trace: (_c = adapter.trace) === null || _c === void 0 ? void 0 : _c.bind(adapter),
31938 };
31939 }
31940
31941 /**
31942 * @license
31943 * Copyright Google LLC All Rights Reserved.
31944 *
31945 * Use of this source code is governed by an MIT-style license that can be
31946 * found in the LICENSE file at https://angular.io/license
31947 */
31948 class RouterEntryPointImpl {
31949 constructor(filePath, moduleName) {
31950 this.filePath = filePath;
31951 this.moduleName = moduleName;
31952 }
31953 get name() {
31954 return this.moduleName;
31955 }
31956 // For debugging purposes.
31957 toString() {
31958 return `RouterEntryPoint(name: ${this.name}, filePath: ${this.filePath})`;
31959 }
31960 }
31961 class RouterEntryPointManager {
31962 constructor(moduleResolver) {
31963 this.moduleResolver = moduleResolver;
31964 this.map = new Map();
31965 }
31966 resolveLoadChildrenIdentifier(loadChildrenIdentifier, context) {
31967 const [relativeFile, moduleName] = loadChildrenIdentifier.split('#');
31968 if (moduleName === undefined) {
31969 return null;
31970 }
31971 const resolvedSf = this.moduleResolver.resolveModule(relativeFile, context.fileName);
31972 if (resolvedSf === null) {
31973 return null;
31974 }
31975 return this.fromNgModule(resolvedSf, moduleName);
31976 }
31977 fromNgModule(sf, moduleName) {
31978 const key = entryPointKeyFor(sf.fileName, moduleName);
31979 if (!this.map.has(key)) {
31980 this.map.set(key, new RouterEntryPointImpl(sf.fileName, moduleName));
31981 }
31982 return this.map.get(key);
31983 }
31984 }
31985 function entryPointKeyFor(filePath, moduleName) {
31986 // Drop the extension to be compatible with how cli calls `listLazyRoutes(entryRoute)`.
31987 return `${filePath.replace(/\.tsx?$/i, '')}#${moduleName}`;
31988 }
31989
31990 /**
31991 * @license
31992 * Copyright Google LLC All Rights Reserved.
31993 *
31994 * Use of this source code is governed by an MIT-style license that can be
31995 * found in the LICENSE file at https://angular.io/license
31996 */
31997 const ROUTES_MARKER = '__ngRoutesMarker__';
31998 function scanForCandidateTransitiveModules(expr, evaluator) {
31999 if (expr === null) {
32000 return [];
32001 }
32002 const candidateModuleKeys = [];
32003 const entries = evaluator.evaluate(expr);
32004 function recursivelyAddModules(entry) {
32005 if (Array.isArray(entry)) {
32006 for (const e of entry) {
32007 recursivelyAddModules(e);
32008 }
32009 }
32010 else if (entry instanceof Map) {
32011 if (entry.has('ngModule')) {
32012 recursivelyAddModules(entry.get('ngModule'));
32013 }
32014 }
32015 else if ((entry instanceof Reference$1) && hasIdentifier(entry.node)) {
32016 const filePath = entry.node.getSourceFile().fileName;
32017 const moduleName = entry.node.name.text;
32018 candidateModuleKeys.push(entryPointKeyFor(filePath, moduleName));
32019 }
32020 }
32021 recursivelyAddModules(entries);
32022 return candidateModuleKeys;
32023 }
32024 function scanForRouteEntryPoints(ngModule, moduleName, data, entryPointManager, evaluator) {
32025 const loadChildrenIdentifiers = [];
32026 const from = entryPointManager.fromNgModule(ngModule, moduleName);
32027 if (data.providers !== null) {
32028 loadChildrenIdentifiers.push(...scanForProviders(data.providers, evaluator));
32029 }
32030 if (data.imports !== null) {
32031 loadChildrenIdentifiers.push(...scanForRouterModuleUsage(data.imports, evaluator));
32032 }
32033 if (data.exports !== null) {
32034 loadChildrenIdentifiers.push(...scanForRouterModuleUsage(data.exports, evaluator));
32035 }
32036 const routes = [];
32037 for (const loadChildren of loadChildrenIdentifiers) {
32038 const resolvedTo = entryPointManager.resolveLoadChildrenIdentifier(loadChildren, ngModule);
32039 if (resolvedTo !== null) {
32040 routes.push({
32041 loadChildren,
32042 from,
32043 resolvedTo,
32044 });
32045 }
32046 }
32047 return routes;
32048 }
32049 function scanForProviders(expr, evaluator) {
32050 const loadChildrenIdentifiers = [];
32051 const providers = evaluator.evaluate(expr);
32052 function recursivelyAddProviders(provider) {
32053 if (Array.isArray(provider)) {
32054 for (const entry of provider) {
32055 recursivelyAddProviders(entry);
32056 }
32057 }
32058 else if (provider instanceof Map) {
32059 if (provider.has('provide') && provider.has('useValue')) {
32060 const provide = provider.get('provide');
32061 const useValue = provider.get('useValue');
32062 if (isRouteToken(provide) && Array.isArray(useValue)) {
32063 loadChildrenIdentifiers.push(...scanForLazyRoutes(useValue));
32064 }
32065 }
32066 }
32067 }
32068 recursivelyAddProviders(providers);
32069 return loadChildrenIdentifiers;
32070 }
32071 function scanForRouterModuleUsage(expr, evaluator) {
32072 const loadChildrenIdentifiers = [];
32073 const imports = evaluator.evaluate(expr, routerModuleFFR);
32074 function recursivelyAddRoutes(imp) {
32075 if (Array.isArray(imp)) {
32076 for (const entry of imp) {
32077 recursivelyAddRoutes(entry);
32078 }
32079 }
32080 else if (imp instanceof Map) {
32081 if (imp.has(ROUTES_MARKER) && imp.has('routes')) {
32082 const routes = imp.get('routes');
32083 if (Array.isArray(routes)) {
32084 loadChildrenIdentifiers.push(...scanForLazyRoutes(routes));
32085 }
32086 }
32087 }
32088 }
32089 recursivelyAddRoutes(imports);
32090 return loadChildrenIdentifiers;
32091 }
32092 function scanForLazyRoutes(routes) {
32093 const loadChildrenIdentifiers = [];
32094 function recursivelyScanRoutes(routes) {
32095 for (let route of routes) {
32096 if (!(route instanceof Map)) {
32097 continue;
32098 }
32099 if (route.has('loadChildren')) {
32100 const loadChildren = route.get('loadChildren');
32101 if (typeof loadChildren === 'string') {
32102 loadChildrenIdentifiers.push(loadChildren);
32103 }
32104 }
32105 else if (route.has('children')) {
32106 const children = route.get('children');
32107 if (Array.isArray(children)) {
32108 recursivelyScanRoutes(children);
32109 }
32110 }
32111 }
32112 }
32113 recursivelyScanRoutes(routes);
32114 return loadChildrenIdentifiers;
32115 }
32116 /**
32117 * A foreign function resolver that converts `RouterModule.forRoot/forChild(X)` to a special object
32118 * of the form `{__ngRoutesMarker__: true, routes: X}`.
32119 *
32120 * These objects are then recognizable inside the larger set of imports/exports.
32121 */
32122 const routerModuleFFR = function routerModuleFFR(ref, args) {
32123 if (!isMethodNodeReference(ref) || !ts$1.isClassDeclaration(ref.node.parent)) {
32124 return null;
32125 }
32126 else if (ref.bestGuessOwningModule === null ||
32127 ref.bestGuessOwningModule.specifier !== '@angular/router') {
32128 return null;
32129 }
32130 else if (ref.node.parent.name === undefined || ref.node.parent.name.text !== 'RouterModule') {
32131 return null;
32132 }
32133 else if (!ts$1.isIdentifier(ref.node.name) ||
32134 (ref.node.name.text !== 'forRoot' && ref.node.name.text !== 'forChild')) {
32135 return null;
32136 }
32137 const routes = args[0];
32138 return ts$1.createObjectLiteral([
32139 ts$1.createPropertyAssignment(ROUTES_MARKER, ts$1.createTrue()),
32140 ts$1.createPropertyAssignment('routes', routes),
32141 ]);
32142 };
32143 function hasIdentifier(node) {
32144 const node_ = node;
32145 return (node_.name !== undefined) && ts$1.isIdentifier(node_.name);
32146 }
32147 function isMethodNodeReference(ref) {
32148 return ts$1.isMethodDeclaration(ref.node);
32149 }
32150 function isRouteToken(ref) {
32151 return ref instanceof Reference$1 && ref.bestGuessOwningModule !== null &&
32152 ref.bestGuessOwningModule.specifier === '@angular/router' && ref.debugName === 'ROUTES';
32153 }
32154
32155 /**
32156 * @license
32157 * Copyright Google LLC All Rights Reserved.
32158 *
32159 * Use of this source code is governed by an MIT-style license that can be
32160 * found in the LICENSE file at https://angular.io/license
32161 */
32162 class NgModuleRouteAnalyzer {
32163 constructor(moduleResolver, evaluator) {
32164 this.evaluator = evaluator;
32165 this.modules = new Map();
32166 this.entryPointManager = new RouterEntryPointManager(moduleResolver);
32167 }
32168 add(sourceFile, moduleName, imports, exports, providers) {
32169 const key = entryPointKeyFor(sourceFile.fileName, moduleName);
32170 if (this.modules.has(key)) {
32171 throw new Error(`Double route analyzing for '${key}'.`);
32172 }
32173 this.modules.set(key, {
32174 sourceFile,
32175 moduleName,
32176 imports,
32177 exports,
32178 providers,
32179 });
32180 }
32181 listLazyRoutes(entryModuleKey) {
32182 if ((entryModuleKey !== undefined) && !this.modules.has(entryModuleKey)) {
32183 throw new Error(`Failed to list lazy routes: Unknown module '${entryModuleKey}'.`);
32184 }
32185 const routes = [];
32186 const scannedModuleKeys = new Set();
32187 const pendingModuleKeys = entryModuleKey ? [entryModuleKey] : Array.from(this.modules.keys());
32188 // When listing lazy routes for a specific entry module, we need to recursively extract
32189 // "transitive" routes from imported/exported modules. This is not necessary when listing all
32190 // lazy routes, because all analyzed modules will be scanned anyway.
32191 const scanRecursively = entryModuleKey !== undefined;
32192 while (pendingModuleKeys.length > 0) {
32193 const key = pendingModuleKeys.pop();
32194 if (scannedModuleKeys.has(key)) {
32195 continue;
32196 }
32197 else {
32198 scannedModuleKeys.add(key);
32199 }
32200 const data = this.modules.get(key);
32201 const entryPoints = scanForRouteEntryPoints(data.sourceFile, data.moduleName, data, this.entryPointManager, this.evaluator);
32202 routes.push(...entryPoints.map(entryPoint => ({
32203 route: entryPoint.loadChildren,
32204 module: entryPoint.from,
32205 referencedModule: entryPoint.resolvedTo,
32206 })));
32207 if (scanRecursively) {
32208 pendingModuleKeys.push(...[
32209 // Scan the retrieved lazy route entry points.
32210 ...entryPoints.map(({ resolvedTo }) => entryPointKeyFor(resolvedTo.filePath, resolvedTo.moduleName)),
32211 // Scan the current module's imported modules.
32212 ...scanForCandidateTransitiveModules(data.imports, this.evaluator),
32213 // Scan the current module's exported modules.
32214 ...scanForCandidateTransitiveModules(data.exports, this.evaluator),
32215 ].filter(key => this.modules.has(key)));
32216 }
32217 }
32218 return routes;
32219 }
32220 }
32221
32222 /**
32223 * @license
32224 * Copyright Google LLC All Rights Reserved.
32225 *
32226 * Use of this source code is governed by an MIT-style license that can be
32227 * found in the LICENSE file at https://angular.io/license
32228 */
32229 /**
32230 * Reads Angular metadata from classes declared in .d.ts files and computes an `ExportScope`.
32231 *
32232 * Given an NgModule declared in a .d.ts file, this resolver can produce a transitive `ExportScope`
32233 * of all of the directives/pipes it exports. It does this by reading metadata off of Ivy static
32234 * fields on directives, components, pipes, and NgModules.
32235 */
32236 class MetadataDtsModuleScopeResolver {
32237 /**
32238 * @param dtsMetaReader a `MetadataReader` which can read metadata from `.d.ts` files.
32239 */
32240 constructor(dtsMetaReader, aliasingHost) {
32241 this.dtsMetaReader = dtsMetaReader;
32242 this.aliasingHost = aliasingHost;
32243 /**
32244 * Cache which holds fully resolved scopes for NgModule classes from .d.ts files.
32245 */
32246 this.cache = new Map();
32247 }
32248 /**
32249 * Resolve a `Reference`'d NgModule from a .d.ts file and produce a transitive `ExportScope`
32250 * listing the directives and pipes which that NgModule exports to others.
32251 *
32252 * This operation relies on a `Reference` instead of a direct TypeScrpt node as the `Reference`s
32253 * produced depend on how the original NgModule was imported.
32254 */
32255 resolve(ref) {
32256 const clazz = ref.node;
32257 const sourceFile = clazz.getSourceFile();
32258 if (!sourceFile.isDeclarationFile) {
32259 throw new Error(`Debug error: DtsModuleScopeResolver.read(${ref.debugName} from ${sourceFile.fileName}), but not a .d.ts file`);
32260 }
32261 if (this.cache.has(clazz)) {
32262 return this.cache.get(clazz);
32263 }
32264 // Build up the export scope - those directives and pipes made visible by this module.
32265 const directives = [];
32266 const pipes = [];
32267 const ngModules = new Set([clazz]);
32268 const meta = this.dtsMetaReader.getNgModuleMetadata(ref);
32269 if (meta === null) {
32270 this.cache.set(clazz, null);
32271 return null;
32272 }
32273 const declarations = new Set();
32274 for (const declRef of meta.declarations) {
32275 declarations.add(declRef.node);
32276 }
32277 // Only the 'exports' field of the NgModule's metadata is important. Imports and declarations
32278 // don't affect the export scope.
32279 for (const exportRef of meta.exports) {
32280 // Attempt to process the export as a directive.
32281 const directive = this.dtsMetaReader.getDirectiveMetadata(exportRef);
32282 if (directive !== null) {
32283 const isReExport = !declarations.has(exportRef.node);
32284 directives.push(this.maybeAlias(directive, sourceFile, isReExport));
32285 continue;
32286 }
32287 // Attempt to process the export as a pipe.
32288 const pipe = this.dtsMetaReader.getPipeMetadata(exportRef);
32289 if (pipe !== null) {
32290 const isReExport = !declarations.has(exportRef.node);
32291 pipes.push(this.maybeAlias(pipe, sourceFile, isReExport));
32292 continue;
32293 }
32294 // Attempt to process the export as a module.
32295 const exportScope = this.resolve(exportRef);
32296 if (exportScope !== null) {
32297 // It is a module. Add exported directives and pipes to the current scope. This might
32298 // involve rewriting the `Reference`s to those types to have an alias expression if one is
32299 // required.
32300 if (this.aliasingHost === null) {
32301 // Fast path when aliases aren't required.
32302 directives.push(...exportScope.exported.directives);
32303 pipes.push(...exportScope.exported.pipes);
32304 }
32305 else {
32306 // It's necessary to rewrite the `Reference`s to add alias expressions. This way, imports
32307 // generated to these directives and pipes will use a shallow import to `sourceFile`
32308 // instead of a deep import directly to the directive or pipe class.
32309 //
32310 // One important check here is whether the directive/pipe is declared in the same
32311 // source file as the re-exporting NgModule. This can happen if both a directive, its
32312 // NgModule, and the re-exporting NgModule are all in the same file. In this case,
32313 // no import alias is needed as it would go to the same file anyway.
32314 for (const directive of exportScope.exported.directives) {
32315 directives.push(this.maybeAlias(directive, sourceFile, /* isReExport */ true));
32316 }
32317 for (const pipe of exportScope.exported.pipes) {
32318 pipes.push(this.maybeAlias(pipe, sourceFile, /* isReExport */ true));
32319 }
32320 for (const ngModule of exportScope.exported.ngModules) {
32321 ngModules.add(ngModule);
32322 }
32323 }
32324 }
32325 continue;
32326 // The export was not a directive, a pipe, or a module. This is an error.
32327 // TODO(alxhub): produce a ts.Diagnostic
32328 }
32329 const exportScope = {
32330 exported: {
32331 directives,
32332 pipes,
32333 ngModules: Array.from(ngModules),
32334 isPoisoned: false,
32335 },
32336 };
32337 this.cache.set(clazz, exportScope);
32338 return exportScope;
32339 }
32340 maybeAlias(dirOrPipe, maybeAliasFrom, isReExport) {
32341 const ref = dirOrPipe.ref;
32342 if (this.aliasingHost === null || ref.node.getSourceFile() === maybeAliasFrom) {
32343 return dirOrPipe;
32344 }
32345 const alias = this.aliasingHost.getAliasIn(ref.node, maybeAliasFrom, isReExport);
32346 if (alias === null) {
32347 return dirOrPipe;
32348 }
32349 return Object.assign(Object.assign({}, dirOrPipe), { ref: ref.cloneWithAlias(alias) });
32350 }
32351 }
32352
32353 /**
32354 * @license
32355 * Copyright Google LLC All Rights Reserved.
32356 *
32357 * Use of this source code is governed by an MIT-style license that can be
32358 * found in the LICENSE file at https://angular.io/license
32359 */
32360 /**
32361 * A registry which collects information about NgModules, Directives, Components, and Pipes which
32362 * are local (declared in the ts.Program being compiled), and can produce `LocalModuleScope`s
32363 * which summarize the compilation scope of a component.
32364 *
32365 * This class implements the logic of NgModule declarations, imports, and exports and can produce,
32366 * for a given component, the set of directives and pipes which are "visible" in that component's
32367 * template.
32368 *
32369 * The `LocalModuleScopeRegistry` has two "modes" of operation. During analysis, data for each
32370 * individual NgModule, Directive, Component, and Pipe is added to the registry. No attempt is made
32371 * to traverse or validate the NgModule graph (imports, exports, etc). After analysis, one of
32372 * `getScopeOfModule` or `getScopeForComponent` can be called, which traverses the NgModule graph
32373 * and applies the NgModule logic to generate a `LocalModuleScope`, the full scope for the given
32374 * module or component.
32375 *
32376 * The `LocalModuleScopeRegistry` is also capable of producing `ts.Diagnostic` errors when Angular
32377 * semantics are violated.
32378 */
32379 class LocalModuleScopeRegistry {
32380 constructor(localReader, dependencyScopeReader, refEmitter, aliasingHost) {
32381 this.localReader = localReader;
32382 this.dependencyScopeReader = dependencyScopeReader;
32383 this.refEmitter = refEmitter;
32384 this.aliasingHost = aliasingHost;
32385 /**
32386 * Tracks whether the registry has been asked to produce scopes for a module or component. Once
32387 * this is true, the registry cannot accept registrations of new directives/pipes/modules as it
32388 * would invalidate the cached scope data.
32389 */
32390 this.sealed = false;
32391 /**
32392 * A map of components from the current compilation unit to the NgModule which declared them.
32393 *
32394 * As components and directives are not distinguished at the NgModule level, this map may also
32395 * contain directives. This doesn't cause any problems but isn't useful as there is no concept of
32396 * a directive's compilation scope.
32397 */
32398 this.declarationToModule = new Map();
32399 /**
32400 * This maps from the directive/pipe class to a map of data for each NgModule that declares the
32401 * directive/pipe. This data is needed to produce an error for the given class.
32402 */
32403 this.duplicateDeclarations = new Map();
32404 this.moduleToRef = new Map();
32405 /**
32406 * A cache of calculated `LocalModuleScope`s for each NgModule declared in the current program.
32407
32408 */
32409 this.cache = new Map();
32410 /**
32411 * Tracks the `RemoteScope` for components requiring "remote scoping".
32412 *
32413 * Remote scoping is when the set of directives which apply to a given component is set in the
32414 * NgModule's file instead of directly on the component def (which is sometimes needed to get
32415 * around cyclic import issues). This is not used in calculation of `LocalModuleScope`s, but is
32416 * tracked here for convenience.
32417 */
32418 this.remoteScoping = new Map();
32419 /**
32420 * Tracks errors accumulated in the processing of scopes for each module declaration.
32421 */
32422 this.scopeErrors = new Map();
32423 /**
32424 * Tracks which NgModules have directives/pipes that are declared in more than one module.
32425 */
32426 this.modulesWithStructuralErrors = new Set();
32427 }
32428 /**
32429 * Add an NgModule's data to the registry.
32430 */
32431 registerNgModuleMetadata(data) {
32432 this.assertCollecting();
32433 const ngModule = data.ref.node;
32434 this.moduleToRef.set(data.ref.node, data.ref);
32435 // Iterate over the module's declarations, and add them to declarationToModule. If duplicates
32436 // are found, they're instead tracked in duplicateDeclarations.
32437 for (const decl of data.declarations) {
32438 this.registerDeclarationOfModule(ngModule, decl, data.rawDeclarations);
32439 }
32440 }
32441 registerDirectiveMetadata(directive) { }
32442 registerPipeMetadata(pipe) { }
32443 getScopeForComponent(clazz) {
32444 const scope = !this.declarationToModule.has(clazz) ?
32445 null :
32446 this.getScopeOfModule(this.declarationToModule.get(clazz).ngModule);
32447 return scope;
32448 }
32449 /**
32450 * If `node` is declared in more than one NgModule (duplicate declaration), then get the
32451 * `DeclarationData` for each offending declaration.
32452 *
32453 * Ordinarily a class is only declared in one NgModule, in which case this function returns
32454 * `null`.
32455 */
32456 getDuplicateDeclarations(node) {
32457 if (!this.duplicateDeclarations.has(node)) {
32458 return null;
32459 }
32460 return Array.from(this.duplicateDeclarations.get(node).values());
32461 }
32462 /**
32463 * Collects registered data for a module and its directives/pipes and convert it into a full
32464 * `LocalModuleScope`.
32465 *
32466 * This method implements the logic of NgModule imports and exports. It returns the
32467 * `LocalModuleScope` for the given NgModule if one can be produced, `null` if no scope was ever
32468 * defined, or the string `'error'` if the scope contained errors.
32469 */
32470 getScopeOfModule(clazz) {
32471 return this.moduleToRef.has(clazz) ?
32472 this.getScopeOfModuleReference(this.moduleToRef.get(clazz)) :
32473 null;
32474 }
32475 /**
32476 * Retrieves any `ts.Diagnostic`s produced during the calculation of the `LocalModuleScope` for
32477 * the given NgModule, or `null` if no errors were present.
32478 */
32479 getDiagnosticsOfModule(clazz) {
32480 // Required to ensure the errors are populated for the given class. If it has been processed
32481 // before, this will be a no-op due to the scope cache.
32482 this.getScopeOfModule(clazz);
32483 if (this.scopeErrors.has(clazz)) {
32484 return this.scopeErrors.get(clazz);
32485 }
32486 else {
32487 return null;
32488 }
32489 }
32490 /**
32491 * Returns a collection of the compilation scope for each registered declaration.
32492 */
32493 getCompilationScopes() {
32494 const scopes = [];
32495 this.declarationToModule.forEach((declData, declaration) => {
32496 const scope = this.getScopeOfModule(declData.ngModule);
32497 if (scope !== null) {
32498 scopes.push(Object.assign({ declaration, ngModule: declData.ngModule }, scope.compilation));
32499 }
32500 });
32501 return scopes;
32502 }
32503 registerDeclarationOfModule(ngModule, decl, rawDeclarations) {
32504 const declData = {
32505 ngModule,
32506 ref: decl,
32507 rawDeclarations,
32508 };
32509 // First, check for duplicate declarations of the same directive/pipe.
32510 if (this.duplicateDeclarations.has(decl.node)) {
32511 // This directive/pipe has already been identified as being duplicated. Add this module to the
32512 // map of modules for which a duplicate declaration exists.
32513 this.duplicateDeclarations.get(decl.node).set(ngModule, declData);
32514 }
32515 else if (this.declarationToModule.has(decl.node) &&
32516 this.declarationToModule.get(decl.node).ngModule !== ngModule) {
32517 // This directive/pipe is already registered as declared in another module. Mark it as a
32518 // duplicate instead.
32519 const duplicateDeclMap = new Map();
32520 const firstDeclData = this.declarationToModule.get(decl.node);
32521 // Mark both modules as having duplicate declarations.
32522 this.modulesWithStructuralErrors.add(firstDeclData.ngModule);
32523 this.modulesWithStructuralErrors.add(ngModule);
32524 // Being detected as a duplicate means there are two NgModules (for now) which declare this
32525 // directive/pipe. Add both of them to the duplicate tracking map.
32526 duplicateDeclMap.set(firstDeclData.ngModule, firstDeclData);
32527 duplicateDeclMap.set(ngModule, declData);
32528 this.duplicateDeclarations.set(decl.node, duplicateDeclMap);
32529 // Remove the directive/pipe from `declarationToModule` as it's a duplicate declaration, and
32530 // therefore not valid.
32531 this.declarationToModule.delete(decl.node);
32532 }
32533 else {
32534 // This is the first declaration of this directive/pipe, so map it.
32535 this.declarationToModule.set(decl.node, declData);
32536 }
32537 }
32538 /**
32539 * Implementation of `getScopeOfModule` which accepts a reference to a class.
32540 */
32541 getScopeOfModuleReference(ref) {
32542 if (this.cache.has(ref.node)) {
32543 return this.cache.get(ref.node);
32544 }
32545 // Seal the registry to protect the integrity of the `LocalModuleScope` cache.
32546 this.sealed = true;
32547 // `ref` should be an NgModule previously added to the registry. If not, a scope for it
32548 // cannot be produced.
32549 const ngModule = this.localReader.getNgModuleMetadata(ref);
32550 if (ngModule === null) {
32551 this.cache.set(ref.node, null);
32552 return null;
32553 }
32554 // Modules which contributed to the compilation scope of this module.
32555 const compilationModules = new Set([ngModule.ref.node]);
32556 // Modules which contributed to the export scope of this module.
32557 const exportedModules = new Set([ngModule.ref.node]);
32558 // Errors produced during computation of the scope are recorded here. At the end, if this array
32559 // isn't empty then `undefined` will be cached and returned to indicate this scope is invalid.
32560 const diagnostics = [];
32561 // At this point, the goal is to produce two distinct transitive sets:
32562 // - the directives and pipes which are visible to components declared in the NgModule.
32563 // - the directives and pipes which are exported to any NgModules which import this one.
32564 // Directives and pipes in the compilation scope.
32565 const compilationDirectives = new Map();
32566 const compilationPipes = new Map();
32567 const declared = new Set();
32568 // Directives and pipes exported to any importing NgModules.
32569 const exportDirectives = new Map();
32570 const exportPipes = new Map();
32571 // The algorithm is as follows:
32572 // 1) Add all of the directives/pipes from each NgModule imported into the current one to the
32573 // compilation scope.
32574 // 2) Add directives/pipes declared in the NgModule to the compilation scope. At this point, the
32575 // compilation scope is complete.
32576 // 3) For each entry in the NgModule's exports:
32577 // a) Attempt to resolve it as an NgModule with its own exported directives/pipes. If it is
32578 // one, add them to the export scope of this NgModule.
32579 // b) Otherwise, it should be a class in the compilation scope of this NgModule. If it is,
32580 // add it to the export scope.
32581 // c) If it's neither an NgModule nor a directive/pipe in the compilation scope, then this
32582 // is an error.
32583 //
32584 let isPoisoned = false;
32585 if (this.modulesWithStructuralErrors.has(ngModule.ref.node)) {
32586 // If the module contains declarations that are duplicates, then it's considered poisoned.
32587 isPoisoned = true;
32588 }
32589 // 1) process imports.
32590 for (const decl of ngModule.imports) {
32591 const importScope = this.getExportedScope(decl, diagnostics, ref.node, 'import');
32592 if (importScope === null) {
32593 // An import wasn't an NgModule, so record an error.
32594 diagnostics.push(invalidRef(ref.node, decl, 'import'));
32595 isPoisoned = true;
32596 continue;
32597 }
32598 else if (importScope === 'invalid' || importScope.exported.isPoisoned) {
32599 // An import was an NgModule but contained errors of its own. Record this as an error too,
32600 // because this scope is always going to be incorrect if one of its imports could not be
32601 // read.
32602 diagnostics.push(invalidTransitiveNgModuleRef(ref.node, decl, 'import'));
32603 isPoisoned = true;
32604 if (importScope === 'invalid') {
32605 continue;
32606 }
32607 }
32608 for (const directive of importScope.exported.directives) {
32609 compilationDirectives.set(directive.ref.node, directive);
32610 }
32611 for (const pipe of importScope.exported.pipes) {
32612 compilationPipes.set(pipe.ref.node, pipe);
32613 }
32614 for (const importedModule of importScope.exported.ngModules) {
32615 compilationModules.add(importedModule);
32616 }
32617 }
32618 // 2) add declarations.
32619 for (const decl of ngModule.declarations) {
32620 const directive = this.localReader.getDirectiveMetadata(decl);
32621 const pipe = this.localReader.getPipeMetadata(decl);
32622 if (directive !== null) {
32623 compilationDirectives.set(decl.node, Object.assign(Object.assign({}, directive), { ref: decl }));
32624 if (directive.isPoisoned) {
32625 isPoisoned = true;
32626 }
32627 }
32628 else if (pipe !== null) {
32629 compilationPipes.set(decl.node, Object.assign(Object.assign({}, pipe), { ref: decl }));
32630 }
32631 else {
32632 const errorNode = decl.getOriginForDiagnostics(ngModule.rawDeclarations);
32633 diagnostics.push(makeDiagnostic(ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, `The class '${decl.node.name.text}' is listed in the declarations ` +
32634 `of the NgModule '${ngModule.ref.node.name
32635 .text}', but is not a directive, a component, or a pipe. ` +
32636 `Either remove it from the NgModule's declarations, or add an appropriate Angular decorator.`, [makeRelatedInformation(decl.node.name, `'${decl.node.name.text}' is declared here.`)]));
32637 isPoisoned = true;
32638 continue;
32639 }
32640 declared.add(decl.node);
32641 }
32642 // 3) process exports.
32643 // Exports can contain modules, components, or directives. They're processed differently.
32644 // Modules are straightforward. Directives and pipes from exported modules are added to the
32645 // export maps. Directives/pipes are different - they might be exports of declared types or
32646 // imported types.
32647 for (const decl of ngModule.exports) {
32648 // Attempt to resolve decl as an NgModule.
32649 const exportScope = this.getExportedScope(decl, diagnostics, ref.node, 'export');
32650 if (exportScope === 'invalid' || (exportScope !== null && exportScope.exported.isPoisoned)) {
32651 // An export was an NgModule but contained errors of its own. Record this as an error too,
32652 // because this scope is always going to be incorrect if one of its exports could not be
32653 // read.
32654 diagnostics.push(invalidTransitiveNgModuleRef(ref.node, decl, 'export'));
32655 isPoisoned = true;
32656 if (exportScope === 'invalid') {
32657 continue;
32658 }
32659 }
32660 else if (exportScope !== null) {
32661 // decl is an NgModule.
32662 for (const directive of exportScope.exported.directives) {
32663 exportDirectives.set(directive.ref.node, directive);
32664 }
32665 for (const pipe of exportScope.exported.pipes) {
32666 exportPipes.set(pipe.ref.node, pipe);
32667 }
32668 for (const exportedModule of exportScope.exported.ngModules) {
32669 exportedModules.add(exportedModule);
32670 }
32671 }
32672 else if (compilationDirectives.has(decl.node)) {
32673 // decl is a directive or component in the compilation scope of this NgModule.
32674 const directive = compilationDirectives.get(decl.node);
32675 exportDirectives.set(decl.node, directive);
32676 }
32677 else if (compilationPipes.has(decl.node)) {
32678 // decl is a pipe in the compilation scope of this NgModule.
32679 const pipe = compilationPipes.get(decl.node);
32680 exportPipes.set(decl.node, pipe);
32681 }
32682 else {
32683 // decl is an unknown export.
32684 if (this.localReader.getDirectiveMetadata(decl) !== null ||
32685 this.localReader.getPipeMetadata(decl) !== null) {
32686 diagnostics.push(invalidReexport(ref.node, decl));
32687 }
32688 else {
32689 diagnostics.push(invalidRef(ref.node, decl, 'export'));
32690 }
32691 isPoisoned = true;
32692 continue;
32693 }
32694 }
32695 const exported = {
32696 directives: Array.from(exportDirectives.values()),
32697 pipes: Array.from(exportPipes.values()),
32698 ngModules: Array.from(exportedModules),
32699 isPoisoned,
32700 };
32701 const reexports = this.getReexports(ngModule, ref, declared, exported, diagnostics);
32702 // Finally, produce the `LocalModuleScope` with both the compilation and export scopes.
32703 const scope = {
32704 ngModule: ngModule.ref.node,
32705 compilation: {
32706 directives: Array.from(compilationDirectives.values()),
32707 pipes: Array.from(compilationPipes.values()),
32708 ngModules: Array.from(compilationModules),
32709 isPoisoned,
32710 },
32711 exported,
32712 reexports,
32713 schemas: ngModule.schemas,
32714 };
32715 // Check if this scope had any errors during production.
32716 if (diagnostics.length > 0) {
32717 // Save the errors for retrieval.
32718 this.scopeErrors.set(ref.node, diagnostics);
32719 // Mark this module as being tainted.
32720 this.modulesWithStructuralErrors.add(ref.node);
32721 }
32722 this.cache.set(ref.node, scope);
32723 return scope;
32724 }
32725 /**
32726 * Check whether a component requires remote scoping.
32727 */
32728 getRemoteScope(node) {
32729 return this.remoteScoping.has(node) ? this.remoteScoping.get(node) : null;
32730 }
32731 /**
32732 * Set a component as requiring remote scoping, with the given directives and pipes to be
32733 * registered remotely.
32734 */
32735 setComponentRemoteScope(node, directives, pipes) {
32736 this.remoteScoping.set(node, { directives, pipes });
32737 }
32738 /**
32739 * Look up the `ExportScope` of a given `Reference` to an NgModule.
32740 *
32741 * The NgModule in question may be declared locally in the current ts.Program, or it may be
32742 * declared in a .d.ts file.
32743 *
32744 * @returns `null` if no scope could be found, or `'invalid'` if the `Reference` is not a valid
32745 * NgModule.
32746 *
32747 * May also contribute diagnostics of its own by adding to the given `diagnostics`
32748 * array parameter.
32749 */
32750 getExportedScope(ref, diagnostics, ownerForErrors, type) {
32751 if (ref.node.getSourceFile().isDeclarationFile) {
32752 // The NgModule is declared in a .d.ts file. Resolve it with the `DependencyScopeReader`.
32753 if (!ts$1.isClassDeclaration(ref.node)) {
32754 // The NgModule is in a .d.ts file but is not declared as a ts.ClassDeclaration. This is an
32755 // error in the .d.ts metadata.
32756 const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT :
32757 ErrorCode.NGMODULE_INVALID_EXPORT;
32758 diagnostics.push(makeDiagnostic(code, identifierOfNode(ref.node) || ref.node, `Appears in the NgModule.${type}s of ${nodeNameForError(ownerForErrors)}, but could not be resolved to an NgModule`));
32759 return 'invalid';
32760 }
32761 return this.dependencyScopeReader.resolve(ref);
32762 }
32763 else {
32764 // The NgModule is declared locally in the current program. Resolve it from the registry.
32765 return this.getScopeOfModuleReference(ref);
32766 }
32767 }
32768 getReexports(ngModule, ref, declared, exported, diagnostics) {
32769 let reexports = null;
32770 const sourceFile = ref.node.getSourceFile();
32771 if (this.aliasingHost === null) {
32772 return null;
32773 }
32774 reexports = [];
32775 // Track re-exports by symbol name, to produce diagnostics if two alias re-exports would share
32776 // the same name.
32777 const reexportMap = new Map();
32778 // Alias ngModuleRef added for readability below.
32779 const ngModuleRef = ref;
32780 const addReexport = (exportRef) => {
32781 if (exportRef.node.getSourceFile() === sourceFile) {
32782 return;
32783 }
32784 const isReExport = !declared.has(exportRef.node);
32785 const exportName = this.aliasingHost.maybeAliasSymbolAs(exportRef, sourceFile, ngModule.ref.node.name.text, isReExport);
32786 if (exportName === null) {
32787 return;
32788 }
32789 if (!reexportMap.has(exportName)) {
32790 if (exportRef.alias && exportRef.alias instanceof ExternalExpr) {
32791 reexports.push({
32792 fromModule: exportRef.alias.value.moduleName,
32793 symbolName: exportRef.alias.value.name,
32794 asAlias: exportName,
32795 });
32796 }
32797 else {
32798 const expr = this.refEmitter.emit(exportRef.cloneWithNoIdentifiers(), sourceFile);
32799 if (!(expr instanceof ExternalExpr) || expr.value.moduleName === null ||
32800 expr.value.name === null) {
32801 throw new Error('Expected ExternalExpr');
32802 }
32803 reexports.push({
32804 fromModule: expr.value.moduleName,
32805 symbolName: expr.value.name,
32806 asAlias: exportName,
32807 });
32808 }
32809 reexportMap.set(exportName, exportRef);
32810 }
32811 else {
32812 // Another re-export already used this name. Produce a diagnostic.
32813 const prevRef = reexportMap.get(exportName);
32814 diagnostics.push(reexportCollision(ngModuleRef.node, prevRef, exportRef));
32815 }
32816 };
32817 for (const { ref } of exported.directives) {
32818 addReexport(ref);
32819 }
32820 for (const { ref } of exported.pipes) {
32821 addReexport(ref);
32822 }
32823 return reexports;
32824 }
32825 assertCollecting() {
32826 if (this.sealed) {
32827 throw new Error(`Assertion: LocalModuleScopeRegistry is not COLLECTING`);
32828 }
32829 }
32830 }
32831 /**
32832 * Produce a `ts.Diagnostic` for an invalid import or export from an NgModule.
32833 */
32834 function invalidRef(clazz, decl, type) {
32835 const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT;
32836 const resolveTarget = type === 'import' ? 'NgModule' : 'NgModule, Component, Directive, or Pipe';
32837 let message = `Appears in the NgModule.${type}s of ${nodeNameForError(clazz)}, but could not be resolved to an ${resolveTarget} class.` +
32838 '\n\n';
32839 const library = decl.ownedByModuleGuess !== null ? ` (${decl.ownedByModuleGuess})` : '';
32840 const sf = decl.node.getSourceFile();
32841 // Provide extra context to the error for the user.
32842 if (!sf.isDeclarationFile) {
32843 // This is a file in the user's program.
32844 const annotationType = type === 'import' ? '@NgModule' : 'Angular';
32845 message += `Is it missing an ${annotationType} annotation?`;
32846 }
32847 else if (sf.fileName.indexOf('node_modules') !== -1) {
32848 // This file comes from a third-party library in node_modules.
32849 message +=
32850 `This likely means that the library${library} which declares ${decl.debugName} has not ` +
32851 'been processed correctly by ngcc, or is not compatible with Angular Ivy. Check if a ' +
32852 'newer version of the library is available, and update if so. Also consider checking ' +
32853 'with the library\'s authors to see if the library is expected to be compatible with Ivy.';
32854 }
32855 else {
32856 // This is a monorepo style local dependency. Unfortunately these are too different to really
32857 // offer much more advice than this.
32858 message += `This likely means that the dependency${library} which declares ${decl.debugName} has not been processed correctly by ngcc.`;
32859 }
32860 return makeDiagnostic(code, identifierOfNode(decl.node) || decl.node, message);
32861 }
32862 /**
32863 * Produce a `ts.Diagnostic` for an import or export which itself has errors.
32864 */
32865 function invalidTransitiveNgModuleRef(clazz, decl, type) {
32866 const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT;
32867 return makeDiagnostic(code, identifierOfNode(decl.node) || decl.node, `Appears in the NgModule.${type}s of ${nodeNameForError(clazz)}, but itself has errors`);
32868 }
32869 /**
32870 * Produce a `ts.Diagnostic` for an exported directive or pipe which was not declared or imported
32871 * by the NgModule in question.
32872 */
32873 function invalidReexport(clazz, decl) {
32874 return makeDiagnostic(ErrorCode.NGMODULE_INVALID_REEXPORT, identifierOfNode(decl.node) || decl.node, `Present in the NgModule.exports of ${nodeNameForError(clazz)} but neither declared nor imported`);
32875 }
32876 /**
32877 * Produce a `ts.Diagnostic` for a collision in re-export names between two directives/pipes.
32878 */
32879 function reexportCollision(module, refA, refB) {
32880 const childMessageText = `This directive/pipe is part of the exports of '${module.name.text}' and shares the same name as another exported directive/pipe.`;
32881 return makeDiagnostic(ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION, module.name, `
32882 There was a name collision between two classes named '${refA.node.name.text}', which are both part of the exports of '${module.name.text}'.
32883
32884 Angular generates re-exports of an NgModule's exported directives/pipes from the module's source file in certain cases, using the declared name of the class. If two classes of the same name are exported, this automatic naming does not work.
32885
32886 To fix this problem please re-export one or both classes directly from this file.
32887 `.trim(), [
32888 makeRelatedInformation(refA.node.name, childMessageText),
32889 makeRelatedInformation(refB.node.name, childMessageText),
32890 ]);
32891 }
32892
32893 /**
32894 * @license
32895 * Copyright Google LLC All Rights Reserved.
32896 *
32897 * Use of this source code is governed by an MIT-style license that can be
32898 * found in the LICENSE file at https://angular.io/license
32899 */
32900 /**
32901 * Computes scope information to be used in template type checking.
32902 */
32903 class TypeCheckScopeRegistry {
32904 constructor(scopeReader, metaReader) {
32905 this.scopeReader = scopeReader;
32906 this.metaReader = metaReader;
32907 /**
32908 * Cache of flattened directive metadata. Because flattened metadata is scope-invariant it's
32909 * cached individually, such that all scopes refer to the same flattened metadata.
32910 */
32911 this.flattenedDirectiveMetaCache = new Map();
32912 /**
32913 * Cache of the computed type check scope per NgModule declaration.
32914 */
32915 this.scopeCache = new Map();
32916 }
32917 /**
32918 * Computes the type-check scope information for the component declaration. If the NgModule
32919 * contains an error, then 'error' is returned. If the component is not declared in any NgModule,
32920 * an empty type-check scope is returned.
32921 */
32922 getTypeCheckScope(node) {
32923 const matcher = new SelectorMatcher();
32924 const directives = [];
32925 const pipes = new Map();
32926 const scope = this.scopeReader.getScopeForComponent(node);
32927 if (scope === null) {
32928 return {
32929 matcher,
32930 directives,
32931 pipes,
32932 schemas: [],
32933 isPoisoned: false,
32934 };
32935 }
32936 if (this.scopeCache.has(scope.ngModule)) {
32937 return this.scopeCache.get(scope.ngModule);
32938 }
32939 for (const meta of scope.compilation.directives) {
32940 if (meta.selector !== null) {
32941 const extMeta = this.getTypeCheckDirectiveMetadata(meta.ref);
32942 matcher.addSelectables(CssSelector.parse(meta.selector), extMeta);
32943 directives.push(extMeta);
32944 }
32945 }
32946 for (const { name, ref } of scope.compilation.pipes) {
32947 if (!ts$1.isClassDeclaration(ref.node)) {
32948 throw new Error(`Unexpected non-class declaration ${ts$1.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`);
32949 }
32950 pipes.set(name, ref);
32951 }
32952 const typeCheckScope = {
32953 matcher,
32954 directives,
32955 pipes,
32956 schemas: scope.schemas,
32957 isPoisoned: scope.compilation.isPoisoned || scope.exported.isPoisoned,
32958 };
32959 this.scopeCache.set(scope.ngModule, typeCheckScope);
32960 return typeCheckScope;
32961 }
32962 getTypeCheckDirectiveMetadata(ref) {
32963 const clazz = ref.node;
32964 if (this.flattenedDirectiveMetaCache.has(clazz)) {
32965 return this.flattenedDirectiveMetaCache.get(clazz);
32966 }
32967 const meta = flattenInheritedDirectiveMetadata(this.metaReader, ref);
32968 this.flattenedDirectiveMetaCache.set(clazz, meta);
32969 return meta;
32970 }
32971 }
32972
32973 /**
32974 * @license
32975 * Copyright Google LLC All Rights Reserved.
32976 *
32977 * Use of this source code is governed by an MIT-style license that can be
32978 * found in the LICENSE file at https://angular.io/license
32979 */
32980 /**
32981 * A `Symbol` which is used to patch extension data onto `ts.SourceFile`s.
32982 */
32983 const NgExtension = Symbol('NgExtension');
32984 /**
32985 * Narrows a `ts.SourceFile` if it has an `NgExtension` property.
32986 */
32987 function isExtended(sf) {
32988 return sf[NgExtension] !== undefined;
32989 }
32990 /**
32991 * Check whether `sf` is a shim `ts.SourceFile` (either a per-file shim or a top-level shim).
32992 */
32993 function isShim(sf) {
32994 return isExtended(sf) && (sf[NgExtension].fileShim !== null || sf[NgExtension].isTopLevelShim);
32995 }
32996
32997 /**
32998 * @license
32999 * Copyright Google LLC All Rights Reserved.
33000 *
33001 * Use of this source code is governed by an MIT-style license that can be
33002 * found in the LICENSE file at https://angular.io/license
33003 */
33004 const STRIP_NG_FACTORY = /(.*)NgFactory$/;
33005 function generatedFactoryTransform(factoryMap, importRewriter) {
33006 return (context) => {
33007 return (file) => {
33008 return transformFactorySourceFile(factoryMap, context, importRewriter, file);
33009 };
33010 };
33011 }
33012 function transformFactorySourceFile(factoryMap, context, importRewriter, file) {
33013 // If this is not a generated file, it won't have factory info associated with it.
33014 if (!factoryMap.has(file.fileName)) {
33015 // Don't transform non-generated code.
33016 return file;
33017 }
33018 const { moduleSymbols, sourceFilePath } = factoryMap.get(file.fileName);
33019 // Not every exported factory statement is valid. They were generated before the program was
33020 // analyzed, and before ngtsc knew which symbols were actually NgModules. factoryMap contains
33021 // that knowledge now, so this transform filters the statement list and removes exported factories
33022 // that aren't actually factories.
33023 //
33024 // This could leave the generated factory file empty. To prevent this (it causes issues with
33025 // closure compiler) a 'ɵNonEmptyModule' export was added when the factory shim was created.
33026 // Preserve that export if needed, and remove it otherwise.
33027 //
33028 // Additionally, an import to @angular/core is generated, but the current compilation unit could
33029 // actually be @angular/core, in which case such an import is invalid and should be replaced with
33030 // the proper path to access Ivy symbols in core.
33031 // The filtered set of statements.
33032 const transformedStatements = [];
33033 // The statement identified as the ɵNonEmptyModule export.
33034 let nonEmptyExport = null;
33035 // Extracted identifiers which refer to import statements from @angular/core.
33036 const coreImportIdentifiers = new Set();
33037 // Consider all the statements.
33038 for (const stmt of file.statements) {
33039 // Look for imports to @angular/core.
33040 if (ts$1.isImportDeclaration(stmt) && ts$1.isStringLiteral(stmt.moduleSpecifier) &&
33041 stmt.moduleSpecifier.text === '@angular/core') {
33042 // Update the import path to point to the correct file using the ImportRewriter.
33043 const rewrittenModuleSpecifier = importRewriter.rewriteSpecifier('@angular/core', sourceFilePath);
33044 if (rewrittenModuleSpecifier !== stmt.moduleSpecifier.text) {
33045 transformedStatements.push(ts$1.updateImportDeclaration(stmt, stmt.decorators, stmt.modifiers, stmt.importClause, ts$1.createStringLiteral(rewrittenModuleSpecifier)));
33046 // Record the identifier by which this imported module goes, so references to its symbols
33047 // can be discovered later.
33048 if (stmt.importClause !== undefined && stmt.importClause.namedBindings !== undefined &&
33049 ts$1.isNamespaceImport(stmt.importClause.namedBindings)) {
33050 coreImportIdentifiers.add(stmt.importClause.namedBindings.name.text);
33051 }
33052 }
33053 else {
33054 transformedStatements.push(stmt);
33055 }
33056 }
33057 else if (ts$1.isVariableStatement(stmt) && stmt.declarationList.declarations.length === 1) {
33058 const decl = stmt.declarationList.declarations[0];
33059 // If this is the ɵNonEmptyModule export, then save it for later.
33060 if (ts$1.isIdentifier(decl.name)) {
33061 if (decl.name.text === 'ɵNonEmptyModule') {
33062 nonEmptyExport = stmt;
33063 continue;
33064 }
33065 // Otherwise, check if this export is a factory for a known NgModule, and retain it if so.
33066 const match = STRIP_NG_FACTORY.exec(decl.name.text);
33067 const module = match ? moduleSymbols.get(match[1]) : null;
33068 if (module) {
33069 // If the module can be tree shaken, then the factory should be wrapped in a
33070 // `noSideEffects()` call which tells Closure to treat the expression as pure, allowing
33071 // it to be removed if the result is not used.
33072 //
33073 // `NgModule`s with an `id` property will be lazy loaded. Google-internal lazy loading
33074 // infra relies on a side effect from the `new NgModuleFactory()` call, which registers
33075 // the module globally. Because of this, we **cannot** tree shake any module which has
33076 // an `id` property. Doing so would cause lazy loaded modules to never be registered.
33077 const moduleIsTreeShakable = !module.hasId;
33078 const newStmt = !moduleIsTreeShakable ?
33079 stmt :
33080 updateInitializers(stmt, (init) => init ? wrapInNoSideEffects(init) : undefined);
33081 transformedStatements.push(newStmt);
33082 }
33083 }
33084 else {
33085 // Leave the statement alone, as it can't be understood.
33086 transformedStatements.push(stmt);
33087 }
33088 }
33089 else {
33090 // Include non-variable statements (imports, etc).
33091 transformedStatements.push(stmt);
33092 }
33093 }
33094 // Check whether the empty module export is still needed.
33095 if (!transformedStatements.some(ts$1.isVariableStatement) && nonEmptyExport !== null) {
33096 // If the resulting file has no factories, include an empty export to
33097 // satisfy closure compiler.
33098 transformedStatements.push(nonEmptyExport);
33099 }
33100 file = ts$1.updateSourceFileNode(file, transformedStatements);
33101 // If any imports to @angular/core were detected and rewritten (which happens when compiling
33102 // @angular/core), go through the SourceFile and rewrite references to symbols imported from core.
33103 if (coreImportIdentifiers.size > 0) {
33104 const visit = (node) => {
33105 node = ts$1.visitEachChild(node, child => visit(child), context);
33106 // Look for expressions of the form "i.s" where 'i' is a detected name for an @angular/core
33107 // import that was changed above. Rewrite 's' using the ImportResolver.
33108 if (ts$1.isPropertyAccessExpression(node) && ts$1.isIdentifier(node.expression) &&
33109 coreImportIdentifiers.has(node.expression.text)) {
33110 // This is an import of a symbol from @angular/core. Transform it with the importRewriter.
33111 const rewrittenSymbol = importRewriter.rewriteSymbol(node.name.text, '@angular/core');
33112 if (rewrittenSymbol !== node.name.text) {
33113 const updated = ts$1.updatePropertyAccess(node, node.expression, ts$1.createIdentifier(rewrittenSymbol));
33114 node = updated;
33115 }
33116 }
33117 return node;
33118 };
33119 file = visit(file);
33120 }
33121 return file;
33122 }
33123 /**
33124 * Wraps the given expression in a call to `ɵnoSideEffects()`, which tells
33125 * Closure we don't care about the side effects of this expression and it should
33126 * be treated as "pure". Closure is free to tree shake this expression if its
33127 * result is not used.
33128 *
33129 * Example: Takes `1 + 2` and returns `i0.ɵnoSideEffects(() => 1 + 2)`.
33130 */
33131 function wrapInNoSideEffects(expr) {
33132 const noSideEffects = ts$1.createPropertyAccess(ts$1.createIdentifier('i0'), 'ɵnoSideEffects');
33133 return ts$1.createCall(noSideEffects,
33134 /* typeArguments */ [],
33135 /* arguments */
33136 [
33137 ts$1.createFunctionExpression(
33138 /* modifiers */ [],
33139 /* asteriskToken */ undefined,
33140 /* name */ undefined,
33141 /* typeParameters */ [],
33142 /* parameters */ [],
33143 /* type */ undefined,
33144 /* body */ ts$1.createBlock([
33145 ts$1.createReturn(expr),
33146 ])),
33147 ]);
33148 }
33149 /**
33150 * Clones and updates the initializers for a given statement to use the new
33151 * expression provided. Does not mutate the input statement.
33152 */
33153 function updateInitializers(stmt, update) {
33154 return ts$1.updateVariableStatement(stmt, stmt.modifiers, ts$1.updateVariableDeclarationList(stmt.declarationList, stmt.declarationList.declarations.map((decl) => ts$1.updateVariableDeclaration(decl, decl.name, decl.type, update(decl.initializer)))));
33155 }
33156
33157 /**
33158 * @license
33159 * Copyright Google LLC All Rights Reserved.
33160 *
33161 * Use of this source code is governed by an MIT-style license that can be
33162 * found in the LICENSE file at https://angular.io/license
33163 */
33164 const IVY_SWITCH_PRE_SUFFIX = '__PRE_R3__';
33165 const IVY_SWITCH_POST_SUFFIX = '__POST_R3__';
33166 function ivySwitchTransform(_) {
33167 return flipIvySwitchInFile;
33168 }
33169 function flipIvySwitchInFile(sf) {
33170 // To replace the statements array, it must be copied. This only needs to happen if a statement
33171 // must actually be replaced within the array, so the newStatements array is lazily initialized.
33172 let newStatements = undefined;
33173 // Iterate over the statements in the file.
33174 for (let i = 0; i < sf.statements.length; i++) {
33175 const statement = sf.statements[i];
33176 // Skip over everything that isn't a variable statement.
33177 if (!ts$1.isVariableStatement(statement) || !hasIvySwitches(statement)) {
33178 continue;
33179 }
33180 // This statement needs to be replaced. Check if the newStatements array needs to be lazily
33181 // initialized to a copy of the original statements.
33182 if (newStatements === undefined) {
33183 newStatements = [...sf.statements];
33184 }
33185 // Flip any switches in the VariableStatement. If there were any, a new statement will be
33186 // returned; otherwise the old statement will be.
33187 newStatements[i] = flipIvySwitchesInVariableStatement(statement, sf.statements);
33188 }
33189 // Only update the statements in the SourceFile if any have changed.
33190 if (newStatements !== undefined) {
33191 return ts$1.updateSourceFileNode(sf, newStatements);
33192 }
33193 return sf;
33194 }
33195 /**
33196 * Look for the ts.Identifier of a ts.Declaration with this name.
33197 *
33198 * The real identifier is needed (rather than fabricating one) as TypeScript decides how to
33199 * reference this identifier based on information stored against its node in the AST, which a
33200 * synthetic node would not have. In particular, since the post-switch variable is often exported,
33201 * TypeScript needs to know this so it can write `exports.VAR` instead of just `VAR` when emitting
33202 * code.
33203 *
33204 * Only variable, function, and class declarations are currently searched.
33205 */
33206 function findPostSwitchIdentifier(statements, name) {
33207 for (const stmt of statements) {
33208 if (ts$1.isVariableStatement(stmt)) {
33209 const decl = stmt.declarationList.declarations.find(decl => ts$1.isIdentifier(decl.name) && decl.name.text === name);
33210 if (decl !== undefined) {
33211 return decl.name;
33212 }
33213 }
33214 else if (ts$1.isFunctionDeclaration(stmt) || ts$1.isClassDeclaration(stmt)) {
33215 if (stmt.name !== undefined && ts$1.isIdentifier(stmt.name) && stmt.name.text === name) {
33216 return stmt.name;
33217 }
33218 }
33219 }
33220 return null;
33221 }
33222 /**
33223 * Flip any Ivy switches which are discovered in the given ts.VariableStatement.
33224 */
33225 function flipIvySwitchesInVariableStatement(stmt, statements) {
33226 // Build a new list of variable declarations. Specific declarations that are initialized to a
33227 // pre-switch identifier will be replaced with a declaration initialized to the post-switch
33228 // identifier.
33229 const newDeclarations = [...stmt.declarationList.declarations];
33230 for (let i = 0; i < newDeclarations.length; i++) {
33231 const decl = newDeclarations[i];
33232 // Skip declarations that aren't initialized to an identifier.
33233 if (decl.initializer === undefined || !ts$1.isIdentifier(decl.initializer)) {
33234 continue;
33235 }
33236 // Skip declarations that aren't Ivy switches.
33237 if (!decl.initializer.text.endsWith(IVY_SWITCH_PRE_SUFFIX)) {
33238 continue;
33239 }
33240 // Determine the name of the post-switch variable.
33241 const postSwitchName = decl.initializer.text.replace(IVY_SWITCH_PRE_SUFFIX, IVY_SWITCH_POST_SUFFIX);
33242 // Find the post-switch variable identifier. If one can't be found, it's an error. This is
33243 // reported as a thrown error and not a diagnostic as transformers cannot output diagnostics.
33244 const newIdentifier = findPostSwitchIdentifier(statements, postSwitchName);
33245 if (newIdentifier === null) {
33246 throw new Error(`Unable to find identifier ${postSwitchName} in ${stmt.getSourceFile().fileName} for the Ivy switch.`);
33247 }
33248 newDeclarations[i] = ts$1.updateVariableDeclaration(
33249 /* node */ decl,
33250 /* name */ decl.name,
33251 /* type */ decl.type,
33252 /* initializer */ newIdentifier);
33253 }
33254 const newDeclList = ts$1.updateVariableDeclarationList(
33255 /* declarationList */ stmt.declarationList,
33256 /* declarations */ newDeclarations);
33257 const newStmt = ts$1.updateVariableStatement(
33258 /* statement */ stmt,
33259 /* modifiers */ stmt.modifiers,
33260 /* declarationList */ newDeclList);
33261 return newStmt;
33262 }
33263 /**
33264 * Check whether the given VariableStatement has any Ivy switch variables.
33265 */
33266 function hasIvySwitches(stmt) {
33267 return stmt.declarationList.declarations.some(decl => decl.initializer !== undefined && ts$1.isIdentifier(decl.initializer) &&
33268 decl.initializer.text.endsWith(IVY_SWITCH_PRE_SUFFIX));
33269 }
33270
33271 /**
33272 * @license
33273 * Copyright Google LLC All Rights Reserved.
33274 *
33275 * Use of this source code is governed by an MIT-style license that can be
33276 * found in the LICENSE file at https://angular.io/license
33277 */
33278 var UpdateMode;
33279 (function (UpdateMode) {
33280 /**
33281 * A complete update creates a completely new overlay of type-checking code on top of the user's
33282 * original program, which doesn't include type-checking code from previous calls to
33283 * `updateFiles`.
33284 */
33285 UpdateMode[UpdateMode["Complete"] = 0] = "Complete";
33286 /**
33287 * An incremental update changes the contents of some files in the type-checking program without
33288 * reverting any prior changes.
33289 */
33290 UpdateMode[UpdateMode["Incremental"] = 1] = "Incremental";
33291 })(UpdateMode || (UpdateMode = {}));
33292
33293 /**
33294 * @license
33295 * Copyright Google LLC All Rights Reserved.
33296 *
33297 * Use of this source code is governed by an MIT-style license that can be
33298 * found in the LICENSE file at https://angular.io/license
33299 */
33300 /**
33301 * Describes the scope of the caller's interest in template type-checking results.
33302 */
33303 var OptimizeFor;
33304 (function (OptimizeFor) {
33305 /**
33306 * Indicates that a consumer of a `TemplateTypeChecker` is only interested in results for a given
33307 * file, and wants them as fast as possible.
33308 *
33309 * Calling `TemplateTypeChecker` methods successively for multiple files while specifying
33310 * `OptimizeFor.SingleFile` can result in significant unnecessary overhead overall.
33311 */
33312 OptimizeFor[OptimizeFor["SingleFile"] = 0] = "SingleFile";
33313 /**
33314 * Indicates that a consumer of a `TemplateTypeChecker` intends to query for results pertaining to
33315 * the entire user program, and so the type-checker should internally optimize for this case.
33316 *
33317 * Initial calls to retrieve type-checking information may take longer, but repeated calls to
33318 * gather information for the whole user program will be significantly faster with this mode of
33319 * optimization.
33320 */
33321 OptimizeFor[OptimizeFor["WholeProgram"] = 1] = "WholeProgram";
33322 })(OptimizeFor || (OptimizeFor = {}));
33323
33324 /**
33325 * @license
33326 * Copyright Google LLC All Rights Reserved.
33327 *
33328 * Use of this source code is governed by an MIT-style license that can be
33329 * found in the LICENSE file at https://angular.io/license
33330 */
33331 /**
33332 * Discriminant of an autocompletion source (a `Completion`).
33333 */
33334 var CompletionKind;
33335 (function (CompletionKind) {
33336 CompletionKind[CompletionKind["Reference"] = 0] = "Reference";
33337 CompletionKind[CompletionKind["Variable"] = 1] = "Variable";
33338 })(CompletionKind || (CompletionKind = {}));
33339
33340 /**
33341 * @license
33342 * Copyright Google LLC All Rights Reserved.
33343 *
33344 * Use of this source code is governed by an MIT-style license that can be
33345 * found in the LICENSE file at https://angular.io/license
33346 */
33347 var SymbolKind;
33348 (function (SymbolKind) {
33349 SymbolKind[SymbolKind["Input"] = 0] = "Input";
33350 SymbolKind[SymbolKind["Output"] = 1] = "Output";
33351 SymbolKind[SymbolKind["Binding"] = 2] = "Binding";
33352 SymbolKind[SymbolKind["Reference"] = 3] = "Reference";
33353 SymbolKind[SymbolKind["Variable"] = 4] = "Variable";
33354 SymbolKind[SymbolKind["Directive"] = 5] = "Directive";
33355 SymbolKind[SymbolKind["Element"] = 6] = "Element";
33356 SymbolKind[SymbolKind["Template"] = 7] = "Template";
33357 SymbolKind[SymbolKind["Expression"] = 8] = "Expression";
33358 SymbolKind[SymbolKind["DomBinding"] = 9] = "DomBinding";
33359 SymbolKind[SymbolKind["Pipe"] = 10] = "Pipe";
33360 })(SymbolKind || (SymbolKind = {}));
33361
33362 /**
33363 * @license
33364 * Copyright Google LLC All Rights Reserved.
33365 *
33366 * Use of this source code is governed by an MIT-style license that can be
33367 * found in the LICENSE file at https://angular.io/license
33368 */
33369 /**
33370 * A `ShimGenerator` which adds type-checking files to the `ts.Program`.
33371 *
33372 * This is a requirement for performant template type-checking, as TypeScript will only reuse
33373 * information in the main program when creating the type-checking program if the set of files in
33374 * each are exactly the same. Thus, the main program also needs the synthetic type-checking files.
33375 */
33376 class TypeCheckShimGenerator {
33377 constructor() {
33378 this.extensionPrefix = 'ngtypecheck';
33379 this.shouldEmit = false;
33380 }
33381 generateShimForFile(sf, genFilePath, priorShimSf) {
33382 if (priorShimSf !== null) {
33383 // If this shim existed in the previous program, reuse it now. It might not be correct, but
33384 // reusing it in the main program allows the shape of its imports to potentially remain the
33385 // same and TS can then use the fastest path for incremental program creation. Later during
33386 // the type-checking phase it's going to either be reused, or replaced anyways. Thus there's
33387 // no harm in reuse here even if it's out of date.
33388 return priorShimSf;
33389 }
33390 return ts$1.createSourceFile(genFilePath, 'export const USED_FOR_NG_TYPE_CHECKING = true;', ts$1.ScriptTarget.Latest, true, ts$1.ScriptKind.TS);
33391 }
33392 static shimFor(fileName) {
33393 return absoluteFrom(fileName.replace(/\.tsx?$/, '.ngtypecheck.ts'));
33394 }
33395 }
33396
33397 /**
33398 * @license
33399 * Copyright Google LLC All Rights Reserved.
33400 *
33401 * Use of this source code is governed by an MIT-style license that can be
33402 * found in the LICENSE file at https://angular.io/license
33403 */
33404 const parseSpanComment = /^(\d+),(\d+)$/;
33405 /**
33406 * Reads the trailing comments and finds the first match which is a span comment (i.e. 4,10) on a
33407 * node and returns it as an `AbsoluteSourceSpan`.
33408 *
33409 * Will return `null` if no trailing comments on the node match the expected form of a source span.
33410 */
33411 function readSpanComment(node, sourceFile = node.getSourceFile()) {
33412 return ts$1.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => {
33413 if (kind !== ts$1.SyntaxKind.MultiLineCommentTrivia) {
33414 return null;
33415 }
33416 const commentText = sourceFile.text.substring(pos + 2, end - 2);
33417 const match = commentText.match(parseSpanComment);
33418 if (match === null) {
33419 return null;
33420 }
33421 return new AbsoluteSourceSpan(+match[1], +match[2]);
33422 }) || null;
33423 }
33424 /** Used to identify what type the comment is. */
33425 var CommentTriviaType;
33426 (function (CommentTriviaType) {
33427 CommentTriviaType["DIAGNOSTIC"] = "D";
33428 CommentTriviaType["EXPRESSION_TYPE_IDENTIFIER"] = "T";
33429 })(CommentTriviaType || (CommentTriviaType = {}));
33430 /** Identifies what the TCB expression is for (for example, a directive declaration). */
33431 var ExpressionIdentifier;
33432 (function (ExpressionIdentifier) {
33433 ExpressionIdentifier["DIRECTIVE"] = "DIR";
33434 ExpressionIdentifier["COMPONENT_COMPLETION"] = "COMPCOMP";
33435 ExpressionIdentifier["EVENT_PARAMETER"] = "EP";
33436 })(ExpressionIdentifier || (ExpressionIdentifier = {}));
33437 /** Tags the node with the given expression identifier. */
33438 function addExpressionIdentifier(node, identifier) {
33439 ts$1.addSyntheticTrailingComment(node, ts$1.SyntaxKind.MultiLineCommentTrivia, `${CommentTriviaType.EXPRESSION_TYPE_IDENTIFIER}:${identifier}`,
33440 /* hasTrailingNewLine */ false);
33441 }
33442 const IGNORE_FOR_DIAGNOSTICS_MARKER = `${CommentTriviaType.DIAGNOSTIC}:ignore`;
33443 /**
33444 * Tag the `ts.Node` with an indication that any errors arising from the evaluation of the node
33445 * should be ignored.
33446 */
33447 function markIgnoreDiagnostics(node) {
33448 ts$1.addSyntheticTrailingComment(node, ts$1.SyntaxKind.MultiLineCommentTrivia, IGNORE_FOR_DIAGNOSTICS_MARKER,
33449 /* hasTrailingNewLine */ false);
33450 }
33451 /** Returns true if the node has a marker that indicates diagnostics errors should be ignored. */
33452 function hasIgnoreForDiagnosticsMarker(node, sourceFile) {
33453 return ts$1.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => {
33454 if (kind !== ts$1.SyntaxKind.MultiLineCommentTrivia) {
33455 return null;
33456 }
33457 const commentText = sourceFile.text.substring(pos + 2, end - 2);
33458 return commentText === IGNORE_FOR_DIAGNOSTICS_MARKER;
33459 }) === true;
33460 }
33461 function makeRecursiveVisitor(visitor) {
33462 function recursiveVisitor(node) {
33463 const res = visitor(node);
33464 return res !== null ? res : node.forEachChild(recursiveVisitor);
33465 }
33466 return recursiveVisitor;
33467 }
33468 function getSpanFromOptions(opts) {
33469 let withSpan = null;
33470 if (opts.withSpan !== undefined) {
33471 if (opts.withSpan instanceof AbsoluteSourceSpan) {
33472 withSpan = opts.withSpan;
33473 }
33474 else {
33475 withSpan = { start: opts.withSpan.start.offset, end: opts.withSpan.end.offset };
33476 }
33477 }
33478 return withSpan;
33479 }
33480 /**
33481 * Given a `ts.Node` with finds the first node whose matching the criteria specified
33482 * by the `FindOptions`.
33483 *
33484 * Returns `null` when no `ts.Node` matches the given conditions.
33485 */
33486 function findFirstMatchingNode(tcb, opts) {
33487 var _a;
33488 const withSpan = getSpanFromOptions(opts);
33489 const withExpressionIdentifier = opts.withExpressionIdentifier;
33490 const sf = tcb.getSourceFile();
33491 const visitor = makeRecursiveVisitor(node => {
33492 if (!opts.filter(node)) {
33493 return null;
33494 }
33495 if (withSpan !== null) {
33496 const comment = readSpanComment(node, sf);
33497 if (comment === null || withSpan.start !== comment.start || withSpan.end !== comment.end) {
33498 return null;
33499 }
33500 }
33501 if (withExpressionIdentifier !== undefined &&
33502 !hasExpressionIdentifier(sf, node, withExpressionIdentifier)) {
33503 return null;
33504 }
33505 return node;
33506 });
33507 return (_a = tcb.forEachChild(visitor)) !== null && _a !== void 0 ? _a : null;
33508 }
33509 /**
33510 * Given a `ts.Node` with source span comments, finds the first node whose source span comment
33511 * matches the given `sourceSpan`. Additionally, the `filter` function allows matching only
33512 * `ts.Nodes` of a given type, which provides the ability to select only matches of a given type
33513 * when there may be more than one.
33514 *
33515 * Returns `null` when no `ts.Node` matches the given conditions.
33516 */
33517 function findAllMatchingNodes(tcb, opts) {
33518 const withSpan = getSpanFromOptions(opts);
33519 const withExpressionIdentifier = opts.withExpressionIdentifier;
33520 const results = [];
33521 const stack = [tcb];
33522 const sf = tcb.getSourceFile();
33523 while (stack.length > 0) {
33524 const node = stack.pop();
33525 if (!opts.filter(node)) {
33526 stack.push(...node.getChildren());
33527 continue;
33528 }
33529 if (withSpan !== null) {
33530 const comment = readSpanComment(node, sf);
33531 if (comment === null || withSpan.start !== comment.start || withSpan.end !== comment.end) {
33532 stack.push(...node.getChildren());
33533 continue;
33534 }
33535 }
33536 if (withExpressionIdentifier !== undefined &&
33537 !hasExpressionIdentifier(sf, node, withExpressionIdentifier)) {
33538 continue;
33539 }
33540 results.push(node);
33541 }
33542 return results;
33543 }
33544 function hasExpressionIdentifier(sourceFile, node, identifier) {
33545 return ts$1.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => {
33546 if (kind !== ts$1.SyntaxKind.MultiLineCommentTrivia) {
33547 return false;
33548 }
33549 const commentText = sourceFile.text.substring(pos + 2, end - 2);
33550 return commentText === `${CommentTriviaType.EXPRESSION_TYPE_IDENTIFIER}:${identifier}`;
33551 }) || false;
33552 }
33553
33554 /**
33555 * @license
33556 * Copyright Google LLC All Rights Reserved.
33557 *
33558 * Use of this source code is governed by an MIT-style license that can be
33559 * found in the LICENSE file at https://angular.io/license
33560 */
33561 /**
33562 * Powers autocompletion for a specific component.
33563 *
33564 * Internally caches autocompletion results, and must be discarded if the component template or
33565 * surrounding TS program have changed.
33566 */
33567 class CompletionEngine {
33568 constructor(tcb, data, shimPath) {
33569 this.tcb = tcb;
33570 this.data = data;
33571 this.shimPath = shimPath;
33572 /**
33573 * Cache of `GlobalCompletion`s for various levels of the template, including the root template
33574 * (`null`).
33575 */
33576 this.globalCompletionCache = new Map();
33577 this.expressionCompletionCache = new Map();
33578 }
33579 /**
33580 * Get global completions within the given template context - either a `TmplAstTemplate` embedded
33581 * view, or `null` for the root template context.
33582 */
33583 getGlobalCompletions(context) {
33584 if (this.globalCompletionCache.has(context)) {
33585 return this.globalCompletionCache.get(context);
33586 }
33587 // Find the component completion expression within the TCB. This looks like: `ctx. /* ... */;`
33588 const globalRead = findFirstMatchingNode(this.tcb, {
33589 filter: ts$1.isPropertyAccessExpression,
33590 withExpressionIdentifier: ExpressionIdentifier.COMPONENT_COMPLETION
33591 });
33592 if (globalRead === null) {
33593 return null;
33594 }
33595 const completion = {
33596 componentContext: {
33597 shimPath: this.shimPath,
33598 // `globalRead.name` is an empty `ts.Identifier`, so its start position immediately follows
33599 // the `.` in `ctx.`. TS autocompletion APIs can then be used to access completion results
33600 // for the component context.
33601 positionInShimFile: globalRead.name.getStart(),
33602 },
33603 templateContext: new Map(),
33604 };
33605 // The bound template already has details about the references and variables in scope in the
33606 // `context` template - they just need to be converted to `Completion`s.
33607 for (const node of this.data.boundTarget.getEntitiesInTemplateScope(context)) {
33608 if (node instanceof Reference) {
33609 completion.templateContext.set(node.name, {
33610 kind: CompletionKind.Reference,
33611 node,
33612 });
33613 }
33614 else {
33615 completion.templateContext.set(node.name, {
33616 kind: CompletionKind.Variable,
33617 node,
33618 });
33619 }
33620 }
33621 this.globalCompletionCache.set(context, completion);
33622 return completion;
33623 }
33624 getExpressionCompletionLocation(expr) {
33625 if (this.expressionCompletionCache.has(expr)) {
33626 return this.expressionCompletionCache.get(expr);
33627 }
33628 // Completion works inside property reads and method calls.
33629 let tsExpr = null;
33630 if (expr instanceof PropertyRead || expr instanceof MethodCall ||
33631 expr instanceof PropertyWrite) {
33632 // Non-safe navigation operations are trivial: `foo.bar` or `foo.bar()`
33633 tsExpr = findFirstMatchingNode(this.tcb, {
33634 filter: ts$1.isPropertyAccessExpression,
33635 withSpan: expr.nameSpan,
33636 });
33637 }
33638 else if (expr instanceof SafePropertyRead || expr instanceof SafeMethodCall) {
33639 // Safe navigation operations are a little more complex, and involve a ternary. Completion
33640 // happens in the "true" case of the ternary.
33641 const ternaryExpr = findFirstMatchingNode(this.tcb, {
33642 filter: ts$1.isParenthesizedExpression,
33643 withSpan: expr.sourceSpan,
33644 });
33645 if (ternaryExpr === null || !ts$1.isConditionalExpression(ternaryExpr.expression)) {
33646 return null;
33647 }
33648 const whenTrue = ternaryExpr.expression.whenTrue;
33649 if (expr instanceof SafePropertyRead && ts$1.isPropertyAccessExpression(whenTrue)) {
33650 tsExpr = whenTrue;
33651 }
33652 else if (expr instanceof SafeMethodCall && ts$1.isCallExpression(whenTrue) &&
33653 ts$1.isPropertyAccessExpression(whenTrue.expression)) {
33654 tsExpr = whenTrue.expression;
33655 }
33656 }
33657 if (tsExpr === null) {
33658 return null;
33659 }
33660 const res = {
33661 shimPath: this.shimPath,
33662 positionInShimFile: tsExpr.name.getEnd(),
33663 };
33664 this.expressionCompletionCache.set(expr, res);
33665 return res;
33666 }
33667 }
33668
33669 /**
33670 * @license
33671 * Copyright Google LLC All Rights Reserved.
33672 *
33673 * Use of this source code is governed by an MIT-style license that can be
33674 * found in the LICENSE file at https://angular.io/license
33675 */
33676 /**
33677 * Constructs a `ts.Diagnostic` for a given `ParseSourceSpan` within a template.
33678 */
33679 function makeTemplateDiagnostic(templateId, mapping, span, category, code, messageText, relatedMessage) {
33680 if (mapping.type === 'direct') {
33681 let relatedInformation = undefined;
33682 if (relatedMessage !== undefined) {
33683 relatedInformation = [{
33684 category: ts$1.DiagnosticCategory.Message,
33685 code: 0,
33686 file: mapping.node.getSourceFile(),
33687 start: relatedMessage.span.start.offset,
33688 length: relatedMessage.span.end.offset - relatedMessage.span.start.offset,
33689 messageText: relatedMessage.text,
33690 }];
33691 }
33692 // For direct mappings, the error is shown inline as ngtsc was able to pinpoint a string
33693 // constant within the `@Component` decorator for the template. This allows us to map the error
33694 // directly into the bytes of the source file.
33695 return {
33696 source: 'ngtsc',
33697 code,
33698 category,
33699 messageText,
33700 file: mapping.node.getSourceFile(),
33701 componentFile: mapping.node.getSourceFile(),
33702 templateId,
33703 start: span.start.offset,
33704 length: span.end.offset - span.start.offset,
33705 relatedInformation,
33706 };
33707 }
33708 else if (mapping.type === 'indirect' || mapping.type === 'external') {
33709 // For indirect mappings (template was declared inline, but ngtsc couldn't map it directly
33710 // to a string constant in the decorator), the component's file name is given with a suffix
33711 // indicating it's not the TS file being displayed, but a template.
33712 // For external temoplates, the HTML filename is used.
33713 const componentSf = mapping.componentClass.getSourceFile();
33714 const componentName = mapping.componentClass.name.text;
33715 // TODO(alxhub): remove cast when TS in g3 supports this narrowing.
33716 const fileName = mapping.type === 'indirect' ?
33717 `${componentSf.fileName} (${componentName} template)` :
33718 mapping.templateUrl;
33719 // TODO(alxhub): investigate creating a fake `ts.SourceFile` here instead of invoking the TS
33720 // parser against the template (HTML is just really syntactically invalid TypeScript code ;).
33721 // Also investigate caching the file to avoid running the parser multiple times.
33722 const sf = ts$1.createSourceFile(fileName, mapping.template, ts$1.ScriptTarget.Latest, false, ts$1.ScriptKind.JSX);
33723 let relatedInformation = [];
33724 if (relatedMessage !== undefined) {
33725 relatedInformation.push({
33726 category: ts$1.DiagnosticCategory.Message,
33727 code: 0,
33728 file: sf,
33729 start: relatedMessage.span.start.offset,
33730 length: relatedMessage.span.end.offset - relatedMessage.span.start.offset,
33731 messageText: relatedMessage.text,
33732 });
33733 }
33734 relatedInformation.push({
33735 category: ts$1.DiagnosticCategory.Message,
33736 code: 0,
33737 file: componentSf,
33738 // mapping.node represents either the 'template' or 'templateUrl' expression. getStart()
33739 // and getEnd() are used because they don't include surrounding whitespace.
33740 start: mapping.node.getStart(),
33741 length: mapping.node.getEnd() - mapping.node.getStart(),
33742 messageText: `Error occurs in the template of component ${componentName}.`,
33743 });
33744 return {
33745 source: 'ngtsc',
33746 category,
33747 code,
33748 messageText,
33749 file: sf,
33750 componentFile: componentSf,
33751 templateId,
33752 start: span.start.offset,
33753 length: span.end.offset - span.start.offset,
33754 // Show a secondary message indicating the component whose template contains the error.
33755 relatedInformation,
33756 };
33757 }
33758 else {
33759 throw new Error(`Unexpected source mapping type: ${mapping.type}`);
33760 }
33761 }
33762
33763 /**
33764 * @license
33765 * Copyright Google LLC All Rights Reserved.
33766 *
33767 * Use of this source code is governed by an MIT-style license that can be
33768 * found in the LICENSE file at https://angular.io/license
33769 */
33770 const TEMPLATE_ID = Symbol('ngTemplateId');
33771 const NEXT_TEMPLATE_ID = Symbol('ngNextTemplateId');
33772 function getTemplateId(clazz) {
33773 const node = clazz;
33774 if (node[TEMPLATE_ID] === undefined) {
33775 node[TEMPLATE_ID] = allocateTemplateId(node.getSourceFile());
33776 }
33777 return node[TEMPLATE_ID];
33778 }
33779 function allocateTemplateId(sf) {
33780 if (sf[NEXT_TEMPLATE_ID] === undefined) {
33781 sf[NEXT_TEMPLATE_ID] = 1;
33782 }
33783 return (`tcb${sf[NEXT_TEMPLATE_ID]++}`);
33784 }
33785
33786 /**
33787 * @license
33788 * Copyright Google LLC All Rights Reserved.
33789 *
33790 * Use of this source code is governed by an MIT-style license that can be
33791 * found in the LICENSE file at https://angular.io/license
33792 */
33793 const REGISTRY = new DomElementSchemaRegistry();
33794 const REMOVE_XHTML_REGEX = /^:xhtml:/;
33795 /**
33796 * Checks non-Angular elements and properties against the `DomElementSchemaRegistry`, a schema
33797 * maintained by the Angular team via extraction from a browser IDL.
33798 */
33799 class RegistryDomSchemaChecker {
33800 constructor(resolver) {
33801 this.resolver = resolver;
33802 this._diagnostics = [];
33803 }
33804 get diagnostics() {
33805 return this._diagnostics;
33806 }
33807 checkElement(id, element, schemas) {
33808 // HTML elements inside an SVG `foreignObject` are declared in the `xhtml` namespace.
33809 // We need to strip it before handing it over to the registry because all HTML tag names
33810 // in the registry are without a namespace.
33811 const name = element.name.replace(REMOVE_XHTML_REGEX, '');
33812 if (!REGISTRY.hasElement(name, schemas)) {
33813 const mapping = this.resolver.getSourceMapping(id);
33814 let errorMsg = `'${name}' is not a known element:\n`;
33815 errorMsg +=
33816 `1. If '${name}' is an Angular component, then verify that it is part of this module.\n`;
33817 if (name.indexOf('-') > -1) {
33818 errorMsg += `2. If '${name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`;
33819 }
33820 else {
33821 errorMsg +=
33822 `2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
33823 }
33824 const diag = makeTemplateDiagnostic(id, mapping, element.startSourceSpan, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.SCHEMA_INVALID_ELEMENT), errorMsg);
33825 this._diagnostics.push(diag);
33826 }
33827 }
33828 checkProperty(id, element, name, span, schemas) {
33829 if (!REGISTRY.hasProperty(element.name, name, schemas)) {
33830 const mapping = this.resolver.getSourceMapping(id);
33831 let errorMsg = `Can't bind to '${name}' since it isn't a known property of '${element.name}'.`;
33832 if (element.name.startsWith('ng-')) {
33833 errorMsg +=
33834 `\n1. If '${name}' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component.` +
33835 `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
33836 }
33837 else if (element.name.indexOf('-') > -1) {
33838 errorMsg +=
33839 `\n1. If '${element.name}' is an Angular component and it has '${name}' input, then verify that it is part of this module.` +
33840 `\n2. If '${element
33841 .name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.` +
33842 `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
33843 }
33844 const diag = makeTemplateDiagnostic(id, mapping, span, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.SCHEMA_INVALID_ATTRIBUTE), errorMsg);
33845 this._diagnostics.push(diag);
33846 }
33847 }
33848 }
33849
33850 /**
33851 * @license
33852 * Copyright Google LLC All Rights Reserved.
33853 *
33854 * Use of this source code is governed by an MIT-style license that can be
33855 * found in the LICENSE file at https://angular.io/license
33856 */
33857 /**
33858 * A `Set` of `ts.SyntaxKind`s of `ts.Expression` which are safe to wrap in a `ts.AsExpression`
33859 * without needing to be wrapped in parentheses.
33860 *
33861 * For example, `foo.bar()` is a `ts.CallExpression`, and can be safely cast to `any` with
33862 * `foo.bar() as any`. however, `foo !== bar` is a `ts.BinaryExpression`, and attempting to cast
33863 * without the parentheses yields the expression `foo !== bar as any`. This is semantically
33864 * equivalent to `foo !== (bar as any)`, which is not what was intended. Thus,
33865 * `ts.BinaryExpression`s need to be wrapped in parentheses before casting.
33866 */
33867 //
33868 const SAFE_TO_CAST_WITHOUT_PARENS = new Set([
33869 // Expressions which are already parenthesized can be cast without further wrapping.
33870 ts$1.SyntaxKind.ParenthesizedExpression,
33871 // Expressions which form a single lexical unit leave no room for precedence issues with the cast.
33872 ts$1.SyntaxKind.Identifier,
33873 ts$1.SyntaxKind.CallExpression,
33874 ts$1.SyntaxKind.NonNullExpression,
33875 ts$1.SyntaxKind.ElementAccessExpression,
33876 ts$1.SyntaxKind.PropertyAccessExpression,
33877 ts$1.SyntaxKind.ArrayLiteralExpression,
33878 ts$1.SyntaxKind.ObjectLiteralExpression,
33879 // The same goes for various literals.
33880 ts$1.SyntaxKind.StringLiteral,
33881 ts$1.SyntaxKind.NumericLiteral,
33882 ts$1.SyntaxKind.TrueKeyword,
33883 ts$1.SyntaxKind.FalseKeyword,
33884 ts$1.SyntaxKind.NullKeyword,
33885 ts$1.SyntaxKind.UndefinedKeyword,
33886 ]);
33887 function tsCastToAny(expr) {
33888 // Wrap `expr` in parentheses if needed (see `SAFE_TO_CAST_WITHOUT_PARENS` above).
33889 if (!SAFE_TO_CAST_WITHOUT_PARENS.has(expr.kind)) {
33890 expr = ts$1.createParen(expr);
33891 }
33892 // The outer expression is always wrapped in parentheses.
33893 return ts$1.createParen(ts$1.createAsExpression(expr, ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword)));
33894 }
33895 /**
33896 * Create an expression which instantiates an element by its HTML tagName.
33897 *
33898 * Thanks to narrowing of `document.createElement()`, this expression will have its type inferred
33899 * based on the tag name, including for custom elements that have appropriate .d.ts definitions.
33900 */
33901 function tsCreateElement(tagName) {
33902 const createElement = ts$1.createPropertyAccess(
33903 /* expression */ ts$1.createIdentifier('document'), 'createElement');
33904 return ts$1.createCall(
33905 /* expression */ createElement,
33906 /* typeArguments */ undefined,
33907 /* argumentsArray */ [ts$1.createLiteral(tagName)]);
33908 }
33909 /**
33910 * Create a `ts.VariableStatement` which declares a variable without explicit initialization.
33911 *
33912 * The initializer `null!` is used to bypass strict variable initialization checks.
33913 *
33914 * Unlike with `tsCreateVariable`, the type of the variable is explicitly specified.
33915 */
33916 function tsDeclareVariable(id, type) {
33917 const decl = ts$1.createVariableDeclaration(
33918 /* name */ id,
33919 /* type */ type,
33920 /* initializer */ ts$1.createNonNullExpression(ts$1.createNull()));
33921 return ts$1.createVariableStatement(
33922 /* modifiers */ undefined,
33923 /* declarationList */ [decl]);
33924 }
33925 /**
33926 * Creates a `ts.TypeQueryNode` for a coerced input.
33927 *
33928 * For example: `typeof MatInput.ngAcceptInputType_value`, where MatInput is `typeName` and `value`
33929 * is the `coercedInputName`.
33930 *
33931 * @param typeName The `EntityName` of the Directive where the static coerced input is defined.
33932 * @param coercedInputName The field name of the coerced input.
33933 */
33934 function tsCreateTypeQueryForCoercedInput(typeName, coercedInputName) {
33935 return ts$1.createTypeQueryNode(ts$1.createQualifiedName(typeName, `ngAcceptInputType_${coercedInputName}`));
33936 }
33937 /**
33938 * Create a `ts.VariableStatement` that initializes a variable with a given expression.
33939 *
33940 * Unlike with `tsDeclareVariable`, the type of the variable is inferred from the initializer
33941 * expression.
33942 */
33943 function tsCreateVariable(id, initializer) {
33944 const decl = ts$1.createVariableDeclaration(
33945 /* name */ id,
33946 /* type */ undefined,
33947 /* initializer */ initializer);
33948 return ts$1.createVariableStatement(
33949 /* modifiers */ undefined,
33950 /* declarationList */ [decl]);
33951 }
33952 /**
33953 * Construct a `ts.CallExpression` that calls a method on a receiver.
33954 */
33955 function tsCallMethod(receiver, methodName, args = []) {
33956 const methodAccess = ts$1.createPropertyAccess(receiver, methodName);
33957 return ts$1.createCall(
33958 /* expression */ methodAccess,
33959 /* typeArguments */ undefined,
33960 /* argumentsArray */ args);
33961 }
33962 function checkIfClassIsExported(node) {
33963 // A class is exported if one of two conditions is met:
33964 // 1) it has the 'export' modifier.
33965 // 2) it's declared at the top level, and there is an export statement for the class.
33966 if (node.modifiers !== undefined &&
33967 node.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.ExportKeyword)) {
33968 // Condition 1 is true, the class has an 'export' keyword attached.
33969 return true;
33970 }
33971 else if (node.parent !== undefined && ts$1.isSourceFile(node.parent) &&
33972 checkIfFileHasExport(node.parent, node.name.text)) {
33973 // Condition 2 is true, the class is exported via an 'export {}' statement.
33974 return true;
33975 }
33976 return false;
33977 }
33978 function checkIfFileHasExport(sf, name) {
33979 for (const stmt of sf.statements) {
33980 if (ts$1.isExportDeclaration(stmt) && stmt.exportClause !== undefined &&
33981 ts$1.isNamedExports(stmt.exportClause)) {
33982 for (const element of stmt.exportClause.elements) {
33983 if (element.propertyName === undefined && element.name.text === name) {
33984 // The named declaration is directly exported.
33985 return true;
33986 }
33987 else if (element.propertyName !== undefined && element.propertyName.text == name) {
33988 // The named declaration is exported via an alias.
33989 return true;
33990 }
33991 }
33992 }
33993 }
33994 return false;
33995 }
33996 function checkIfGenericTypesAreUnbound(node) {
33997 if (node.typeParameters === undefined) {
33998 return true;
33999 }
34000 return node.typeParameters.every(param => param.constraint === undefined);
34001 }
34002 function isAccessExpression(node) {
34003 return ts$1.isPropertyAccessExpression(node) || ts$1.isElementAccessExpression(node);
34004 }
34005
34006 /**
34007 * @license
34008 * Copyright Google LLC All Rights Reserved.
34009 *
34010 * Use of this source code is governed by an MIT-style license that can be
34011 * found in the LICENSE file at https://angular.io/license
34012 */
34013 /**
34014 * Determines whether the provided type can be emitted, which means that it can be safely emitted
34015 * into a different location.
34016 *
34017 * If this function returns true, a `TypeEmitter` should be able to succeed. Vice versa, if this
34018 * function returns false, then using the `TypeEmitter` should not be attempted as it is known to
34019 * fail.
34020 */
34021 function canEmitType(type, resolver) {
34022 return canEmitTypeWorker(type);
34023 function canEmitTypeWorker(type) {
34024 return visitTypeNode(type, {
34025 visitTypeReferenceNode: type => canEmitTypeReference(type),
34026 visitArrayTypeNode: type => canEmitTypeWorker(type.elementType),
34027 visitKeywordType: () => true,
34028 visitLiteralType: () => true,
34029 visitOtherType: () => false,
34030 });
34031 }
34032 function canEmitTypeReference(type) {
34033 const reference = resolver(type);
34034 // If the type could not be resolved, it can not be emitted.
34035 if (reference === null) {
34036 return false;
34037 }
34038 // If the type is a reference without a owning module, consider the type not to be eligible for
34039 // emitting.
34040 if (reference instanceof Reference$1 && !reference.hasOwningModuleGuess) {
34041 return false;
34042 }
34043 // The type can be emitted if either it does not have any type arguments, or all of them can be
34044 // emitted.
34045 return type.typeArguments === undefined || type.typeArguments.every(canEmitTypeWorker);
34046 }
34047 }
34048 /**
34049 * Given a `ts.TypeNode`, this class derives an equivalent `ts.TypeNode` that has been emitted into
34050 * a different context.
34051 *
34052 * For example, consider the following code:
34053 *
34054 * ```
34055 * import {NgIterable} from '@angular/core';
34056 *
34057 * class NgForOf<T, U extends NgIterable<T>> {}
34058 * ```
34059 *
34060 * Here, the generic type parameters `T` and `U` can be emitted into a different context, as the
34061 * type reference to `NgIterable` originates from an absolute module import so that it can be
34062 * emitted anywhere, using that same module import. The process of emitting translates the
34063 * `NgIterable` type reference to a type reference that is valid in the context in which it is
34064 * emitted, for example:
34065 *
34066 * ```
34067 * import * as i0 from '@angular/core';
34068 * import * as i1 from '@angular/common';
34069 *
34070 * const _ctor1: <T, U extends i0.NgIterable<T>>(o: Pick<i1.NgForOf<T, U>, 'ngForOf'>):
34071 * i1.NgForOf<T, U>;
34072 * ```
34073 *
34074 * Notice how the type reference for `NgIterable` has been translated into a qualified name,
34075 * referring to the namespace import that was created.
34076 */
34077 class TypeEmitter {
34078 constructor(resolver, emitReference) {
34079 this.resolver = resolver;
34080 this.emitReference = emitReference;
34081 }
34082 emitType(type) {
34083 return visitTypeNode(type, {
34084 visitTypeReferenceNode: type => this.emitTypeReference(type),
34085 visitArrayTypeNode: type => ts$1.updateArrayTypeNode(type, this.emitType(type.elementType)),
34086 visitKeywordType: type => type,
34087 visitLiteralType: type => type,
34088 visitOtherType: () => {
34089 throw new Error('Unable to emit a complex type');
34090 },
34091 });
34092 }
34093 emitTypeReference(type) {
34094 // Determine the reference that the type corresponds with.
34095 const reference = this.resolver(type);
34096 if (reference === null) {
34097 throw new Error('Unable to emit an unresolved reference');
34098 }
34099 // Emit the type arguments, if any.
34100 let typeArguments = undefined;
34101 if (type.typeArguments !== undefined) {
34102 typeArguments = ts$1.createNodeArray(type.typeArguments.map(typeArg => this.emitType(typeArg)));
34103 }
34104 // Emit the type name.
34105 let typeName = type.typeName;
34106 if (reference instanceof Reference$1) {
34107 if (!reference.hasOwningModuleGuess) {
34108 throw new Error('A type reference to emit must be imported from an absolute module');
34109 }
34110 const emittedType = this.emitReference(reference);
34111 if (!ts$1.isTypeReferenceNode(emittedType)) {
34112 throw new Error(`Expected TypeReferenceNode for emitted reference, got ${ts$1.SyntaxKind[emittedType.kind]}`);
34113 }
34114 typeName = emittedType.typeName;
34115 }
34116 return ts$1.updateTypeReferenceNode(type, typeName, typeArguments);
34117 }
34118 }
34119 function visitTypeNode(type, visitor) {
34120 if (ts$1.isTypeReferenceNode(type)) {
34121 return visitor.visitTypeReferenceNode(type);
34122 }
34123 else if (ts$1.isArrayTypeNode(type)) {
34124 return visitor.visitArrayTypeNode(type);
34125 }
34126 else if (ts$1.isLiteralTypeNode(type)) {
34127 return visitor.visitLiteralType(type);
34128 }
34129 switch (type.kind) {
34130 case ts$1.SyntaxKind.AnyKeyword:
34131 case ts$1.SyntaxKind.UnknownKeyword:
34132 case ts$1.SyntaxKind.NumberKeyword:
34133 case ts$1.SyntaxKind.ObjectKeyword:
34134 case ts$1.SyntaxKind.BooleanKeyword:
34135 case ts$1.SyntaxKind.StringKeyword:
34136 case ts$1.SyntaxKind.UndefinedKeyword:
34137 case ts$1.SyntaxKind.NullKeyword:
34138 return visitor.visitKeywordType(type);
34139 default:
34140 return visitor.visitOtherType(type);
34141 }
34142 }
34143
34144 /**
34145 * @license
34146 * Copyright Google LLC All Rights Reserved.
34147 *
34148 * Use of this source code is governed by an MIT-style license that can be
34149 * found in the LICENSE file at https://angular.io/license
34150 */
34151 /**
34152 * See `TypeEmitter` for more information on the emitting process.
34153 */
34154 class TypeParameterEmitter {
34155 constructor(typeParameters, reflector) {
34156 this.typeParameters = typeParameters;
34157 this.reflector = reflector;
34158 }
34159 /**
34160 * Determines whether the type parameters can be emitted. If this returns true, then a call to
34161 * `emit` is known to succeed. Vice versa, if false is returned then `emit` should not be
34162 * called, as it would fail.
34163 */
34164 canEmit() {
34165 if (this.typeParameters === undefined) {
34166 return true;
34167 }
34168 return this.typeParameters.every(typeParam => {
34169 if (typeParam.constraint === undefined) {
34170 return true;
34171 }
34172 return canEmitType(typeParam.constraint, type => this.resolveTypeReference(type));
34173 });
34174 }
34175 /**
34176 * Emits the type parameters using the provided emitter function for `Reference`s.
34177 */
34178 emit(emitReference) {
34179 if (this.typeParameters === undefined) {
34180 return undefined;
34181 }
34182 const emitter = new TypeEmitter(type => this.resolveTypeReference(type), emitReference);
34183 return this.typeParameters.map(typeParam => {
34184 const constraint = typeParam.constraint !== undefined ? emitter.emitType(typeParam.constraint) : undefined;
34185 return ts$1.updateTypeParameterDeclaration(
34186 /* node */ typeParam,
34187 /* name */ typeParam.name,
34188 /* constraint */ constraint,
34189 /* defaultType */ typeParam.default);
34190 });
34191 }
34192 resolveTypeReference(type) {
34193 const target = ts$1.isIdentifier(type.typeName) ? type.typeName : type.typeName.right;
34194 const declaration = this.reflector.getDeclarationOfIdentifier(target);
34195 // If no declaration could be resolved or does not have a `ts.Declaration`, the type cannot be
34196 // resolved.
34197 if (declaration === null || declaration.node === null) {
34198 return null;
34199 }
34200 // If the declaration corresponds with a local type parameter, the type reference can be used
34201 // as is.
34202 if (this.isLocalTypeParameter(declaration.node)) {
34203 return type;
34204 }
34205 let owningModule = null;
34206 if (declaration.viaModule !== null) {
34207 owningModule = {
34208 specifier: declaration.viaModule,
34209 resolutionContext: type.getSourceFile().fileName,
34210 };
34211 }
34212 return new Reference$1(declaration.node, owningModule);
34213 }
34214 isLocalTypeParameter(decl) {
34215 // Checking for local type parameters only occurs during resolution of type parameters, so it is
34216 // guaranteed that type parameters are present.
34217 return this.typeParameters.some(param => param === decl);
34218 }
34219 }
34220
34221 /**
34222 * @license
34223 * Copyright Google LLC All Rights Reserved.
34224 *
34225 * Use of this source code is governed by an MIT-style license that can be
34226 * found in the LICENSE file at https://angular.io/license
34227 */
34228 function generateTypeCtorDeclarationFn(node, meta, nodeTypeRef, typeParams, reflector) {
34229 if (requiresInlineTypeCtor(node, reflector)) {
34230 throw new Error(`${node.name.text} requires an inline type constructor`);
34231 }
34232 const rawTypeArgs = typeParams !== undefined ? generateGenericArgs(typeParams) : undefined;
34233 const rawType = ts$1.createTypeReferenceNode(nodeTypeRef, rawTypeArgs);
34234 const initParam = constructTypeCtorParameter(node, meta, rawType);
34235 const typeParameters = typeParametersWithDefaultTypes(typeParams);
34236 if (meta.body) {
34237 const fnType = ts$1.createFunctionTypeNode(
34238 /* typeParameters */ typeParameters,
34239 /* parameters */ [initParam],
34240 /* type */ rawType);
34241 const decl = ts$1.createVariableDeclaration(
34242 /* name */ meta.fnName,
34243 /* type */ fnType,
34244 /* body */ ts$1.createNonNullExpression(ts$1.createNull()));
34245 const declList = ts$1.createVariableDeclarationList([decl], ts$1.NodeFlags.Const);
34246 return ts$1.createVariableStatement(
34247 /* modifiers */ undefined,
34248 /* declarationList */ declList);
34249 }
34250 else {
34251 return ts$1.createFunctionDeclaration(
34252 /* decorators */ undefined,
34253 /* modifiers */ [ts$1.createModifier(ts$1.SyntaxKind.DeclareKeyword)],
34254 /* asteriskToken */ undefined,
34255 /* name */ meta.fnName,
34256 /* typeParameters */ typeParameters,
34257 /* parameters */ [initParam],
34258 /* type */ rawType,
34259 /* body */ undefined);
34260 }
34261 }
34262 /**
34263 * Generate an inline type constructor for the given class and metadata.
34264 *
34265 * An inline type constructor is a specially shaped TypeScript static method, intended to be placed
34266 * within a directive class itself, that permits type inference of any generic type parameters of
34267 * the class from the types of expressions bound to inputs or outputs, and the types of elements
34268 * that match queries performed by the directive. It also catches any errors in the types of these
34269 * expressions. This method is never called at runtime, but is used in type-check blocks to
34270 * construct directive types.
34271 *
34272 * An inline type constructor for NgFor looks like:
34273 *
34274 * static ngTypeCtor<T>(init: Pick<NgForOf<T>, 'ngForOf'|'ngForTrackBy'|'ngForTemplate'>):
34275 * NgForOf<T>;
34276 *
34277 * A typical constructor would be:
34278 *
34279 * NgForOf.ngTypeCtor(init: {
34280 * ngForOf: ['foo', 'bar'],
34281 * ngForTrackBy: null as any,
34282 * ngForTemplate: null as any,
34283 * }); // Infers a type of NgForOf<string>.
34284 *
34285 * Any inputs declared on the type for which no property binding is present are assigned a value of
34286 * type `any`, to avoid producing any type errors for unset inputs.
34287 *
34288 * Inline type constructors are used when the type being created has bounded generic types which
34289 * make writing a declared type constructor (via `generateTypeCtorDeclarationFn`) difficult or
34290 * impossible.
34291 *
34292 * @param node the `ClassDeclaration<ts.ClassDeclaration>` for which a type constructor will be
34293 * generated.
34294 * @param meta additional metadata required to generate the type constructor.
34295 * @returns a `ts.MethodDeclaration` for the type constructor.
34296 */
34297 function generateInlineTypeCtor(node, meta) {
34298 // Build rawType, a `ts.TypeNode` of the class with its generic parameters passed through from
34299 // the definition without any type bounds. For example, if the class is
34300 // `FooDirective<T extends Bar>`, its rawType would be `FooDirective<T>`.
34301 const rawTypeArgs = node.typeParameters !== undefined ? generateGenericArgs(node.typeParameters) : undefined;
34302 const rawType = ts$1.createTypeReferenceNode(node.name, rawTypeArgs);
34303 const initParam = constructTypeCtorParameter(node, meta, rawType);
34304 // If this constructor is being generated into a .ts file, then it needs a fake body. The body
34305 // is set to a return of `null!`. If the type constructor is being generated into a .d.ts file,
34306 // it needs no body.
34307 let body = undefined;
34308 if (meta.body) {
34309 body = ts$1.createBlock([
34310 ts$1.createReturn(ts$1.createNonNullExpression(ts$1.createNull())),
34311 ]);
34312 }
34313 // Create the type constructor method declaration.
34314 return ts$1.createMethod(
34315 /* decorators */ undefined,
34316 /* modifiers */ [ts$1.createModifier(ts$1.SyntaxKind.StaticKeyword)],
34317 /* asteriskToken */ undefined,
34318 /* name */ meta.fnName,
34319 /* questionToken */ undefined,
34320 /* typeParameters */ typeParametersWithDefaultTypes(node.typeParameters),
34321 /* parameters */ [initParam],
34322 /* type */ rawType,
34323 /* body */ body);
34324 }
34325 function constructTypeCtorParameter(node, meta, rawType) {
34326 // initType is the type of 'init', the single argument to the type constructor method.
34327 // If the Directive has any inputs, its initType will be:
34328 //
34329 // Pick<rawType, 'inputA'|'inputB'>
34330 //
34331 // Pick here is used to select only those fields from which the generic type parameters of the
34332 // directive will be inferred.
34333 //
34334 // In the special case there are no inputs, initType is set to {}.
34335 let initType = null;
34336 const keys = meta.fields.inputs;
34337 const plainKeys = [];
34338 const coercedKeys = [];
34339 for (const key of keys) {
34340 if (!meta.coercedInputFields.has(key)) {
34341 plainKeys.push(ts$1.createLiteralTypeNode(ts$1.createStringLiteral(key)));
34342 }
34343 else {
34344 coercedKeys.push(ts$1.createPropertySignature(
34345 /* modifiers */ undefined,
34346 /* name */ key,
34347 /* questionToken */ undefined,
34348 /* type */ tsCreateTypeQueryForCoercedInput(rawType.typeName, key),
34349 /* initializer */ undefined));
34350 }
34351 }
34352 if (plainKeys.length > 0) {
34353 // Construct a union of all the field names.
34354 const keyTypeUnion = ts$1.createUnionTypeNode(plainKeys);
34355 // Construct the Pick<rawType, keyTypeUnion>.
34356 initType = ts$1.createTypeReferenceNode('Pick', [rawType, keyTypeUnion]);
34357 }
34358 if (coercedKeys.length > 0) {
34359 const coercedLiteral = ts$1.createTypeLiteralNode(coercedKeys);
34360 initType = initType !== null ? ts$1.createIntersectionTypeNode([initType, coercedLiteral]) :
34361 coercedLiteral;
34362 }
34363 if (initType === null) {
34364 // Special case - no inputs, outputs, or other fields which could influence the result type.
34365 initType = ts$1.createTypeLiteralNode([]);
34366 }
34367 // Create the 'init' parameter itself.
34368 return ts$1.createParameter(
34369 /* decorators */ undefined,
34370 /* modifiers */ undefined,
34371 /* dotDotDotToken */ undefined,
34372 /* name */ 'init',
34373 /* questionToken */ undefined,
34374 /* type */ initType,
34375 /* initializer */ undefined);
34376 }
34377 function generateGenericArgs(params) {
34378 return params.map(param => ts$1.createTypeReferenceNode(param.name, undefined));
34379 }
34380 function requiresInlineTypeCtor(node, host) {
34381 // The class requires an inline type constructor if it has generic type bounds that can not be
34382 // emitted into a different context.
34383 return !checkIfGenericTypeBoundsAreContextFree(node, host);
34384 }
34385 function checkIfGenericTypeBoundsAreContextFree(node, reflector) {
34386 // Generic type parameters are considered context free if they can be emitted into any context.
34387 return new TypeParameterEmitter(node.typeParameters, reflector).canEmit();
34388 }
34389 /**
34390 * Add a default `= any` to type parameters that don't have a default value already.
34391 *
34392 * TypeScript uses the default type of a type parameter whenever inference of that parameter fails.
34393 * This can happen when inferring a complex type from 'any'. For example, if `NgFor`'s inference is
34394 * done with the TCB code:
34395 *
34396 * ```
34397 * class NgFor<T> {
34398 * ngForOf: T[];
34399 * }
34400 *
34401 * declare function ctor<T>(o: Pick<NgFor<T>, 'ngForOf'|'ngForTrackBy'|'ngForTemplate'>): NgFor<T>;
34402 * ```
34403 *
34404 * An invocation looks like:
34405 *
34406 * ```
34407 * var _t1 = ctor({ngForOf: [1, 2], ngForTrackBy: null as any, ngForTemplate: null as any});
34408 * ```
34409 *
34410 * This correctly infers the type `NgFor<number>` for `_t1`, since `T` is inferred from the
34411 * assignment of type `number[]` to `ngForOf`'s type `T[]`. However, if `any` is passed instead:
34412 *
34413 * ```
34414 * var _t2 = ctor({ngForOf: [1, 2] as any, ngForTrackBy: null as any, ngForTemplate: null as any});
34415 * ```
34416 *
34417 * then inference for `T` fails (it cannot be inferred from `T[] = any`). In this case, `T` takes
34418 * the type `{}`, and so `_t2` is inferred as `NgFor<{}>`. This is obviously wrong.
34419 *
34420 * Adding a default type to the generic declaration in the constructor solves this problem, as the
34421 * default type will be used in the event that inference fails.
34422 *
34423 * ```
34424 * declare function ctor<T = any>(o: Pick<NgFor<T>, 'ngForOf'>): NgFor<T>;
34425 *
34426 * var _t3 = ctor({ngForOf: [1, 2] as any});
34427 * ```
34428 *
34429 * This correctly infers `T` as `any`, and therefore `_t3` as `NgFor<any>`.
34430 */
34431 function typeParametersWithDefaultTypes(params) {
34432 if (params === undefined) {
34433 return undefined;
34434 }
34435 return params.map(param => {
34436 if (param.default === undefined) {
34437 return ts$1.updateTypeParameterDeclaration(
34438 /* node */ param,
34439 /* name */ param.name,
34440 /* constraint */ param.constraint,
34441 /* defaultType */ ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
34442 }
34443 else {
34444 return param;
34445 }
34446 });
34447 }
34448
34449 /**
34450 * @license
34451 * Copyright Google LLC All Rights Reserved.
34452 *
34453 * Use of this source code is governed by an MIT-style license that can be
34454 * found in the LICENSE file at https://angular.io/license
34455 */
34456 /**
34457 * A context which hosts one or more Type Check Blocks (TCBs).
34458 *
34459 * An `Environment` supports the generation of TCBs by tracking necessary imports, declarations of
34460 * type constructors, and other statements beyond the type-checking code within the TCB itself.
34461 * Through method calls on `Environment`, the TCB generator can request `ts.Expression`s which
34462 * reference declarations in the `Environment` for these artifacts`.
34463 *
34464 * `Environment` can be used in a standalone fashion, or can be extended to support more specialized
34465 * usage.
34466 */
34467 class Environment {
34468 constructor(config, importManager, refEmitter, reflector, contextFile) {
34469 this.config = config;
34470 this.importManager = importManager;
34471 this.refEmitter = refEmitter;
34472 this.reflector = reflector;
34473 this.contextFile = contextFile;
34474 this.nextIds = {
34475 pipeInst: 1,
34476 typeCtor: 1,
34477 };
34478 this.typeCtors = new Map();
34479 this.typeCtorStatements = [];
34480 this.pipeInsts = new Map();
34481 this.pipeInstStatements = [];
34482 }
34483 /**
34484 * Get an expression referring to a type constructor for the given directive.
34485 *
34486 * Depending on the shape of the directive itself, this could be either a reference to a declared
34487 * type constructor, or to an inline type constructor.
34488 */
34489 typeCtorFor(dir) {
34490 const dirRef = dir.ref;
34491 const node = dirRef.node;
34492 if (this.typeCtors.has(node)) {
34493 return this.typeCtors.get(node);
34494 }
34495 if (requiresInlineTypeCtor(node, this.reflector)) {
34496 // The constructor has already been created inline, we just need to construct a reference to
34497 // it.
34498 const ref = this.reference(dirRef);
34499 const typeCtorExpr = ts$1.createPropertyAccess(ref, 'ngTypeCtor');
34500 this.typeCtors.set(node, typeCtorExpr);
34501 return typeCtorExpr;
34502 }
34503 else {
34504 const fnName = `_ctor${this.nextIds.typeCtor++}`;
34505 const nodeTypeRef = this.referenceType(dirRef);
34506 if (!ts$1.isTypeReferenceNode(nodeTypeRef)) {
34507 throw new Error(`Expected TypeReferenceNode from reference to ${dirRef.debugName}`);
34508 }
34509 const meta = {
34510 fnName,
34511 body: true,
34512 fields: {
34513 inputs: dir.inputs.classPropertyNames,
34514 outputs: dir.outputs.classPropertyNames,
34515 // TODO: support queries
34516 queries: dir.queries,
34517 },
34518 coercedInputFields: dir.coercedInputFields,
34519 };
34520 const typeParams = this.emitTypeParameters(node);
34521 const typeCtor = generateTypeCtorDeclarationFn(node, meta, nodeTypeRef.typeName, typeParams, this.reflector);
34522 this.typeCtorStatements.push(typeCtor);
34523 const fnId = ts$1.createIdentifier(fnName);
34524 this.typeCtors.set(node, fnId);
34525 return fnId;
34526 }
34527 }
34528 /*
34529 * Get an expression referring to an instance of the given pipe.
34530 */
34531 pipeInst(ref) {
34532 if (this.pipeInsts.has(ref.node)) {
34533 return this.pipeInsts.get(ref.node);
34534 }
34535 const pipeType = this.referenceType(ref);
34536 const pipeInstId = ts$1.createIdentifier(`_pipe${this.nextIds.pipeInst++}`);
34537 this.pipeInstStatements.push(tsDeclareVariable(pipeInstId, pipeType));
34538 this.pipeInsts.set(ref.node, pipeInstId);
34539 return pipeInstId;
34540 }
34541 /**
34542 * Generate a `ts.Expression` that references the given node.
34543 *
34544 * This may involve importing the node into the file if it's not declared there already.
34545 */
34546 reference(ref) {
34547 // Disable aliasing for imports generated in a template type-checking context, as there is no
34548 // guarantee that any alias re-exports exist in the .d.ts files. It's safe to use direct imports
34549 // in these cases as there is no strict dependency checking during the template type-checking
34550 // pass.
34551 const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing);
34552 // Use `translateExpression` to convert the `Expression` into a `ts.Expression`.
34553 return translateExpression(ngExpr, this.importManager);
34554 }
34555 /**
34556 * Generate a `ts.TypeNode` that references the given node as a type.
34557 *
34558 * This may involve importing the node into the file if it's not declared there already.
34559 */
34560 referenceType(ref) {
34561 const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing | ImportFlags.AllowTypeImports);
34562 // Create an `ExpressionType` from the `Expression` and translate it via `translateType`.
34563 // TODO(alxhub): support references to types with generic arguments in a clean way.
34564 return translateType(new ExpressionType(ngExpr), this.importManager);
34565 }
34566 emitTypeParameters(declaration) {
34567 const emitter = new TypeParameterEmitter(declaration.typeParameters, this.reflector);
34568 return emitter.emit(ref => this.referenceType(ref));
34569 }
34570 /**
34571 * Generate a `ts.TypeNode` that references a given type from the provided module.
34572 *
34573 * This will involve importing the type into the file, and will also add type parameters if
34574 * provided.
34575 */
34576 referenceExternalType(moduleName, name, typeParams) {
34577 const external = new ExternalExpr({ moduleName, name });
34578 return translateType(new ExpressionType(external, [ /* modifiers */], typeParams), this.importManager);
34579 }
34580 getPreludeStatements() {
34581 return [
34582 ...this.pipeInstStatements,
34583 ...this.typeCtorStatements,
34584 ];
34585 }
34586 }
34587
34588 /**
34589 * @license
34590 * Copyright Google LLC All Rights Reserved.
34591 *
34592 * Use of this source code is governed by an MIT-style license that can be
34593 * found in the LICENSE file at https://angular.io/license
34594 */
34595 class OutOfBandDiagnosticRecorderImpl {
34596 constructor(resolver) {
34597 this.resolver = resolver;
34598 this._diagnostics = [];
34599 /**
34600 * Tracks which `BindingPipe` nodes have already been recorded as invalid, so only one diagnostic
34601 * is ever produced per node.
34602 */
34603 this.recordedPipes = new Set();
34604 }
34605 get diagnostics() {
34606 return this._diagnostics;
34607 }
34608 missingReferenceTarget(templateId, ref) {
34609 const mapping = this.resolver.getSourceMapping(templateId);
34610 const value = ref.value.trim();
34611 const errorMsg = `No directive found with exportAs '${value}'.`;
34612 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, ref.valueSpan || ref.sourceSpan, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.MISSING_REFERENCE_TARGET), errorMsg));
34613 }
34614 missingPipe(templateId, ast) {
34615 if (this.recordedPipes.has(ast)) {
34616 return;
34617 }
34618 const mapping = this.resolver.getSourceMapping(templateId);
34619 const errorMsg = `No pipe found with name '${ast.name}'.`;
34620 const sourceSpan = this.resolver.toParseSourceSpan(templateId, ast.nameSpan);
34621 if (sourceSpan === null) {
34622 throw new Error(`Assertion failure: no SourceLocation found for usage of pipe '${ast.name}'.`);
34623 }
34624 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, sourceSpan, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.MISSING_PIPE), errorMsg));
34625 this.recordedPipes.add(ast);
34626 }
34627 illegalAssignmentToTemplateVar(templateId, assignment, target) {
34628 const mapping = this.resolver.getSourceMapping(templateId);
34629 const errorMsg = `Cannot use variable '${assignment
34630 .name}' as the left-hand side of an assignment expression. Template variables are read-only.`;
34631 const sourceSpan = this.resolver.toParseSourceSpan(templateId, assignment.sourceSpan);
34632 if (sourceSpan === null) {
34633 throw new Error(`Assertion failure: no SourceLocation found for property binding.`);
34634 }
34635 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, sourceSpan, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.WRITE_TO_READ_ONLY_VARIABLE), errorMsg, {
34636 text: `The variable ${assignment.name} is declared here.`,
34637 span: target.valueSpan || target.sourceSpan,
34638 }));
34639 }
34640 duplicateTemplateVar(templateId, variable, firstDecl) {
34641 const mapping = this.resolver.getSourceMapping(templateId);
34642 const errorMsg = `Cannot redeclare variable '${variable.name}' as it was previously declared elsewhere for the same template.`;
34643 // The allocation of the error here is pretty useless for variables declared in microsyntax,
34644 // since the sourceSpan refers to the entire microsyntax property, not a span for the specific
34645 // variable in question.
34646 //
34647 // TODO(alxhub): allocate to a tighter span once one is available.
34648 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, variable.sourceSpan, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.DUPLICATE_VARIABLE_DECLARATION), errorMsg, {
34649 text: `The variable '${firstDecl.name}' was first declared here.`,
34650 span: firstDecl.sourceSpan,
34651 }));
34652 }
34653 requiresInlineTcb(templateId, node) {
34654 this._diagnostics.push(makeInlineDiagnostic(templateId, ErrorCode.INLINE_TCB_REQUIRED, node.name, `This component requires inline template type-checking, which is not supported by the current environment.`));
34655 }
34656 requiresInlineTypeConstructors(templateId, node, directives) {
34657 let message;
34658 if (directives.length > 1) {
34659 message =
34660 `This component uses directives which require inline type constructors, which are not supported by the current environment.`;
34661 }
34662 else {
34663 message =
34664 `This component uses a directive which requires an inline type constructor, which is not supported by the current environment.`;
34665 }
34666 this._diagnostics.push(makeInlineDiagnostic(templateId, ErrorCode.INLINE_TYPE_CTOR_REQUIRED, node.name, message, directives.map(dir => makeRelatedInformation(dir.name, `Requires an inline type constructor.`))));
34667 }
34668 }
34669 function makeInlineDiagnostic(templateId, code, node, messageText, relatedInformation) {
34670 return Object.assign(Object.assign({}, makeDiagnostic(code, node, messageText, relatedInformation)), { componentFile: node.getSourceFile(), templateId });
34671 }
34672
34673 /**
34674 * @license
34675 * Copyright Google LLC All Rights Reserved.
34676 *
34677 * Use of this source code is governed by an MIT-style license that can be
34678 * found in the LICENSE file at https://angular.io/license
34679 */
34680 function requiresInlineTypeCheckBlock(node, usedPipes) {
34681 // In order to qualify for a declared TCB (not inline) two conditions must be met:
34682 // 1) the class must be exported
34683 // 2) it must not have constrained generic types
34684 if (!checkIfClassIsExported(node)) {
34685 // Condition 1 is false, the class is not exported.
34686 return true;
34687 }
34688 else if (!checkIfGenericTypesAreUnbound(node)) {
34689 // Condition 2 is false, the class has constrained generic types
34690 return true;
34691 }
34692 else if (Array.from(usedPipes.values())
34693 .some(pipeRef => !checkIfClassIsExported(pipeRef.node))) {
34694 // If one of the pipes used by the component is not exported, a non-inline TCB will not be able
34695 // to import it, so this requires an inline TCB.
34696 return true;
34697 }
34698 else {
34699 return false;
34700 }
34701 }
34702 /** Maps a shim position back to a template location. */
34703 function getTemplateMapping(shimSf, position, resolver, isDiagnosticRequest) {
34704 const node = getTokenAtPosition(shimSf, position);
34705 const sourceLocation = findSourceLocation(node, shimSf, isDiagnosticRequest);
34706 if (sourceLocation === null) {
34707 return null;
34708 }
34709 const mapping = resolver.getSourceMapping(sourceLocation.id);
34710 const span = resolver.toParseSourceSpan(sourceLocation.id, sourceLocation.span);
34711 if (span === null) {
34712 return null;
34713 }
34714 // TODO(atscott): Consider adding a context span by walking up from `node` until we get a
34715 // different span.
34716 return { sourceLocation, templateSourceMapping: mapping, span };
34717 }
34718 function findTypeCheckBlock(file, id, isDiagnosticRequest) {
34719 for (const stmt of file.statements) {
34720 if (ts$1.isFunctionDeclaration(stmt) && getTemplateId$1(stmt, file, isDiagnosticRequest) === id) {
34721 return stmt;
34722 }
34723 }
34724 return null;
34725 }
34726 /**
34727 * Traverses up the AST starting from the given node to extract the source location from comments
34728 * that have been emitted into the TCB. If the node does not exist within a TCB, or if an ignore
34729 * marker comment is found up the tree (and this is part of a diagnostic request), this function
34730 * returns null.
34731 */
34732 function findSourceLocation(node, sourceFile, isDiagnosticsRequest) {
34733 // Search for comments until the TCB's function declaration is encountered.
34734 while (node !== undefined && !ts$1.isFunctionDeclaration(node)) {
34735 if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticsRequest) {
34736 // There's an ignore marker on this node, so the diagnostic should not be reported.
34737 return null;
34738 }
34739 const span = readSpanComment(node, sourceFile);
34740 if (span !== null) {
34741 // Once the positional information has been extracted, search further up the TCB to extract
34742 // the unique id that is attached with the TCB's function declaration.
34743 const id = getTemplateId$1(node, sourceFile, isDiagnosticsRequest);
34744 if (id === null) {
34745 return null;
34746 }
34747 return { id, span };
34748 }
34749 node = node.parent;
34750 }
34751 return null;
34752 }
34753 function getTemplateId$1(node, sourceFile, isDiagnosticRequest) {
34754 // Walk up to the function declaration of the TCB, the file information is attached there.
34755 while (!ts$1.isFunctionDeclaration(node)) {
34756 if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticRequest) {
34757 // There's an ignore marker on this node, so the diagnostic should not be reported.
34758 return null;
34759 }
34760 node = node.parent;
34761 // Bail once we have reached the root.
34762 if (node === undefined) {
34763 return null;
34764 }
34765 }
34766 const start = node.getFullStart();
34767 return ts$1.forEachLeadingCommentRange(sourceFile.text, start, (pos, end, kind) => {
34768 if (kind !== ts$1.SyntaxKind.MultiLineCommentTrivia) {
34769 return null;
34770 }
34771 const commentText = sourceFile.text.substring(pos + 2, end - 2);
34772 return commentText;
34773 }) || null;
34774 }
34775
34776 /**
34777 * @license
34778 * Copyright Google LLC All Rights Reserved.
34779 *
34780 * Use of this source code is governed by an MIT-style license that can be
34781 * found in the LICENSE file at https://angular.io/license
34782 */
34783 /**
34784 * Wraps the node in parenthesis such that inserted span comments become attached to the proper
34785 * node. This is an alias for `ts.createParen` with the benefit that it signifies that the
34786 * inserted parenthesis are for diagnostic purposes, not for correctness of the rendered TCB code.
34787 *
34788 * Note that it is important that nodes and its attached comment are not wrapped into parenthesis
34789 * by default, as it prevents correct translation of e.g. diagnostics produced for incorrect method
34790 * arguments. Such diagnostics would then be produced for the parenthesised node whereas the
34791 * positional comment would be located within that node, resulting in a mismatch.
34792 */
34793 function wrapForDiagnostics(expr) {
34794 return ts$1.createParen(expr);
34795 }
34796 /**
34797 * Wraps the node in parenthesis such that inserted span comments become attached to the proper
34798 * node. This is an alias for `ts.createParen` with the benefit that it signifies that the
34799 * inserted parenthesis are for use by the type checker, not for correctness of the rendered TCB
34800 * code.
34801 */
34802 function wrapForTypeChecker(expr) {
34803 return ts$1.createParen(expr);
34804 }
34805 /**
34806 * Adds a synthetic comment to the expression that represents the parse span of the provided node.
34807 * This comment can later be retrieved as trivia of a node to recover original source locations.
34808 */
34809 function addParseSpanInfo(node, span) {
34810 let commentText;
34811 if (span instanceof AbsoluteSourceSpan) {
34812 commentText = `${span.start},${span.end}`;
34813 }
34814 else {
34815 commentText = `${span.start.offset},${span.end.offset}`;
34816 }
34817 ts$1.addSyntheticTrailingComment(node, ts$1.SyntaxKind.MultiLineCommentTrivia, commentText, /* hasTrailingNewLine */ false);
34818 }
34819 /**
34820 * Adds a synthetic comment to the function declaration that contains the template id
34821 * of the class declaration.
34822 */
34823 function addTemplateId(tcb, id) {
34824 ts$1.addSyntheticLeadingComment(tcb, ts$1.SyntaxKind.MultiLineCommentTrivia, id, true);
34825 }
34826 /**
34827 * Determines if the diagnostic should be reported. Some diagnostics are produced because of the
34828 * way TCBs are generated; those diagnostics should not be reported as type check errors of the
34829 * template.
34830 */
34831 function shouldReportDiagnostic(diagnostic) {
34832 const { code } = diagnostic;
34833 if (code === 6133 /* $var is declared but its value is never read. */) {
34834 return false;
34835 }
34836 else if (code === 6199 /* All variables are unused. */) {
34837 return false;
34838 }
34839 else if (code === 2695 /* Left side of comma operator is unused and has no side effects. */) {
34840 return false;
34841 }
34842 else if (code === 7006 /* Parameter '$event' implicitly has an 'any' type. */) {
34843 return false;
34844 }
34845 return true;
34846 }
34847 /**
34848 * Attempts to translate a TypeScript diagnostic produced during template type-checking to their
34849 * location of origin, based on the comments that are emitted in the TCB code.
34850 *
34851 * If the diagnostic could not be translated, `null` is returned to indicate that the diagnostic
34852 * should not be reported at all. This prevents diagnostics from non-TCB code in a user's source
34853 * file from being reported as type-check errors.
34854 */
34855 function translateDiagnostic(diagnostic, resolver) {
34856 if (diagnostic.file === undefined || diagnostic.start === undefined) {
34857 return null;
34858 }
34859 const fullMapping = getTemplateMapping(diagnostic.file, diagnostic.start, resolver, /*isDiagnosticsRequest*/ true);
34860 if (fullMapping === null) {
34861 return null;
34862 }
34863 const { sourceLocation, templateSourceMapping, span } = fullMapping;
34864 return makeTemplateDiagnostic(sourceLocation.id, templateSourceMapping, span, diagnostic.category, diagnostic.code, diagnostic.messageText);
34865 }
34866
34867 /**
34868 * @license
34869 * Copyright Google LLC All Rights Reserved.
34870 *
34871 * Use of this source code is governed by an MIT-style license that can be
34872 * found in the LICENSE file at https://angular.io/license
34873 */
34874 const NULL_AS_ANY = ts$1.createAsExpression(ts$1.createNull(), ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
34875 const UNDEFINED = ts$1.createIdentifier('undefined');
34876 const UNARY_OPS = new Map([
34877 ['+', ts$1.SyntaxKind.PlusToken],
34878 ['-', ts$1.SyntaxKind.MinusToken],
34879 ]);
34880 const BINARY_OPS = new Map([
34881 ['+', ts$1.SyntaxKind.PlusToken],
34882 ['-', ts$1.SyntaxKind.MinusToken],
34883 ['<', ts$1.SyntaxKind.LessThanToken],
34884 ['>', ts$1.SyntaxKind.GreaterThanToken],
34885 ['<=', ts$1.SyntaxKind.LessThanEqualsToken],
34886 ['>=', ts$1.SyntaxKind.GreaterThanEqualsToken],
34887 ['==', ts$1.SyntaxKind.EqualsEqualsToken],
34888 ['===', ts$1.SyntaxKind.EqualsEqualsEqualsToken],
34889 ['*', ts$1.SyntaxKind.AsteriskToken],
34890 ['/', ts$1.SyntaxKind.SlashToken],
34891 ['%', ts$1.SyntaxKind.PercentToken],
34892 ['!=', ts$1.SyntaxKind.ExclamationEqualsToken],
34893 ['!==', ts$1.SyntaxKind.ExclamationEqualsEqualsToken],
34894 ['||', ts$1.SyntaxKind.BarBarToken],
34895 ['&&', ts$1.SyntaxKind.AmpersandAmpersandToken],
34896 ['&', ts$1.SyntaxKind.AmpersandToken],
34897 ['|', ts$1.SyntaxKind.BarToken],
34898 ]);
34899 /**
34900 * Convert an `AST` to TypeScript code directly, without going through an intermediate `Expression`
34901 * AST.
34902 */
34903 function astToTypescript(ast, maybeResolve, config) {
34904 const translator = new AstTranslator(maybeResolve, config);
34905 return translator.translate(ast);
34906 }
34907 class AstTranslator {
34908 constructor(maybeResolve, config) {
34909 this.maybeResolve = maybeResolve;
34910 this.config = config;
34911 }
34912 translate(ast) {
34913 // Skip over an `ASTWithSource` as its `visit` method calls directly into its ast's `visit`,
34914 // which would prevent any custom resolution through `maybeResolve` for that node.
34915 if (ast instanceof ASTWithSource) {
34916 ast = ast.ast;
34917 }
34918 // The `EmptyExpr` doesn't have a dedicated method on `AstVisitor`, so it's special cased here.
34919 if (ast instanceof EmptyExpr) {
34920 return UNDEFINED;
34921 }
34922 // First attempt to let any custom resolution logic provide a translation for the given node.
34923 const resolved = this.maybeResolve(ast);
34924 if (resolved !== null) {
34925 return resolved;
34926 }
34927 return ast.visit(this);
34928 }
34929 visitUnary(ast) {
34930 const expr = this.translate(ast.expr);
34931 const op = UNARY_OPS.get(ast.operator);
34932 if (op === undefined) {
34933 throw new Error(`Unsupported Unary.operator: ${ast.operator}`);
34934 }
34935 const node = wrapForDiagnostics(ts$1.createPrefix(op, expr));
34936 addParseSpanInfo(node, ast.sourceSpan);
34937 return node;
34938 }
34939 visitBinary(ast) {
34940 const lhs = wrapForDiagnostics(this.translate(ast.left));
34941 const rhs = wrapForDiagnostics(this.translate(ast.right));
34942 const op = BINARY_OPS.get(ast.operation);
34943 if (op === undefined) {
34944 throw new Error(`Unsupported Binary.operation: ${ast.operation}`);
34945 }
34946 const node = ts$1.createBinary(lhs, op, rhs);
34947 addParseSpanInfo(node, ast.sourceSpan);
34948 return node;
34949 }
34950 visitChain(ast) {
34951 const elements = ast.expressions.map(expr => this.translate(expr));
34952 const node = wrapForDiagnostics(ts$1.createCommaList(elements));
34953 addParseSpanInfo(node, ast.sourceSpan);
34954 return node;
34955 }
34956 visitConditional(ast) {
34957 const condExpr = this.translate(ast.condition);
34958 const trueExpr = this.translate(ast.trueExp);
34959 // Wrap `falseExpr` in parens so that the trailing parse span info is not attributed to the
34960 // whole conditional.
34961 // In the following example, the last source span comment (5,6) could be seen as the
34962 // trailing comment for _either_ the whole conditional expression _or_ just the `falseExpr` that
34963 // is immediately before it:
34964 // `conditional /*1,2*/ ? trueExpr /*3,4*/ : falseExpr /*5,6*/`
34965 // This should be instead be `conditional /*1,2*/ ? trueExpr /*3,4*/ : (falseExpr /*5,6*/)`
34966 const falseExpr = wrapForTypeChecker(this.translate(ast.falseExp));
34967 const node = ts$1.createParen(ts$1.createConditional(condExpr, trueExpr, falseExpr));
34968 addParseSpanInfo(node, ast.sourceSpan);
34969 return node;
34970 }
34971 visitFunctionCall(ast) {
34972 const receiver = wrapForDiagnostics(this.translate(ast.target));
34973 const args = ast.args.map(expr => this.translate(expr));
34974 const node = ts$1.createCall(receiver, undefined, args);
34975 addParseSpanInfo(node, ast.sourceSpan);
34976 return node;
34977 }
34978 visitImplicitReceiver(ast) {
34979 throw new Error('Method not implemented.');
34980 }
34981 visitThisReceiver(ast) {
34982 throw new Error('Method not implemented.');
34983 }
34984 visitInterpolation(ast) {
34985 // Build up a chain of binary + operations to simulate the string concatenation of the
34986 // interpolation's expressions. The chain is started using an actual string literal to ensure
34987 // the type is inferred as 'string'.
34988 return ast.expressions.reduce((lhs, ast) => ts$1.createBinary(lhs, ts$1.SyntaxKind.PlusToken, wrapForTypeChecker(this.translate(ast))), ts$1.createLiteral(''));
34989 }
34990 visitKeyedRead(ast) {
34991 const receiver = wrapForDiagnostics(this.translate(ast.obj));
34992 const key = this.translate(ast.key);
34993 const node = ts$1.createElementAccess(receiver, key);
34994 addParseSpanInfo(node, ast.sourceSpan);
34995 return node;
34996 }
34997 visitKeyedWrite(ast) {
34998 const receiver = wrapForDiagnostics(this.translate(ast.obj));
34999 const left = ts$1.createElementAccess(receiver, this.translate(ast.key));
35000 // TODO(joost): annotate `left` with the span of the element access, which is not currently
35001 // available on `ast`.
35002 const right = wrapForTypeChecker(this.translate(ast.value));
35003 const node = wrapForDiagnostics(ts$1.createBinary(left, ts$1.SyntaxKind.EqualsToken, right));
35004 addParseSpanInfo(node, ast.sourceSpan);
35005 return node;
35006 }
35007 visitLiteralArray(ast) {
35008 const elements = ast.expressions.map(expr => this.translate(expr));
35009 const literal = ts$1.createArrayLiteral(elements);
35010 // If strictLiteralTypes is disabled, array literals are cast to `any`.
35011 const node = this.config.strictLiteralTypes ? literal : tsCastToAny(literal);
35012 addParseSpanInfo(node, ast.sourceSpan);
35013 return node;
35014 }
35015 visitLiteralMap(ast) {
35016 const properties = ast.keys.map(({ key }, idx) => {
35017 const value = this.translate(ast.values[idx]);
35018 return ts$1.createPropertyAssignment(ts$1.createStringLiteral(key), value);
35019 });
35020 const literal = ts$1.createObjectLiteral(properties, true);
35021 // If strictLiteralTypes is disabled, object literals are cast to `any`.
35022 const node = this.config.strictLiteralTypes ? literal : tsCastToAny(literal);
35023 addParseSpanInfo(node, ast.sourceSpan);
35024 return node;
35025 }
35026 visitLiteralPrimitive(ast) {
35027 let node;
35028 if (ast.value === undefined) {
35029 node = ts$1.createIdentifier('undefined');
35030 }
35031 else if (ast.value === null) {
35032 node = ts$1.createNull();
35033 }
35034 else {
35035 node = ts$1.createLiteral(ast.value);
35036 }
35037 addParseSpanInfo(node, ast.sourceSpan);
35038 return node;
35039 }
35040 visitMethodCall(ast) {
35041 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
35042 const method = ts$1.createPropertyAccess(receiver, ast.name);
35043 addParseSpanInfo(method, ast.nameSpan);
35044 const args = ast.args.map(expr => this.translate(expr));
35045 const node = ts$1.createCall(method, undefined, args);
35046 addParseSpanInfo(node, ast.sourceSpan);
35047 return node;
35048 }
35049 visitNonNullAssert(ast) {
35050 const expr = wrapForDiagnostics(this.translate(ast.expression));
35051 const node = ts$1.createNonNullExpression(expr);
35052 addParseSpanInfo(node, ast.sourceSpan);
35053 return node;
35054 }
35055 visitPipe(ast) {
35056 throw new Error('Method not implemented.');
35057 }
35058 visitPrefixNot(ast) {
35059 const expression = wrapForDiagnostics(this.translate(ast.expression));
35060 const node = ts$1.createLogicalNot(expression);
35061 addParseSpanInfo(node, ast.sourceSpan);
35062 return node;
35063 }
35064 visitPropertyRead(ast) {
35065 // This is a normal property read - convert the receiver to an expression and emit the correct
35066 // TypeScript expression to read the property.
35067 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
35068 const name = ts$1.createPropertyAccess(receiver, ast.name);
35069 addParseSpanInfo(name, ast.nameSpan);
35070 const node = wrapForDiagnostics(name);
35071 addParseSpanInfo(node, ast.sourceSpan);
35072 return node;
35073 }
35074 visitPropertyWrite(ast) {
35075 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
35076 const left = ts$1.createPropertyAccess(receiver, ast.name);
35077 addParseSpanInfo(left, ast.nameSpan);
35078 // TypeScript reports assignment errors on the entire lvalue expression. Annotate the lvalue of
35079 // the assignment with the sourceSpan, which includes receivers, rather than nameSpan for
35080 // consistency of the diagnostic location.
35081 // a.b.c = 1
35082 // ^^^^^^^^^ sourceSpan
35083 // ^ nameSpan
35084 const leftWithPath = wrapForDiagnostics(left);
35085 addParseSpanInfo(leftWithPath, ast.sourceSpan);
35086 // The right needs to be wrapped in parens as well or we cannot accurately match its
35087 // span to just the RHS. For example, the span in `e = $event /*0,10*/` is ambiguous.
35088 // It could refer to either the whole binary expression or just the RHS.
35089 // We should instead generate `e = ($event /*0,10*/)` so we know the span 0,10 matches RHS.
35090 const right = wrapForTypeChecker(this.translate(ast.value));
35091 const node = wrapForDiagnostics(ts$1.createBinary(leftWithPath, ts$1.SyntaxKind.EqualsToken, right));
35092 addParseSpanInfo(node, ast.sourceSpan);
35093 return node;
35094 }
35095 visitQuote(ast) {
35096 return NULL_AS_ANY;
35097 }
35098 visitSafeMethodCall(ast) {
35099 // See the comments in SafePropertyRead above for an explanation of the cases here.
35100 let node;
35101 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
35102 const args = ast.args.map(expr => this.translate(expr));
35103 if (this.config.strictSafeNavigationTypes) {
35104 // "a?.method(...)" becomes (null as any ? a!.method(...) : undefined)
35105 const method = ts$1.createPropertyAccess(ts$1.createNonNullExpression(receiver), ast.name);
35106 addParseSpanInfo(method, ast.nameSpan);
35107 const call = ts$1.createCall(method, undefined, args);
35108 node = ts$1.createParen(ts$1.createConditional(NULL_AS_ANY, call, UNDEFINED));
35109 }
35110 else if (VeSafeLhsInferenceBugDetector.veWillInferAnyFor(ast)) {
35111 // "a?.method(...)" becomes (a as any).method(...)
35112 const method = ts$1.createPropertyAccess(tsCastToAny(receiver), ast.name);
35113 addParseSpanInfo(method, ast.nameSpan);
35114 node = ts$1.createCall(method, undefined, args);
35115 }
35116 else {
35117 // "a?.method(...)" becomes (a!.method(...) as any)
35118 const method = ts$1.createPropertyAccess(ts$1.createNonNullExpression(receiver), ast.name);
35119 addParseSpanInfo(method, ast.nameSpan);
35120 node = tsCastToAny(ts$1.createCall(method, undefined, args));
35121 }
35122 addParseSpanInfo(node, ast.sourceSpan);
35123 return node;
35124 }
35125 visitSafePropertyRead(ast) {
35126 let node;
35127 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
35128 // The form of safe property reads depends on whether strictness is in use.
35129 if (this.config.strictSafeNavigationTypes) {
35130 // Basically, the return here is either the type of the complete expression with a null-safe
35131 // property read, or `undefined`. So a ternary is used to create an "or" type:
35132 // "a?.b" becomes (null as any ? a!.b : undefined)
35133 // The type of this expression is (typeof a!.b) | undefined, which is exactly as desired.
35134 const expr = ts$1.createPropertyAccess(ts$1.createNonNullExpression(receiver), ast.name);
35135 addParseSpanInfo(expr, ast.nameSpan);
35136 node = ts$1.createParen(ts$1.createConditional(NULL_AS_ANY, expr, UNDEFINED));
35137 }
35138 else if (VeSafeLhsInferenceBugDetector.veWillInferAnyFor(ast)) {
35139 // Emulate a View Engine bug where 'any' is inferred for the left-hand side of the safe
35140 // navigation operation. With this bug, the type of the left-hand side is regarded as any.
35141 // Therefore, the left-hand side only needs repeating in the output (to validate it), and then
35142 // 'any' is used for the rest of the expression. This is done using a comma operator:
35143 // "a?.b" becomes (a as any).b, which will of course have type 'any'.
35144 node = ts$1.createPropertyAccess(tsCastToAny(receiver), ast.name);
35145 }
35146 else {
35147 // The View Engine bug isn't active, so check the entire type of the expression, but the final
35148 // result is still inferred as `any`.
35149 // "a?.b" becomes (a!.b as any)
35150 const expr = ts$1.createPropertyAccess(ts$1.createNonNullExpression(receiver), ast.name);
35151 addParseSpanInfo(expr, ast.nameSpan);
35152 node = tsCastToAny(expr);
35153 }
35154 addParseSpanInfo(node, ast.sourceSpan);
35155 return node;
35156 }
35157 }
35158 /**
35159 * Checks whether View Engine will infer a type of 'any' for the left-hand side of a safe navigation
35160 * operation.
35161 *
35162 * In View Engine's template type-checker, certain receivers of safe navigation operations will
35163 * cause a temporary variable to be allocated as part of the checking expression, to save the value
35164 * of the receiver and use it more than once in the expression. This temporary variable has type
35165 * 'any'. In practice, this means certain receivers cause View Engine to not check the full
35166 * expression, and other receivers will receive more complete checking.
35167 *
35168 * For compatibility, this logic is adapted from View Engine's expression_converter.ts so that the
35169 * Ivy checker can emulate this bug when needed.
35170 */
35171 class VeSafeLhsInferenceBugDetector {
35172 static veWillInferAnyFor(ast) {
35173 return ast.receiver.visit(VeSafeLhsInferenceBugDetector.SINGLETON);
35174 }
35175 visitUnary(ast) {
35176 return ast.expr.visit(this);
35177 }
35178 visitBinary(ast) {
35179 return ast.left.visit(this) || ast.right.visit(this);
35180 }
35181 visitChain(ast) {
35182 return false;
35183 }
35184 visitConditional(ast) {
35185 return ast.condition.visit(this) || ast.trueExp.visit(this) || ast.falseExp.visit(this);
35186 }
35187 visitFunctionCall(ast) {
35188 return true;
35189 }
35190 visitImplicitReceiver(ast) {
35191 return false;
35192 }
35193 visitThisReceiver(ast) {
35194 return false;
35195 }
35196 visitInterpolation(ast) {
35197 return ast.expressions.some(exp => exp.visit(this));
35198 }
35199 visitKeyedRead(ast) {
35200 return false;
35201 }
35202 visitKeyedWrite(ast) {
35203 return false;
35204 }
35205 visitLiteralArray(ast) {
35206 return true;
35207 }
35208 visitLiteralMap(ast) {
35209 return true;
35210 }
35211 visitLiteralPrimitive(ast) {
35212 return false;
35213 }
35214 visitMethodCall(ast) {
35215 return true;
35216 }
35217 visitPipe(ast) {
35218 return true;
35219 }
35220 visitPrefixNot(ast) {
35221 return ast.expression.visit(this);
35222 }
35223 visitNonNullAssert(ast) {
35224 return ast.expression.visit(this);
35225 }
35226 visitPropertyRead(ast) {
35227 return false;
35228 }
35229 visitPropertyWrite(ast) {
35230 return false;
35231 }
35232 visitQuote(ast) {
35233 return false;
35234 }
35235 visitSafeMethodCall(ast) {
35236 return true;
35237 }
35238 visitSafePropertyRead(ast) {
35239 return false;
35240 }
35241 }
35242 VeSafeLhsInferenceBugDetector.SINGLETON = new VeSafeLhsInferenceBugDetector();
35243
35244 /**
35245 * @license
35246 * Copyright Google LLC All Rights Reserved.
35247 *
35248 * Use of this source code is governed by an MIT-style license that can be
35249 * found in the LICENSE file at https://angular.io/license
35250 */
35251 /**
35252 * Visits a template and records any semantic errors within its expressions.
35253 */
35254 class ExpressionSemanticVisitor extends RecursiveAstVisitor {
35255 constructor(templateId, boundTarget, oob) {
35256 super();
35257 this.templateId = templateId;
35258 this.boundTarget = boundTarget;
35259 this.oob = oob;
35260 }
35261 visitPropertyWrite(ast, context) {
35262 super.visitPropertyWrite(ast, context);
35263 if (!(ast.receiver instanceof ImplicitReceiver)) {
35264 return;
35265 }
35266 const target = this.boundTarget.getExpressionTarget(ast);
35267 if (target instanceof Variable) {
35268 // Template variables are read-only.
35269 this.oob.illegalAssignmentToTemplateVar(this.templateId, ast, target);
35270 }
35271 }
35272 static visit(ast, id, boundTarget, oob) {
35273 ast.visit(new ExpressionSemanticVisitor(id, boundTarget, oob));
35274 }
35275 }
35276
35277 /**
35278 * @license
35279 * Copyright Google LLC All Rights Reserved.
35280 *
35281 * Use of this source code is governed by an MIT-style license that can be
35282 * found in the LICENSE file at https://angular.io/license
35283 */
35284 /**
35285 * Given a `ts.ClassDeclaration` for a component, and metadata regarding that component, compose a
35286 * "type check block" function.
35287 *
35288 * When passed through TypeScript's TypeChecker, type errors that arise within the type check block
35289 * function indicate issues in the template itself.
35290 *
35291 * As a side effect of generating a TCB for the component, `ts.Diagnostic`s may also be produced
35292 * directly for issues within the template which are identified during generation. These issues are
35293 * recorded in either the `domSchemaChecker` (which checks usage of DOM elements and bindings) as
35294 * well as the `oobRecorder` (which records errors when the type-checking code generator is unable
35295 * to sufficiently understand a template).
35296 *
35297 * @param env an `Environment` into which type-checking code will be generated.
35298 * @param ref a `Reference` to the component class which should be type-checked.
35299 * @param name a `ts.Identifier` to use for the generated `ts.FunctionDeclaration`.
35300 * @param meta metadata about the component's template and the function being generated.
35301 * @param domSchemaChecker used to check and record errors regarding improper usage of DOM elements
35302 * and bindings.
35303 * @param oobRecorder used to record errors regarding template elements which could not be correctly
35304 * translated into types during TCB generation.
35305 */
35306 function generateTypeCheckBlock(env, ref, name, meta, domSchemaChecker, oobRecorder) {
35307 const tcb = new Context$1(env, domSchemaChecker, oobRecorder, meta.id, meta.boundTarget, meta.pipes, meta.schemas);
35308 const scope = Scope$1.forNodes(tcb, null, tcb.boundTarget.target.template, /* guard */ null);
35309 const ctxRawType = env.referenceType(ref);
35310 if (!ts$1.isTypeReferenceNode(ctxRawType)) {
35311 throw new Error(`Expected TypeReferenceNode when referencing the ctx param for ${ref.debugName}`);
35312 }
35313 const paramList = [tcbCtxParam(ref.node, ctxRawType.typeName, env.config.useContextGenericType)];
35314 const scopeStatements = scope.render();
35315 const innerBody = ts$1.createBlock([
35316 ...env.getPreludeStatements(),
35317 ...scopeStatements,
35318 ]);
35319 // Wrap the body in an "if (true)" expression. This is unnecessary but has the effect of causing
35320 // the `ts.Printer` to format the type-check block nicely.
35321 const body = ts$1.createBlock([ts$1.createIf(ts$1.createTrue(), innerBody, undefined)]);
35322 const fnDecl = ts$1.createFunctionDeclaration(
35323 /* decorators */ undefined,
35324 /* modifiers */ undefined,
35325 /* asteriskToken */ undefined,
35326 /* name */ name,
35327 /* typeParameters */ env.config.useContextGenericType ? ref.node.typeParameters : undefined,
35328 /* parameters */ paramList,
35329 /* type */ undefined,
35330 /* body */ body);
35331 addTemplateId(fnDecl, meta.id);
35332 return fnDecl;
35333 }
35334 /**
35335 * A code generation operation that's involved in the construction of a Type Check Block.
35336 *
35337 * The generation of a TCB is non-linear. Bindings within a template may result in the need to
35338 * construct certain types earlier than they otherwise would be constructed. That is, if the
35339 * generation of a TCB for a template is broken down into specific operations (constructing a
35340 * directive, extracting a variable from a let- operation, etc), then it's possible for operations
35341 * earlier in the sequence to depend on operations which occur later in the sequence.
35342 *
35343 * `TcbOp` abstracts the different types of operations which are required to convert a template into
35344 * a TCB. This allows for two phases of processing for the template, where 1) a linear sequence of
35345 * `TcbOp`s is generated, and then 2) these operations are executed, not necessarily in linear
35346 * order.
35347 *
35348 * Each `TcbOp` may insert statements into the body of the TCB, and also optionally return a
35349 * `ts.Expression` which can be used to reference the operation's result.
35350 */
35351 class TcbOp {
35352 /**
35353 * Replacement value or operation used while this `TcbOp` is executing (i.e. to resolve circular
35354 * references during its execution).
35355 *
35356 * This is usually a `null!` expression (which asks TS to infer an appropriate type), but another
35357 * `TcbOp` can be returned in cases where additional code generation is necessary to deal with
35358 * circular references.
35359 */
35360 circularFallback() {
35361 return INFER_TYPE_FOR_CIRCULAR_OP_EXPR;
35362 }
35363 }
35364 /**
35365 * A `TcbOp` which creates an expression for a native DOM element (or web component) from a
35366 * `TmplAstElement`.
35367 *
35368 * Executing this operation returns a reference to the element variable.
35369 */
35370 class TcbElementOp extends TcbOp {
35371 constructor(tcb, scope, element) {
35372 super();
35373 this.tcb = tcb;
35374 this.scope = scope;
35375 this.element = element;
35376 }
35377 get optional() {
35378 // The statement generated by this operation is only used for type-inference of the DOM
35379 // element's type and won't report diagnostics by itself, so the operation is marked as optional
35380 // to avoid generating statements for DOM elements that are never referenced.
35381 return true;
35382 }
35383 execute() {
35384 const id = this.tcb.allocateId();
35385 // Add the declaration of the element using document.createElement.
35386 const initializer = tsCreateElement(this.element.name);
35387 addParseSpanInfo(initializer, this.element.startSourceSpan || this.element.sourceSpan);
35388 this.scope.addStatement(tsCreateVariable(id, initializer));
35389 return id;
35390 }
35391 }
35392 /**
35393 * A `TcbOp` which creates an expression for particular let- `TmplAstVariable` on a
35394 * `TmplAstTemplate`'s context.
35395 *
35396 * Executing this operation returns a reference to the variable variable (lol).
35397 */
35398 class TcbVariableOp extends TcbOp {
35399 constructor(tcb, scope, template, variable) {
35400 super();
35401 this.tcb = tcb;
35402 this.scope = scope;
35403 this.template = template;
35404 this.variable = variable;
35405 }
35406 get optional() {
35407 return false;
35408 }
35409 execute() {
35410 // Look for a context variable for the template.
35411 const ctx = this.scope.resolve(this.template);
35412 // Allocate an identifier for the TmplAstVariable, and initialize it to a read of the variable
35413 // on the template context.
35414 const id = this.tcb.allocateId();
35415 const initializer = ts$1.createPropertyAccess(
35416 /* expression */ ctx,
35417 /* name */ this.variable.value || '$implicit');
35418 addParseSpanInfo(id, this.variable.keySpan);
35419 // Declare the variable, and return its identifier.
35420 let variable;
35421 if (this.variable.valueSpan !== undefined) {
35422 addParseSpanInfo(initializer, this.variable.valueSpan);
35423 variable = tsCreateVariable(id, wrapForTypeChecker(initializer));
35424 }
35425 else {
35426 variable = tsCreateVariable(id, initializer);
35427 }
35428 addParseSpanInfo(variable.declarationList.declarations[0], this.variable.sourceSpan);
35429 this.scope.addStatement(variable);
35430 return id;
35431 }
35432 }
35433 /**
35434 * A `TcbOp` which generates a variable for a `TmplAstTemplate`'s context.
35435 *
35436 * Executing this operation returns a reference to the template's context variable.
35437 */
35438 class TcbTemplateContextOp extends TcbOp {
35439 constructor(tcb, scope) {
35440 super();
35441 this.tcb = tcb;
35442 this.scope = scope;
35443 // The declaration of the context variable is only needed when the context is actually referenced.
35444 this.optional = true;
35445 }
35446 execute() {
35447 // Allocate a template ctx variable and declare it with an 'any' type. The type of this variable
35448 // may be narrowed as a result of template guard conditions.
35449 const ctx = this.tcb.allocateId();
35450 const type = ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword);
35451 this.scope.addStatement(tsDeclareVariable(ctx, type));
35452 return ctx;
35453 }
35454 }
35455 /**
35456 * A `TcbOp` which descends into a `TmplAstTemplate`'s children and generates type-checking code for
35457 * them.
35458 *
35459 * This operation wraps the children's type-checking code in an `if` block, which may include one
35460 * or more type guard conditions that narrow types within the template body.
35461 */
35462 class TcbTemplateBodyOp extends TcbOp {
35463 constructor(tcb, scope, template) {
35464 super();
35465 this.tcb = tcb;
35466 this.scope = scope;
35467 this.template = template;
35468 }
35469 get optional() {
35470 return false;
35471 }
35472 execute() {
35473 // An `if` will be constructed, within which the template's children will be type checked. The
35474 // `if` is used for two reasons: it creates a new syntactic scope, isolating variables declared
35475 // in the template's TCB from the outer context, and it allows any directives on the templates
35476 // to perform type narrowing of either expressions or the template's context.
35477 //
35478 // The guard is the `if` block's condition. It's usually set to `true` but directives that exist
35479 // on the template can trigger extra guard expressions that serve to narrow types within the
35480 // `if`. `guard` is calculated by starting with `true` and adding other conditions as needed.
35481 // Collect these into `guards` by processing the directives.
35482 const directiveGuards = [];
35483 const directives = this.tcb.boundTarget.getDirectivesOfNode(this.template);
35484 if (directives !== null) {
35485 for (const dir of directives) {
35486 const dirInstId = this.scope.resolve(this.template, dir);
35487 const dirId = this.tcb.env.reference(dir.ref);
35488 // There are two kinds of guards. Template guards (ngTemplateGuards) allow type narrowing of
35489 // the expression passed to an @Input of the directive. Scan the directive to see if it has
35490 // any template guards, and generate them if needed.
35491 dir.ngTemplateGuards.forEach(guard => {
35492 // For each template guard function on the directive, look for a binding to that input.
35493 const boundInput = this.template.inputs.find(i => i.name === guard.inputName) ||
35494 this.template.templateAttrs.find((i) => i instanceof BoundAttribute && i.name === guard.inputName);
35495 if (boundInput !== undefined) {
35496 // If there is such a binding, generate an expression for it.
35497 const expr = tcbExpression(boundInput.value, this.tcb, this.scope);
35498 // The expression has already been checked in the type constructor invocation, so
35499 // it should be ignored when used within a template guard.
35500 markIgnoreDiagnostics(expr);
35501 if (guard.type === 'binding') {
35502 // Use the binding expression itself as guard.
35503 directiveGuards.push(expr);
35504 }
35505 else {
35506 // Call the guard function on the directive with the directive instance and that
35507 // expression.
35508 const guardInvoke = tsCallMethod(dirId, `ngTemplateGuard_${guard.inputName}`, [
35509 dirInstId,
35510 expr,
35511 ]);
35512 addParseSpanInfo(guardInvoke, boundInput.value.sourceSpan);
35513 directiveGuards.push(guardInvoke);
35514 }
35515 }
35516 });
35517 // The second kind of guard is a template context guard. This guard narrows the template
35518 // rendering context variable `ctx`.
35519 if (dir.hasNgTemplateContextGuard && this.tcb.env.config.applyTemplateContextGuards) {
35520 const ctx = this.scope.resolve(this.template);
35521 const guardInvoke = tsCallMethod(dirId, 'ngTemplateContextGuard', [dirInstId, ctx]);
35522 addParseSpanInfo(guardInvoke, this.template.sourceSpan);
35523 directiveGuards.push(guardInvoke);
35524 }
35525 }
35526 }
35527 // By default the guard is simply `true`.
35528 let guard = null;
35529 // If there are any guards from directives, use them instead.
35530 if (directiveGuards.length > 0) {
35531 // Pop the first value and use it as the initializer to reduce(). This way, a single guard
35532 // will be used on its own, but two or more will be combined into binary AND expressions.
35533 guard = directiveGuards.reduce((expr, dirGuard) => ts$1.createBinary(expr, ts$1.SyntaxKind.AmpersandAmpersandToken, dirGuard), directiveGuards.pop());
35534 }
35535 // Create a new Scope for the template. This constructs the list of operations for the template
35536 // children, as well as tracks bindings within the template.
35537 const tmplScope = Scope$1.forNodes(this.tcb, this.scope, this.template, guard);
35538 // Render the template's `Scope` into its statements.
35539 const statements = tmplScope.render();
35540 if (statements.length === 0) {
35541 // As an optimization, don't generate the scope's block if it has no statements. This is
35542 // beneficial for templates that contain for example `<span *ngIf="first"></span>`, in which
35543 // case there's no need to render the `NgIf` guard expression. This seems like a minor
35544 // improvement, however it reduces the number of flow-node antecedents that TypeScript needs
35545 // to keep into account for such cases, resulting in an overall reduction of
35546 // type-checking time.
35547 return null;
35548 }
35549 let tmplBlock = ts$1.createBlock(statements);
35550 if (guard !== null) {
35551 // The scope has a guard that needs to be applied, so wrap the template block into an `if`
35552 // statement containing the guard expression.
35553 tmplBlock = ts$1.createIf(/* expression */ guard, /* thenStatement */ tmplBlock);
35554 }
35555 this.scope.addStatement(tmplBlock);
35556 return null;
35557 }
35558 }
35559 /**
35560 * A `TcbOp` which renders a text binding (interpolation) into the TCB.
35561 *
35562 * Executing this operation returns nothing.
35563 */
35564 class TcbTextInterpolationOp extends TcbOp {
35565 constructor(tcb, scope, binding) {
35566 super();
35567 this.tcb = tcb;
35568 this.scope = scope;
35569 this.binding = binding;
35570 }
35571 get optional() {
35572 return false;
35573 }
35574 execute() {
35575 const expr = tcbExpression(this.binding.value, this.tcb, this.scope);
35576 this.scope.addStatement(ts$1.createExpressionStatement(expr));
35577 return null;
35578 }
35579 }
35580 /**
35581 * A `TcbOp` which constructs an instance of a directive _without_ setting any of its inputs. Inputs
35582 * are later set in the `TcbDirectiveInputsOp`. Type checking was found to be faster when done in
35583 * this way as opposed to `TcbDirectiveCtorOp` which is only necessary when the directive is
35584 * generic.
35585 *
35586 * Executing this operation returns a reference to the directive instance variable with its inferred
35587 * type.
35588 */
35589 class TcbDirectiveTypeOp extends TcbOp {
35590 constructor(tcb, scope, node, dir) {
35591 super();
35592 this.tcb = tcb;
35593 this.scope = scope;
35594 this.node = node;
35595 this.dir = dir;
35596 }
35597 get optional() {
35598 // The statement generated by this operation is only used to declare the directive's type and
35599 // won't report diagnostics by itself, so the operation is marked as optional to avoid
35600 // generating declarations for directives that don't have any inputs/outputs.
35601 return true;
35602 }
35603 execute() {
35604 const id = this.tcb.allocateId();
35605 const type = this.tcb.env.referenceType(this.dir.ref);
35606 addExpressionIdentifier(type, ExpressionIdentifier.DIRECTIVE);
35607 addParseSpanInfo(type, this.node.startSourceSpan || this.node.sourceSpan);
35608 this.scope.addStatement(tsDeclareVariable(id, type));
35609 return id;
35610 }
35611 }
35612 /**
35613 * A `TcbOp` which creates a variable for a local ref in a template.
35614 * The initializer for the variable is the variable expression for the directive, template, or
35615 * element the ref refers to. When the reference is used in the template, those TCB statements will
35616 * access this variable as well. For example:
35617 * ```
35618 * var _t1 = document.createElement('div');
35619 * var _t2 = _t1;
35620 * _t2.value
35621 * ```
35622 * This operation supports more fluent lookups for the `TemplateTypeChecker` when getting a symbol
35623 * for a reference. In most cases, this isn't essential; that is, the information for the symbol
35624 * could be gathered without this operation using the `BoundTarget`. However, for the case of
35625 * ng-template references, we will need this reference variable to not only provide a location in
35626 * the shim file, but also to narrow the variable to the correct `TemplateRef<T>` type rather than
35627 * `TemplateRef<any>` (this work is still TODO).
35628 *
35629 * Executing this operation returns a reference to the directive instance variable with its inferred
35630 * type.
35631 */
35632 class TcbReferenceOp extends TcbOp {
35633 constructor(tcb, scope, node, host, target) {
35634 super();
35635 this.tcb = tcb;
35636 this.scope = scope;
35637 this.node = node;
35638 this.host = host;
35639 this.target = target;
35640 // The statement generated by this operation is only used to for the Type Checker
35641 // so it can map a reference variable in the template directly to a node in the TCB.
35642 this.optional = true;
35643 }
35644 execute() {
35645 const id = this.tcb.allocateId();
35646 let initializer = this.target instanceof Template || this.target instanceof Element ?
35647 this.scope.resolve(this.target) :
35648 this.scope.resolve(this.host, this.target);
35649 // The reference is either to an element, an <ng-template> node, or to a directive on an
35650 // element or template.
35651 if ((this.target instanceof Element && !this.tcb.env.config.checkTypeOfDomReferences) ||
35652 !this.tcb.env.config.checkTypeOfNonDomReferences) {
35653 // References to DOM nodes are pinned to 'any' when `checkTypeOfDomReferences` is `false`.
35654 // References to `TemplateRef`s and directives are pinned to 'any' when
35655 // `checkTypeOfNonDomReferences` is `false`.
35656 initializer =
35657 ts$1.createAsExpression(initializer, ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
35658 }
35659 else if (this.target instanceof Template) {
35660 // Direct references to an <ng-template> node simply require a value of type
35661 // `TemplateRef<any>`. To get this, an expression of the form
35662 // `(_t1 as any as TemplateRef<any>)` is constructed.
35663 initializer =
35664 ts$1.createAsExpression(initializer, ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
35665 initializer = ts$1.createAsExpression(initializer, this.tcb.env.referenceExternalType('@angular/core', 'TemplateRef', [DYNAMIC_TYPE]));
35666 initializer = ts$1.createParen(initializer);
35667 }
35668 addParseSpanInfo(initializer, this.node.sourceSpan);
35669 addParseSpanInfo(id, this.node.keySpan);
35670 this.scope.addStatement(tsCreateVariable(id, initializer));
35671 return id;
35672 }
35673 }
35674 /**
35675 * A `TcbOp` which is used when the target of a reference is missing. This operation generates a
35676 * variable of type any for usages of the invalid reference to resolve to. The invalid reference
35677 * itself is recorded out-of-band.
35678 */
35679 class TcbInvalidReferenceOp extends TcbOp {
35680 constructor(tcb, scope) {
35681 super();
35682 this.tcb = tcb;
35683 this.scope = scope;
35684 // The declaration of a missing reference is only needed when the reference is resolved.
35685 this.optional = true;
35686 }
35687 execute() {
35688 const id = this.tcb.allocateId();
35689 this.scope.addStatement(tsCreateVariable(id, NULL_AS_ANY));
35690 return id;
35691 }
35692 }
35693 /**
35694 * A `TcbOp` which constructs an instance of a directive with types inferred from its inputs. The
35695 * inputs themselves are not checked here; checking of inputs is achieved in `TcbDirectiveInputsOp`.
35696 * Any errors reported in this statement are ignored, as the type constructor call is only present
35697 * for type-inference.
35698 *
35699 * When a Directive is generic, it is required that the TCB generates the instance using this method
35700 * in order to infer the type information correctly.
35701 *
35702 * Executing this operation returns a reference to the directive instance variable with its inferred
35703 * type.
35704 */
35705 class TcbDirectiveCtorOp extends TcbOp {
35706 constructor(tcb, scope, node, dir) {
35707 super();
35708 this.tcb = tcb;
35709 this.scope = scope;
35710 this.node = node;
35711 this.dir = dir;
35712 }
35713 get optional() {
35714 // The statement generated by this operation is only used to infer the directive's type and
35715 // won't report diagnostics by itself, so the operation is marked as optional.
35716 return true;
35717 }
35718 execute() {
35719 const id = this.tcb.allocateId();
35720 addExpressionIdentifier(id, ExpressionIdentifier.DIRECTIVE);
35721 addParseSpanInfo(id, this.node.startSourceSpan || this.node.sourceSpan);
35722 const genericInputs = new Map();
35723 const inputs = getBoundInputs(this.dir, this.node, this.tcb);
35724 for (const input of inputs) {
35725 // Skip text attributes if configured to do so.
35726 if (!this.tcb.env.config.checkTypeOfAttributes &&
35727 input.attribute instanceof TextAttribute) {
35728 continue;
35729 }
35730 for (const fieldName of input.fieldNames) {
35731 // Skip the field if an attribute has already been bound to it; we can't have a duplicate
35732 // key in the type constructor call.
35733 if (genericInputs.has(fieldName)) {
35734 continue;
35735 }
35736 const expression = translateInput(input.attribute, this.tcb, this.scope);
35737 genericInputs.set(fieldName, {
35738 type: 'binding',
35739 field: fieldName,
35740 expression,
35741 sourceSpan: input.attribute.sourceSpan
35742 });
35743 }
35744 }
35745 // Add unset directive inputs for each of the remaining unset fields.
35746 for (const [fieldName] of this.dir.inputs) {
35747 if (!genericInputs.has(fieldName)) {
35748 genericInputs.set(fieldName, { type: 'unset', field: fieldName });
35749 }
35750 }
35751 // Call the type constructor of the directive to infer a type, and assign the directive
35752 // instance.
35753 const typeCtor = tcbCallTypeCtor(this.dir, this.tcb, Array.from(genericInputs.values()));
35754 markIgnoreDiagnostics(typeCtor);
35755 this.scope.addStatement(tsCreateVariable(id, typeCtor));
35756 return id;
35757 }
35758 circularFallback() {
35759 return new TcbDirectiveCtorCircularFallbackOp(this.tcb, this.scope, this.node, this.dir);
35760 }
35761 }
35762 /**
35763 * A `TcbOp` which generates code to check input bindings on an element that correspond with the
35764 * members of a directive.
35765 *
35766 * Executing this operation returns nothing.
35767 */
35768 class TcbDirectiveInputsOp extends TcbOp {
35769 constructor(tcb, scope, node, dir) {
35770 super();
35771 this.tcb = tcb;
35772 this.scope = scope;
35773 this.node = node;
35774 this.dir = dir;
35775 }
35776 get optional() {
35777 return false;
35778 }
35779 execute() {
35780 let dirId = null;
35781 // TODO(joost): report duplicate properties
35782 const inputs = getBoundInputs(this.dir, this.node, this.tcb);
35783 for (const input of inputs) {
35784 // For bound inputs, the property is assigned the binding expression.
35785 let expr = translateInput(input.attribute, this.tcb, this.scope);
35786 if (!this.tcb.env.config.checkTypeOfInputBindings) {
35787 // If checking the type of bindings is disabled, cast the resulting expression to 'any'
35788 // before the assignment.
35789 expr = tsCastToAny(expr);
35790 }
35791 else if (!this.tcb.env.config.strictNullInputBindings) {
35792 // If strict null checks are disabled, erase `null` and `undefined` from the type by
35793 // wrapping the expression in a non-null assertion.
35794 expr = ts$1.createNonNullExpression(expr);
35795 }
35796 let assignment = wrapForDiagnostics(expr);
35797 for (const fieldName of input.fieldNames) {
35798 let target;
35799 if (this.dir.coercedInputFields.has(fieldName)) {
35800 // The input has a coercion declaration which should be used instead of assigning the
35801 // expression into the input field directly. To achieve this, a variable is declared
35802 // with a type of `typeof Directive.ngAcceptInputType_fieldName` which is then used as
35803 // target of the assignment.
35804 const dirTypeRef = this.tcb.env.referenceType(this.dir.ref);
35805 if (!ts$1.isTypeReferenceNode(dirTypeRef)) {
35806 throw new Error(`Expected TypeReferenceNode from reference to ${this.dir.ref.debugName}`);
35807 }
35808 const id = this.tcb.allocateId();
35809 const type = tsCreateTypeQueryForCoercedInput(dirTypeRef.typeName, fieldName);
35810 this.scope.addStatement(tsDeclareVariable(id, type));
35811 target = id;
35812 }
35813 else if (this.dir.undeclaredInputFields.has(fieldName)) {
35814 // If no coercion declaration is present nor is the field declared (i.e. the input is
35815 // declared in a `@Directive` or `@Component` decorator's `inputs` property) there is no
35816 // assignment target available, so this field is skipped.
35817 continue;
35818 }
35819 else if (!this.tcb.env.config.honorAccessModifiersForInputBindings &&
35820 this.dir.restrictedInputFields.has(fieldName)) {
35821 // If strict checking of access modifiers is disabled and the field is restricted
35822 // (i.e. private/protected/readonly), generate an assignment into a temporary variable
35823 // that has the type of the field. This achieves type-checking but circumvents the access
35824 // modifiers.
35825 if (dirId === null) {
35826 dirId = this.scope.resolve(this.node, this.dir);
35827 }
35828 const id = this.tcb.allocateId();
35829 const dirTypeRef = this.tcb.env.referenceType(this.dir.ref);
35830 if (!ts$1.isTypeReferenceNode(dirTypeRef)) {
35831 throw new Error(`Expected TypeReferenceNode from reference to ${this.dir.ref.debugName}`);
35832 }
35833 const type = ts$1.createIndexedAccessTypeNode(ts$1.createTypeQueryNode(dirId), ts$1.createLiteralTypeNode(ts$1.createStringLiteral(fieldName)));
35834 const temp = tsDeclareVariable(id, type);
35835 this.scope.addStatement(temp);
35836 target = id;
35837 }
35838 else {
35839 if (dirId === null) {
35840 dirId = this.scope.resolve(this.node, this.dir);
35841 }
35842 // To get errors assign directly to the fields on the instance, using property access
35843 // when possible. String literal fields may not be valid JS identifiers so we use
35844 // literal element access instead for those cases.
35845 target = this.dir.stringLiteralInputFields.has(fieldName) ?
35846 ts$1.createElementAccess(dirId, ts$1.createStringLiteral(fieldName)) :
35847 ts$1.createPropertyAccess(dirId, ts$1.createIdentifier(fieldName));
35848 }
35849 if (input.attribute.keySpan !== undefined) {
35850 addParseSpanInfo(target, input.attribute.keySpan);
35851 }
35852 // Finally the assignment is extended by assigning it into the target expression.
35853 assignment = ts$1.createBinary(target, ts$1.SyntaxKind.EqualsToken, assignment);
35854 }
35855 addParseSpanInfo(assignment, input.attribute.sourceSpan);
35856 // Ignore diagnostics for text attributes if configured to do so.
35857 if (!this.tcb.env.config.checkTypeOfAttributes &&
35858 input.attribute instanceof TextAttribute) {
35859 markIgnoreDiagnostics(assignment);
35860 }
35861 this.scope.addStatement(ts$1.createExpressionStatement(assignment));
35862 }
35863 return null;
35864 }
35865 }
35866 /**
35867 * A `TcbOp` which is used to generate a fallback expression if the inference of a directive type
35868 * via `TcbDirectiveCtorOp` requires a reference to its own type. This can happen using a template
35869 * reference:
35870 *
35871 * ```html
35872 * <some-cmp #ref [prop]="ref.foo"></some-cmp>
35873 * ```
35874 *
35875 * In this case, `TcbDirectiveCtorCircularFallbackOp` will add a second inference of the directive
35876 * type to the type-check block, this time calling the directive's type constructor without any
35877 * input expressions. This infers the widest possible supertype for the directive, which is used to
35878 * resolve any recursive references required to infer the real type.
35879 */
35880 class TcbDirectiveCtorCircularFallbackOp extends TcbOp {
35881 constructor(tcb, scope, node, dir) {
35882 super();
35883 this.tcb = tcb;
35884 this.scope = scope;
35885 this.node = node;
35886 this.dir = dir;
35887 }
35888 get optional() {
35889 return false;
35890 }
35891 execute() {
35892 const id = this.tcb.allocateId();
35893 const typeCtor = this.tcb.env.typeCtorFor(this.dir);
35894 const circularPlaceholder = ts$1.createCall(typeCtor, /* typeArguments */ undefined, [ts$1.createNonNullExpression(ts$1.createNull())]);
35895 this.scope.addStatement(tsCreateVariable(id, circularPlaceholder));
35896 return id;
35897 }
35898 }
35899 /**
35900 * A `TcbOp` which feeds elements and unclaimed properties to the `DomSchemaChecker`.
35901 *
35902 * The DOM schema is not checked via TCB code generation. Instead, the `DomSchemaChecker` ingests
35903 * elements and property bindings and accumulates synthetic `ts.Diagnostic`s out-of-band. These are
35904 * later merged with the diagnostics generated from the TCB.
35905 *
35906 * For convenience, the TCB iteration of the template is used to drive the `DomSchemaChecker` via
35907 * the `TcbDomSchemaCheckerOp`.
35908 */
35909 class TcbDomSchemaCheckerOp extends TcbOp {
35910 constructor(tcb, element, checkElement, claimedInputs) {
35911 super();
35912 this.tcb = tcb;
35913 this.element = element;
35914 this.checkElement = checkElement;
35915 this.claimedInputs = claimedInputs;
35916 }
35917 get optional() {
35918 return false;
35919 }
35920 execute() {
35921 if (this.checkElement) {
35922 this.tcb.domSchemaChecker.checkElement(this.tcb.id, this.element, this.tcb.schemas);
35923 }
35924 // TODO(alxhub): this could be more efficient.
35925 for (const binding of this.element.inputs) {
35926 if (binding.type === 0 /* Property */ && this.claimedInputs.has(binding.name)) {
35927 // Skip this binding as it was claimed by a directive.
35928 continue;
35929 }
35930 if (binding.type === 0 /* Property */) {
35931 if (binding.name !== 'style' && binding.name !== 'class') {
35932 // A direct binding to a property.
35933 const propertyName = ATTR_TO_PROP[binding.name] || binding.name;
35934 this.tcb.domSchemaChecker.checkProperty(this.tcb.id, this.element, propertyName, binding.sourceSpan, this.tcb.schemas);
35935 }
35936 }
35937 }
35938 return null;
35939 }
35940 }
35941 /**
35942 * Mapping between attributes names that don't correspond to their element property names.
35943 * Note: this mapping has to be kept in sync with the equally named mapping in the runtime.
35944 */
35945 const ATTR_TO_PROP = {
35946 'class': 'className',
35947 'for': 'htmlFor',
35948 'formaction': 'formAction',
35949 'innerHtml': 'innerHTML',
35950 'readonly': 'readOnly',
35951 'tabindex': 'tabIndex',
35952 };
35953 /**
35954 * A `TcbOp` which generates code to check "unclaimed inputs" - bindings on an element which were
35955 * not attributed to any directive or component, and are instead processed against the HTML element
35956 * itself.
35957 *
35958 * Currently, only the expressions of these bindings are checked. The targets of the bindings are
35959 * checked against the DOM schema via a `TcbDomSchemaCheckerOp`.
35960 *
35961 * Executing this operation returns nothing.
35962 */
35963 class TcbUnclaimedInputsOp extends TcbOp {
35964 constructor(tcb, scope, element, claimedInputs) {
35965 super();
35966 this.tcb = tcb;
35967 this.scope = scope;
35968 this.element = element;
35969 this.claimedInputs = claimedInputs;
35970 }
35971 get optional() {
35972 return false;
35973 }
35974 execute() {
35975 // `this.inputs` contains only those bindings not matched by any directive. These bindings go to
35976 // the element itself.
35977 let elId = null;
35978 // TODO(alxhub): this could be more efficient.
35979 for (const binding of this.element.inputs) {
35980 if (binding.type === 0 /* Property */ && this.claimedInputs.has(binding.name)) {
35981 // Skip this binding as it was claimed by a directive.
35982 continue;
35983 }
35984 let expr = tcbExpression(binding.value, this.tcb, this.scope);
35985 if (!this.tcb.env.config.checkTypeOfInputBindings) {
35986 // If checking the type of bindings is disabled, cast the resulting expression to 'any'
35987 // before the assignment.
35988 expr = tsCastToAny(expr);
35989 }
35990 else if (!this.tcb.env.config.strictNullInputBindings) {
35991 // If strict null checks are disabled, erase `null` and `undefined` from the type by
35992 // wrapping the expression in a non-null assertion.
35993 expr = ts$1.createNonNullExpression(expr);
35994 }
35995 if (this.tcb.env.config.checkTypeOfDomBindings && binding.type === 0 /* Property */) {
35996 if (binding.name !== 'style' && binding.name !== 'class') {
35997 if (elId === null) {
35998 elId = this.scope.resolve(this.element);
35999 }
36000 // A direct binding to a property.
36001 const propertyName = ATTR_TO_PROP[binding.name] || binding.name;
36002 const prop = ts$1.createElementAccess(elId, ts$1.createStringLiteral(propertyName));
36003 const stmt = ts$1.createBinary(prop, ts$1.SyntaxKind.EqualsToken, wrapForDiagnostics(expr));
36004 addParseSpanInfo(stmt, binding.sourceSpan);
36005 this.scope.addStatement(ts$1.createExpressionStatement(stmt));
36006 }
36007 else {
36008 this.scope.addStatement(ts$1.createExpressionStatement(expr));
36009 }
36010 }
36011 else {
36012 // A binding to an animation, attribute, class or style. For now, only validate the right-
36013 // hand side of the expression.
36014 // TODO: properly check class and style bindings.
36015 this.scope.addStatement(ts$1.createExpressionStatement(expr));
36016 }
36017 }
36018 return null;
36019 }
36020 }
36021 /**
36022 * A `TcbOp` which generates code to check event bindings on an element that correspond with the
36023 * outputs of a directive.
36024 *
36025 * Executing this operation returns nothing.
36026 */
36027 class TcbDirectiveOutputsOp extends TcbOp {
36028 constructor(tcb, scope, node, dir) {
36029 super();
36030 this.tcb = tcb;
36031 this.scope = scope;
36032 this.node = node;
36033 this.dir = dir;
36034 }
36035 get optional() {
36036 return false;
36037 }
36038 execute() {
36039 let dirId = null;
36040 const outputs = this.dir.outputs;
36041 for (const output of this.node.outputs) {
36042 if (output.type !== 0 /* Regular */ || !outputs.hasBindingPropertyName(output.name)) {
36043 continue;
36044 }
36045 // TODO(alxhub): consider supporting multiple fields with the same property name for outputs.
36046 const field = outputs.getByBindingPropertyName(output.name)[0].classPropertyName;
36047 if (dirId === null) {
36048 dirId = this.scope.resolve(this.node, this.dir);
36049 }
36050 const outputField = ts$1.createElementAccess(dirId, ts$1.createStringLiteral(field));
36051 addParseSpanInfo(outputField, output.keySpan);
36052 if (this.tcb.env.config.checkTypeOfOutputEvents) {
36053 // For strict checking of directive events, generate a call to the `subscribe` method
36054 // on the directive's output field to let type information flow into the handler function's
36055 // `$event` parameter.
36056 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 0 /* Infer */);
36057 const subscribeFn = ts$1.createPropertyAccess(outputField, 'subscribe');
36058 const call = ts$1.createCall(subscribeFn, /* typeArguments */ undefined, [handler]);
36059 addParseSpanInfo(call, output.sourceSpan);
36060 this.scope.addStatement(ts$1.createExpressionStatement(call));
36061 }
36062 else {
36063 // If strict checking of directive events is disabled:
36064 //
36065 // * We still generate the access to the output field as a statement in the TCB so consumers
36066 // of the `TemplateTypeChecker` can still find the node for the class member for the
36067 // output.
36068 // * Emit a handler function where the `$event` parameter has an explicit `any` type.
36069 this.scope.addStatement(ts$1.createExpressionStatement(outputField));
36070 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 1 /* Any */);
36071 this.scope.addStatement(ts$1.createExpressionStatement(handler));
36072 }
36073 ExpressionSemanticVisitor.visit(output.handler, this.tcb.id, this.tcb.boundTarget, this.tcb.oobRecorder);
36074 }
36075 return null;
36076 }
36077 }
36078 /**
36079 * A `TcbOp` which generates code to check "unclaimed outputs" - event bindings on an element which
36080 * were not attributed to any directive or component, and are instead processed against the HTML
36081 * element itself.
36082 *
36083 * Executing this operation returns nothing.
36084 */
36085 class TcbUnclaimedOutputsOp extends TcbOp {
36086 constructor(tcb, scope, element, claimedOutputs) {
36087 super();
36088 this.tcb = tcb;
36089 this.scope = scope;
36090 this.element = element;
36091 this.claimedOutputs = claimedOutputs;
36092 }
36093 get optional() {
36094 return false;
36095 }
36096 execute() {
36097 let elId = null;
36098 // TODO(alxhub): this could be more efficient.
36099 for (const output of this.element.outputs) {
36100 if (this.claimedOutputs.has(output.name)) {
36101 // Skip this event handler as it was claimed by a directive.
36102 continue;
36103 }
36104 if (output.type === 1 /* Animation */) {
36105 // Animation output bindings always have an `$event` parameter of type `AnimationEvent`.
36106 const eventType = this.tcb.env.config.checkTypeOfAnimationEvents ?
36107 this.tcb.env.referenceExternalType('@angular/animations', 'AnimationEvent') :
36108 1 /* Any */;
36109 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, eventType);
36110 this.scope.addStatement(ts$1.createExpressionStatement(handler));
36111 }
36112 else if (this.tcb.env.config.checkTypeOfDomEvents) {
36113 // If strict checking of DOM events is enabled, generate a call to `addEventListener` on
36114 // the element instance so that TypeScript's type inference for
36115 // `HTMLElement.addEventListener` using `HTMLElementEventMap` to infer an accurate type for
36116 // `$event` depending on the event name. For unknown event names, TypeScript resorts to the
36117 // base `Event` type.
36118 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 0 /* Infer */);
36119 if (elId === null) {
36120 elId = this.scope.resolve(this.element);
36121 }
36122 const propertyAccess = ts$1.createPropertyAccess(elId, 'addEventListener');
36123 addParseSpanInfo(propertyAccess, output.keySpan);
36124 const call = ts$1.createCall(
36125 /* expression */ propertyAccess,
36126 /* typeArguments */ undefined,
36127 /* arguments */ [ts$1.createStringLiteral(output.name), handler]);
36128 addParseSpanInfo(call, output.sourceSpan);
36129 this.scope.addStatement(ts$1.createExpressionStatement(call));
36130 }
36131 else {
36132 // If strict checking of DOM inputs is disabled, emit a handler function where the `$event`
36133 // parameter has an explicit `any` type.
36134 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 1 /* Any */);
36135 this.scope.addStatement(ts$1.createExpressionStatement(handler));
36136 }
36137 ExpressionSemanticVisitor.visit(output.handler, this.tcb.id, this.tcb.boundTarget, this.tcb.oobRecorder);
36138 }
36139 return null;
36140 }
36141 }
36142 /**
36143 * A `TcbOp` which generates a completion point for the component context.
36144 *
36145 * This completion point looks like `ctx. ;` in the TCB output, and does not produce diagnostics.
36146 * TypeScript autocompletion APIs can be used at this completion point (after the '.') to produce
36147 * autocompletion results of properties and methods from the template's component context.
36148 */
36149 class TcbComponentContextCompletionOp extends TcbOp {
36150 constructor(scope) {
36151 super();
36152 this.scope = scope;
36153 this.optional = false;
36154 }
36155 execute() {
36156 const ctx = ts$1.createIdentifier('ctx');
36157 const ctxDot = ts$1.createPropertyAccess(ctx, '');
36158 markIgnoreDiagnostics(ctxDot);
36159 addExpressionIdentifier(ctxDot, ExpressionIdentifier.COMPONENT_COMPLETION);
36160 this.scope.addStatement(ts$1.createExpressionStatement(ctxDot));
36161 return null;
36162 }
36163 }
36164 /**
36165 * Value used to break a circular reference between `TcbOp`s.
36166 *
36167 * This value is returned whenever `TcbOp`s have a circular dependency. The expression is a non-null
36168 * assertion of the null value (in TypeScript, the expression `null!`). This construction will infer
36169 * the least narrow type for whatever it's assigned to.
36170 */
36171 const INFER_TYPE_FOR_CIRCULAR_OP_EXPR = ts$1.createNonNullExpression(ts$1.createNull());
36172 /**
36173 * Overall generation context for the type check block.
36174 *
36175 * `Context` handles operations during code generation which are global with respect to the whole
36176 * block. It's responsible for variable name allocation and management of any imports needed. It
36177 * also contains the template metadata itself.
36178 */
36179 class Context$1 {
36180 constructor(env, domSchemaChecker, oobRecorder, id, boundTarget, pipes, schemas) {
36181 this.env = env;
36182 this.domSchemaChecker = domSchemaChecker;
36183 this.oobRecorder = oobRecorder;
36184 this.id = id;
36185 this.boundTarget = boundTarget;
36186 this.pipes = pipes;
36187 this.schemas = schemas;
36188 this.nextId = 1;
36189 }
36190 /**
36191 * Allocate a new variable name for use within the `Context`.
36192 *
36193 * Currently this uses a monotonically increasing counter, but in the future the variable name
36194 * might change depending on the type of data being stored.
36195 */
36196 allocateId() {
36197 return ts$1.createIdentifier(`_t${this.nextId++}`);
36198 }
36199 getPipeByName(name) {
36200 if (!this.pipes.has(name)) {
36201 return null;
36202 }
36203 return this.pipes.get(name);
36204 }
36205 }
36206 /**
36207 * Local scope within the type check block for a particular template.
36208 *
36209 * The top-level template and each nested `<ng-template>` have their own `Scope`, which exist in a
36210 * hierarchy. The structure of this hierarchy mirrors the syntactic scopes in the generated type
36211 * check block, where each nested template is encased in an `if` structure.
36212 *
36213 * As a template's `TcbOp`s are executed in a given `Scope`, statements are added via
36214 * `addStatement()`. When this processing is complete, the `Scope` can be turned into a `ts.Block`
36215 * via `renderToBlock()`.
36216 *
36217 * If a `TcbOp` requires the output of another, it can call `resolve()`.
36218 */
36219 class Scope$1 {
36220 constructor(tcb, parent = null, guard = null) {
36221 this.tcb = tcb;
36222 this.parent = parent;
36223 this.guard = guard;
36224 /**
36225 * A queue of operations which need to be performed to generate the TCB code for this scope.
36226 *
36227 * This array can contain either a `TcbOp` which has yet to be executed, or a `ts.Expression|null`
36228 * representing the memoized result of executing the operation. As operations are executed, their
36229 * results are written into the `opQueue`, overwriting the original operation.
36230 *
36231 * If an operation is in the process of being executed, it is temporarily overwritten here with
36232 * `INFER_TYPE_FOR_CIRCULAR_OP_EXPR`. This way, if a cycle is encountered where an operation
36233 * depends transitively on its own result, the inner operation will infer the least narrow type
36234 * that fits instead. This has the same semantics as TypeScript itself when types are referenced
36235 * circularly.
36236 */
36237 this.opQueue = [];
36238 /**
36239 * A map of `TmplAstElement`s to the index of their `TcbElementOp` in the `opQueue`
36240 */
36241 this.elementOpMap = new Map();
36242 /**
36243 * A map of maps which tracks the index of `TcbDirectiveCtorOp`s in the `opQueue` for each
36244 * directive on a `TmplAstElement` or `TmplAstTemplate` node.
36245 */
36246 this.directiveOpMap = new Map();
36247 /**
36248 * A map of `TmplAstReference`s to the index of their `TcbReferenceOp` in the `opQueue`
36249 */
36250 this.referenceOpMap = new Map();
36251 /**
36252 * Map of immediately nested <ng-template>s (within this `Scope`) represented by `TmplAstTemplate`
36253 * nodes to the index of their `TcbTemplateContextOp`s in the `opQueue`.
36254 */
36255 this.templateCtxOpMap = new Map();
36256 /**
36257 * Map of variables declared on the template that created this `Scope` (represented by
36258 * `TmplAstVariable` nodes) to the index of their `TcbVariableOp`s in the `opQueue`.
36259 */
36260 this.varMap = new Map();
36261 /**
36262 * Statements for this template.
36263 *
36264 * Executing the `TcbOp`s in the `opQueue` populates this array.
36265 */
36266 this.statements = [];
36267 }
36268 /**
36269 * Constructs a `Scope` given either a `TmplAstTemplate` or a list of `TmplAstNode`s.
36270 *
36271 * @param tcb the overall context of TCB generation.
36272 * @param parent the `Scope` of the parent template (if any) or `null` if this is the root
36273 * `Scope`.
36274 * @param templateOrNodes either a `TmplAstTemplate` representing the template for which to
36275 * calculate the `Scope`, or a list of nodes if no outer template object is available.
36276 * @param guard an expression that is applied to this scope for type narrowing purposes.
36277 */
36278 static forNodes(tcb, parent, templateOrNodes, guard) {
36279 const scope = new Scope$1(tcb, parent, guard);
36280 if (parent === null && tcb.env.config.enableTemplateTypeChecker) {
36281 // Add an autocompletion point for the component context.
36282 scope.opQueue.push(new TcbComponentContextCompletionOp(scope));
36283 }
36284 let children;
36285 // If given an actual `TmplAstTemplate` instance, then process any additional information it
36286 // has.
36287 if (templateOrNodes instanceof Template) {
36288 // The template's variable declarations need to be added as `TcbVariableOp`s.
36289 const varMap = new Map();
36290 for (const v of templateOrNodes.variables) {
36291 // Validate that variables on the `TmplAstTemplate` are only declared once.
36292 if (!varMap.has(v.name)) {
36293 varMap.set(v.name, v);
36294 }
36295 else {
36296 const firstDecl = varMap.get(v.name);
36297 tcb.oobRecorder.duplicateTemplateVar(tcb.id, v, firstDecl);
36298 }
36299 const opIndex = scope.opQueue.push(new TcbVariableOp(tcb, scope, templateOrNodes, v)) - 1;
36300 scope.varMap.set(v, opIndex);
36301 }
36302 children = templateOrNodes.children;
36303 }
36304 else {
36305 children = templateOrNodes;
36306 }
36307 for (const node of children) {
36308 scope.appendNode(node);
36309 }
36310 return scope;
36311 }
36312 /**
36313 * Look up a `ts.Expression` representing the value of some operation in the current `Scope`,
36314 * including any parent scope(s). This method always returns a mutable clone of the
36315 * `ts.Expression` with the comments cleared.
36316 *
36317 * @param node a `TmplAstNode` of the operation in question. The lookup performed will depend on
36318 * the type of this node:
36319 *
36320 * Assuming `directive` is not present, then `resolve` will return:
36321 *
36322 * * `TmplAstElement` - retrieve the expression for the element DOM node
36323 * * `TmplAstTemplate` - retrieve the template context variable
36324 * * `TmplAstVariable` - retrieve a template let- variable
36325 * * `TmplAstReference` - retrieve variable created for the local ref
36326 *
36327 * @param directive if present, a directive type on a `TmplAstElement` or `TmplAstTemplate` to
36328 * look up instead of the default for an element or template node.
36329 */
36330 resolve(node, directive) {
36331 // Attempt to resolve the operation locally.
36332 const res = this.resolveLocal(node, directive);
36333 if (res !== null) {
36334 // We want to get a clone of the resolved expression and clear the trailing comments
36335 // so they don't continue to appear in every place the expression is used.
36336 // As an example, this would otherwise produce:
36337 // var _t1 /**T:DIR*/ /*1,2*/ = _ctor1();
36338 // _t1 /**T:DIR*/ /*1,2*/.input = 'value';
36339 //
36340 // In addition, returning a clone prevents the consumer of `Scope#resolve` from
36341 // attaching comments at the declaration site.
36342 const clone = ts$1.getMutableClone(res);
36343 ts$1.setSyntheticTrailingComments(clone, []);
36344 return clone;
36345 }
36346 else if (this.parent !== null) {
36347 // Check with the parent.
36348 return this.parent.resolve(node, directive);
36349 }
36350 else {
36351 throw new Error(`Could not resolve ${node} / ${directive}`);
36352 }
36353 }
36354 /**
36355 * Add a statement to this scope.
36356 */
36357 addStatement(stmt) {
36358 this.statements.push(stmt);
36359 }
36360 /**
36361 * Get the statements.
36362 */
36363 render() {
36364 for (let i = 0; i < this.opQueue.length; i++) {
36365 // Optional statements cannot be skipped when we are generating the TCB for use
36366 // by the TemplateTypeChecker.
36367 const skipOptional = !this.tcb.env.config.enableTemplateTypeChecker;
36368 this.executeOp(i, skipOptional);
36369 }
36370 return this.statements;
36371 }
36372 /**
36373 * Returns an expression of all template guards that apply to this scope, including those of
36374 * parent scopes. If no guards have been applied, null is returned.
36375 */
36376 guards() {
36377 let parentGuards = null;
36378 if (this.parent !== null) {
36379 // Start with the guards from the parent scope, if present.
36380 parentGuards = this.parent.guards();
36381 }
36382 if (this.guard === null) {
36383 // This scope does not have a guard, so return the parent's guards as is.
36384 return parentGuards;
36385 }
36386 else if (parentGuards === null) {
36387 // There's no guards from the parent scope, so this scope's guard represents all available
36388 // guards.
36389 return this.guard;
36390 }
36391 else {
36392 // Both the parent scope and this scope provide a guard, so create a combination of the two.
36393 // It is important that the parent guard is used as left operand, given that it may provide
36394 // narrowing that is required for this scope's guard to be valid.
36395 return ts$1.createBinary(parentGuards, ts$1.SyntaxKind.AmpersandAmpersandToken, this.guard);
36396 }
36397 }
36398 resolveLocal(ref, directive) {
36399 if (ref instanceof Reference && this.referenceOpMap.has(ref)) {
36400 return this.resolveOp(this.referenceOpMap.get(ref));
36401 }
36402 else if (ref instanceof Variable && this.varMap.has(ref)) {
36403 // Resolving a context variable for this template.
36404 // Execute the `TcbVariableOp` associated with the `TmplAstVariable`.
36405 return this.resolveOp(this.varMap.get(ref));
36406 }
36407 else if (ref instanceof Template && directive === undefined &&
36408 this.templateCtxOpMap.has(ref)) {
36409 // Resolving the context of the given sub-template.
36410 // Execute the `TcbTemplateContextOp` for the template.
36411 return this.resolveOp(this.templateCtxOpMap.get(ref));
36412 }
36413 else if ((ref instanceof Element || ref instanceof Template) &&
36414 directive !== undefined && this.directiveOpMap.has(ref)) {
36415 // Resolving a directive on an element or sub-template.
36416 const dirMap = this.directiveOpMap.get(ref);
36417 if (dirMap.has(directive)) {
36418 return this.resolveOp(dirMap.get(directive));
36419 }
36420 else {
36421 return null;
36422 }
36423 }
36424 else if (ref instanceof Element && this.elementOpMap.has(ref)) {
36425 // Resolving the DOM node of an element in this template.
36426 return this.resolveOp(this.elementOpMap.get(ref));
36427 }
36428 else {
36429 return null;
36430 }
36431 }
36432 /**
36433 * Like `executeOp`, but assert that the operation actually returned `ts.Expression`.
36434 */
36435 resolveOp(opIndex) {
36436 const res = this.executeOp(opIndex, /* skipOptional */ false);
36437 if (res === null) {
36438 throw new Error(`Error resolving operation, got null`);
36439 }
36440 return res;
36441 }
36442 /**
36443 * Execute a particular `TcbOp` in the `opQueue`.
36444 *
36445 * This method replaces the operation in the `opQueue` with the result of execution (once done)
36446 * and also protects against a circular dependency from the operation to itself by temporarily
36447 * setting the operation's result to a special expression.
36448 */
36449 executeOp(opIndex, skipOptional) {
36450 const op = this.opQueue[opIndex];
36451 if (!(op instanceof TcbOp)) {
36452 return op;
36453 }
36454 if (skipOptional && op.optional) {
36455 return null;
36456 }
36457 // Set the result of the operation in the queue to its circular fallback. If executing this
36458 // operation results in a circular dependency, this will prevent an infinite loop and allow for
36459 // the resolution of such cycles.
36460 this.opQueue[opIndex] = op.circularFallback();
36461 const res = op.execute();
36462 // Once the operation has finished executing, it's safe to cache the real result.
36463 this.opQueue[opIndex] = res;
36464 return res;
36465 }
36466 appendNode(node) {
36467 if (node instanceof Element) {
36468 const opIndex = this.opQueue.push(new TcbElementOp(this.tcb, this, node)) - 1;
36469 this.elementOpMap.set(node, opIndex);
36470 this.appendDirectivesAndInputsOfNode(node);
36471 this.appendOutputsOfNode(node);
36472 for (const child of node.children) {
36473 this.appendNode(child);
36474 }
36475 this.checkAndAppendReferencesOfNode(node);
36476 }
36477 else if (node instanceof Template) {
36478 // Template children are rendered in a child scope.
36479 this.appendDirectivesAndInputsOfNode(node);
36480 this.appendOutputsOfNode(node);
36481 const ctxIndex = this.opQueue.push(new TcbTemplateContextOp(this.tcb, this)) - 1;
36482 this.templateCtxOpMap.set(node, ctxIndex);
36483 if (this.tcb.env.config.checkTemplateBodies) {
36484 this.opQueue.push(new TcbTemplateBodyOp(this.tcb, this, node));
36485 }
36486 else if (this.tcb.env.config.alwaysCheckSchemaInTemplateBodies) {
36487 this.appendDeepSchemaChecks(node.children);
36488 }
36489 this.checkAndAppendReferencesOfNode(node);
36490 }
36491 else if (node instanceof BoundText) {
36492 this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, node));
36493 }
36494 else if (node instanceof Icu) {
36495 this.appendIcuExpressions(node);
36496 }
36497 }
36498 checkAndAppendReferencesOfNode(node) {
36499 for (const ref of node.references) {
36500 const target = this.tcb.boundTarget.getReferenceTarget(ref);
36501 let ctxIndex;
36502 if (target === null) {
36503 // The reference is invalid if it doesn't have a target, so report it as an error.
36504 this.tcb.oobRecorder.missingReferenceTarget(this.tcb.id, ref);
36505 // Any usages of the invalid reference will be resolved to a variable of type any.
36506 ctxIndex = this.opQueue.push(new TcbInvalidReferenceOp(this.tcb, this)) - 1;
36507 }
36508 else if (target instanceof Template || target instanceof Element) {
36509 ctxIndex = this.opQueue.push(new TcbReferenceOp(this.tcb, this, ref, node, target)) - 1;
36510 }
36511 else {
36512 ctxIndex =
36513 this.opQueue.push(new TcbReferenceOp(this.tcb, this, ref, node, target.directive)) - 1;
36514 }
36515 this.referenceOpMap.set(ref, ctxIndex);
36516 }
36517 }
36518 appendDirectivesAndInputsOfNode(node) {
36519 // Collect all the inputs on the element.
36520 const claimedInputs = new Set();
36521 const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
36522 if (directives === null || directives.length === 0) {
36523 // If there are no directives, then all inputs are unclaimed inputs, so queue an operation
36524 // to add them if needed.
36525 if (node instanceof Element) {
36526 this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node, claimedInputs));
36527 this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, /* checkElement */ true, claimedInputs));
36528 }
36529 return;
36530 }
36531 const dirMap = new Map();
36532 for (const dir of directives) {
36533 const directiveOp = dir.isGeneric ? new TcbDirectiveCtorOp(this.tcb, this, node, dir) :
36534 new TcbDirectiveTypeOp(this.tcb, this, node, dir);
36535 const dirIndex = this.opQueue.push(directiveOp) - 1;
36536 dirMap.set(dir, dirIndex);
36537 this.opQueue.push(new TcbDirectiveInputsOp(this.tcb, this, node, dir));
36538 }
36539 this.directiveOpMap.set(node, dirMap);
36540 // After expanding the directives, we might need to queue an operation to check any unclaimed
36541 // inputs.
36542 if (node instanceof Element) {
36543 // Go through the directives and remove any inputs that it claims from `elementInputs`.
36544 for (const dir of directives) {
36545 for (const propertyName of dir.inputs.propertyNames) {
36546 claimedInputs.add(propertyName);
36547 }
36548 }
36549 this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node, claimedInputs));
36550 // If there are no directives which match this element, then it's a "plain" DOM element (or a
36551 // web component), and should be checked against the DOM schema. If any directives match,
36552 // we must assume that the element could be custom (either a component, or a directive like
36553 // <router-outlet>) and shouldn't validate the element name itself.
36554 const checkElement = directives.length === 0;
36555 this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, checkElement, claimedInputs));
36556 }
36557 }
36558 appendOutputsOfNode(node) {
36559 // Collect all the outputs on the element.
36560 const claimedOutputs = new Set();
36561 const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
36562 if (directives === null || directives.length === 0) {
36563 // If there are no directives, then all outputs are unclaimed outputs, so queue an operation
36564 // to add them if needed.
36565 if (node instanceof Element) {
36566 this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, claimedOutputs));
36567 }
36568 return;
36569 }
36570 // Queue operations for all directives to check the relevant outputs for a directive.
36571 for (const dir of directives) {
36572 this.opQueue.push(new TcbDirectiveOutputsOp(this.tcb, this, node, dir));
36573 }
36574 // After expanding the directives, we might need to queue an operation to check any unclaimed
36575 // outputs.
36576 if (node instanceof Element) {
36577 // Go through the directives and register any outputs that it claims in `claimedOutputs`.
36578 for (const dir of directives) {
36579 for (const outputProperty of dir.outputs.propertyNames) {
36580 claimedOutputs.add(outputProperty);
36581 }
36582 }
36583 this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, claimedOutputs));
36584 }
36585 }
36586 appendDeepSchemaChecks(nodes) {
36587 for (const node of nodes) {
36588 if (!(node instanceof Element || node instanceof Template)) {
36589 continue;
36590 }
36591 if (node instanceof Element) {
36592 const claimedInputs = new Set();
36593 const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
36594 let hasDirectives;
36595 if (directives === null || directives.length === 0) {
36596 hasDirectives = false;
36597 }
36598 else {
36599 hasDirectives = true;
36600 for (const dir of directives) {
36601 for (const propertyName of dir.inputs.propertyNames) {
36602 claimedInputs.add(propertyName);
36603 }
36604 }
36605 }
36606 this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, !hasDirectives, claimedInputs));
36607 }
36608 this.appendDeepSchemaChecks(node.children);
36609 }
36610 }
36611 appendIcuExpressions(node) {
36612 for (const variable of Object.values(node.vars)) {
36613 this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, variable));
36614 }
36615 for (const placeholder of Object.values(node.placeholders)) {
36616 if (placeholder instanceof BoundText) {
36617 this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, placeholder));
36618 }
36619 }
36620 }
36621 }
36622 /**
36623 * Create the `ctx` parameter to the top-level TCB function.
36624 *
36625 * This is a parameter with a type equivalent to the component type, with all generic type
36626 * parameters listed (without their generic bounds).
36627 */
36628 function tcbCtxParam(node, name, useGenericType) {
36629 let typeArguments = undefined;
36630 // Check if the component is generic, and pass generic type parameters if so.
36631 if (node.typeParameters !== undefined) {
36632 if (useGenericType) {
36633 typeArguments =
36634 node.typeParameters.map(param => ts$1.createTypeReferenceNode(param.name, undefined));
36635 }
36636 else {
36637 typeArguments =
36638 node.typeParameters.map(() => ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
36639 }
36640 }
36641 const type = ts$1.createTypeReferenceNode(name, typeArguments);
36642 return ts$1.createParameter(
36643 /* decorators */ undefined,
36644 /* modifiers */ undefined,
36645 /* dotDotDotToken */ undefined,
36646 /* name */ 'ctx',
36647 /* questionToken */ undefined,
36648 /* type */ type,
36649 /* initializer */ undefined);
36650 }
36651 /**
36652 * Process an `AST` expression and convert it into a `ts.Expression`, generating references to the
36653 * correct identifiers in the current scope.
36654 */
36655 function tcbExpression(ast, tcb, scope) {
36656 const translator = new TcbExpressionTranslator(tcb, scope);
36657 return translator.translate(ast);
36658 }
36659 class TcbExpressionTranslator {
36660 constructor(tcb, scope) {
36661 this.tcb = tcb;
36662 this.scope = scope;
36663 }
36664 translate(ast) {
36665 // `astToTypescript` actually does the conversion. A special resolver `tcbResolve` is passed
36666 // which interprets specific expression nodes that interact with the `ImplicitReceiver`. These
36667 // nodes actually refer to identifiers within the current scope.
36668 return astToTypescript(ast, ast => this.resolve(ast), this.tcb.env.config);
36669 }
36670 /**
36671 * Resolve an `AST` expression within the given scope.
36672 *
36673 * Some `AST` expressions refer to top-level concepts (references, variables, the component
36674 * context). This method assists in resolving those.
36675 */
36676 resolve(ast) {
36677 if (ast instanceof PropertyRead && ast.receiver instanceof ImplicitReceiver) {
36678 // Try to resolve a bound target for this expression. If no such target is available, then
36679 // the expression is referencing the top-level component context. In that case, `null` is
36680 // returned here to let it fall through resolution so it will be caught when the
36681 // `ImplicitReceiver` is resolved in the branch below.
36682 return this.resolveTarget(ast);
36683 }
36684 else if (ast instanceof PropertyWrite && ast.receiver instanceof ImplicitReceiver) {
36685 const target = this.resolveTarget(ast);
36686 if (target === null) {
36687 return null;
36688 }
36689 const expr = this.translate(ast.value);
36690 const result = ts$1.createParen(ts$1.createBinary(target, ts$1.SyntaxKind.EqualsToken, expr));
36691 addParseSpanInfo(result, ast.sourceSpan);
36692 return result;
36693 }
36694 else if (ast instanceof ImplicitReceiver) {
36695 // AST instances representing variables and references look very similar to property reads
36696 // or method calls from the component context: both have the shape
36697 // PropertyRead(ImplicitReceiver, 'propName') or MethodCall(ImplicitReceiver, 'methodName').
36698 //
36699 // `translate` will first try to `resolve` the outer PropertyRead/MethodCall. If this works,
36700 // it's because the `BoundTarget` found an expression target for the whole expression, and
36701 // therefore `translate` will never attempt to `resolve` the ImplicitReceiver of that
36702 // PropertyRead/MethodCall.
36703 //
36704 // Therefore if `resolve` is called on an `ImplicitReceiver`, it's because no outer
36705 // PropertyRead/MethodCall resolved to a variable or reference, and therefore this is a
36706 // property read or method call on the component context itself.
36707 return ts$1.createIdentifier('ctx');
36708 }
36709 else if (ast instanceof BindingPipe) {
36710 const expr = this.translate(ast.exp);
36711 const pipeRef = this.tcb.getPipeByName(ast.name);
36712 let pipe;
36713 if (pipeRef === null) {
36714 // No pipe by that name exists in scope. Record this as an error.
36715 this.tcb.oobRecorder.missingPipe(this.tcb.id, ast);
36716 // Use an 'any' value to at least allow the rest of the expression to be checked.
36717 pipe = NULL_AS_ANY;
36718 }
36719 else if (this.tcb.env.config.checkTypeOfPipes) {
36720 // Use a variable declared as the pipe's type.
36721 pipe = this.tcb.env.pipeInst(pipeRef);
36722 }
36723 else {
36724 // Use an 'any' value when not checking the type of the pipe.
36725 pipe = ts$1.createAsExpression(this.tcb.env.pipeInst(pipeRef), ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
36726 }
36727 const args = ast.args.map(arg => this.translate(arg));
36728 const methodAccess = ts$1.createPropertyAccess(pipe, 'transform');
36729 addParseSpanInfo(methodAccess, ast.nameSpan);
36730 const result = ts$1.createCall(
36731 /* expression */ methodAccess,
36732 /* typeArguments */ undefined,
36733 /* argumentsArray */ [expr, ...args]);
36734 addParseSpanInfo(result, ast.sourceSpan);
36735 return result;
36736 }
36737 else if (ast instanceof MethodCall && ast.receiver instanceof ImplicitReceiver &&
36738 !(ast.receiver instanceof ThisReceiver)) {
36739 // Resolve the special `$any(expr)` syntax to insert a cast of the argument to type `any`.
36740 // `$any(expr)` -> `expr as any`
36741 if (ast.name === '$any' && ast.args.length === 1) {
36742 const expr = this.translate(ast.args[0]);
36743 const exprAsAny = ts$1.createAsExpression(expr, ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
36744 const result = ts$1.createParen(exprAsAny);
36745 addParseSpanInfo(result, ast.sourceSpan);
36746 return result;
36747 }
36748 // Attempt to resolve a bound target for the method, and generate the method call if a target
36749 // could be resolved. If no target is available, then the method is referencing the top-level
36750 // component context, in which case `null` is returned to let the `ImplicitReceiver` being
36751 // resolved to the component context.
36752 const receiver = this.resolveTarget(ast);
36753 if (receiver === null) {
36754 return null;
36755 }
36756 const method = wrapForDiagnostics(receiver);
36757 addParseSpanInfo(method, ast.nameSpan);
36758 const args = ast.args.map(arg => this.translate(arg));
36759 const node = ts$1.createCall(method, undefined, args);
36760 addParseSpanInfo(node, ast.sourceSpan);
36761 return node;
36762 }
36763 else {
36764 // This AST isn't special after all.
36765 return null;
36766 }
36767 }
36768 /**
36769 * Attempts to resolve a bound target for a given expression, and translates it into the
36770 * appropriate `ts.Expression` that represents the bound target. If no target is available,
36771 * `null` is returned.
36772 */
36773 resolveTarget(ast) {
36774 const binding = this.tcb.boundTarget.getExpressionTarget(ast);
36775 if (binding === null) {
36776 return null;
36777 }
36778 const expr = this.scope.resolve(binding);
36779 addParseSpanInfo(expr, ast.sourceSpan);
36780 return expr;
36781 }
36782 }
36783 /**
36784 * Call the type constructor of a directive instance on a given template node, inferring a type for
36785 * the directive instance from any bound inputs.
36786 */
36787 function tcbCallTypeCtor(dir, tcb, inputs) {
36788 const typeCtor = tcb.env.typeCtorFor(dir);
36789 // Construct an array of `ts.PropertyAssignment`s for each of the directive's inputs.
36790 const members = inputs.map(input => {
36791 const propertyName = ts$1.createStringLiteral(input.field);
36792 if (input.type === 'binding') {
36793 // For bound inputs, the property is assigned the binding expression.
36794 let expr = input.expression;
36795 if (!tcb.env.config.checkTypeOfInputBindings) {
36796 // If checking the type of bindings is disabled, cast the resulting expression to 'any'
36797 // before the assignment.
36798 expr = tsCastToAny(expr);
36799 }
36800 else if (!tcb.env.config.strictNullInputBindings) {
36801 // If strict null checks are disabled, erase `null` and `undefined` from the type by
36802 // wrapping the expression in a non-null assertion.
36803 expr = ts$1.createNonNullExpression(expr);
36804 }
36805 const assignment = ts$1.createPropertyAssignment(propertyName, wrapForDiagnostics(expr));
36806 addParseSpanInfo(assignment, input.sourceSpan);
36807 return assignment;
36808 }
36809 else {
36810 // A type constructor is required to be called with all input properties, so any unset
36811 // inputs are simply assigned a value of type `any` to ignore them.
36812 return ts$1.createPropertyAssignment(propertyName, NULL_AS_ANY);
36813 }
36814 });
36815 // Call the `ngTypeCtor` method on the directive class, with an object literal argument created
36816 // from the matched inputs.
36817 return ts$1.createCall(
36818 /* expression */ typeCtor,
36819 /* typeArguments */ undefined,
36820 /* argumentsArray */ [ts$1.createObjectLiteral(members)]);
36821 }
36822 function getBoundInputs(directive, node, tcb) {
36823 const boundInputs = [];
36824 const processAttribute = (attr) => {
36825 // Skip non-property bindings.
36826 if (attr instanceof BoundAttribute && attr.type !== 0 /* Property */) {
36827 return;
36828 }
36829 // Skip the attribute if the directive does not have an input for it.
36830 const inputs = directive.inputs.getByBindingPropertyName(attr.name);
36831 if (inputs === null) {
36832 return;
36833 }
36834 const fieldNames = inputs.map(input => input.classPropertyName);
36835 boundInputs.push({ attribute: attr, fieldNames });
36836 };
36837 node.inputs.forEach(processAttribute);
36838 node.attributes.forEach(processAttribute);
36839 if (node instanceof Template) {
36840 node.templateAttrs.forEach(processAttribute);
36841 }
36842 return boundInputs;
36843 }
36844 /**
36845 * Translates the given attribute binding to a `ts.Expression`.
36846 */
36847 function translateInput(attr, tcb, scope) {
36848 if (attr instanceof BoundAttribute) {
36849 // Produce an expression representing the value of the binding.
36850 return tcbExpression(attr.value, tcb, scope);
36851 }
36852 else {
36853 // For regular attributes with a static string value, use the represented string literal.
36854 return ts$1.createStringLiteral(attr.value);
36855 }
36856 }
36857 const EVENT_PARAMETER = '$event';
36858 /**
36859 * Creates an arrow function to be used as handler function for event bindings. The handler
36860 * function has a single parameter `$event` and the bound event's handler `AST` represented as a
36861 * TypeScript expression as its body.
36862 *
36863 * When `eventType` is set to `Infer`, the `$event` parameter will not have an explicit type. This
36864 * allows for the created handler function to have its `$event` parameter's type inferred based on
36865 * how it's used, to enable strict type checking of event bindings. When set to `Any`, the `$event`
36866 * parameter will have an explicit `any` type, effectively disabling strict type checking of event
36867 * bindings. Alternatively, an explicit type can be passed for the `$event` parameter.
36868 */
36869 function tcbCreateEventHandler(event, tcb, scope, eventType) {
36870 const handler = tcbEventHandlerExpression(event.handler, tcb, scope);
36871 let eventParamType;
36872 if (eventType === 0 /* Infer */) {
36873 eventParamType = undefined;
36874 }
36875 else if (eventType === 1 /* Any */) {
36876 eventParamType = ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword);
36877 }
36878 else {
36879 eventParamType = eventType;
36880 }
36881 // Obtain all guards that have been applied to the scope and its parents, as they have to be
36882 // repeated within the handler function for their narrowing to be in effect within the handler.
36883 const guards = scope.guards();
36884 let body = ts$1.createExpressionStatement(handler);
36885 if (guards !== null) {
36886 // Wrap the body in an `if` statement containing all guards that have to be applied.
36887 body = ts$1.createIf(guards, body);
36888 }
36889 const eventParam = ts$1.createParameter(
36890 /* decorators */ undefined,
36891 /* modifiers */ undefined,
36892 /* dotDotDotToken */ undefined,
36893 /* name */ EVENT_PARAMETER,
36894 /* questionToken */ undefined,
36895 /* type */ eventParamType);
36896 addExpressionIdentifier(eventParam, ExpressionIdentifier.EVENT_PARAMETER);
36897 return ts$1.createFunctionExpression(
36898 /* modifier */ undefined,
36899 /* asteriskToken */ undefined,
36900 /* name */ undefined,
36901 /* typeParameters */ undefined,
36902 /* parameters */ [eventParam],
36903 /* type */ ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword),
36904 /* body */ ts$1.createBlock([body]));
36905 }
36906 /**
36907 * Similar to `tcbExpression`, this function converts the provided `AST` expression into a
36908 * `ts.Expression`, with special handling of the `$event` variable that can be used within event
36909 * bindings.
36910 */
36911 function tcbEventHandlerExpression(ast, tcb, scope) {
36912 const translator = new TcbEventHandlerTranslator(tcb, scope);
36913 return translator.translate(ast);
36914 }
36915 class TcbEventHandlerTranslator extends TcbExpressionTranslator {
36916 resolve(ast) {
36917 // Recognize a property read on the implicit receiver corresponding with the event parameter
36918 // that is available in event bindings. Since this variable is a parameter of the handler
36919 // function that the converted expression becomes a child of, just create a reference to the
36920 // parameter by its name.
36921 if (ast instanceof PropertyRead && ast.receiver instanceof ImplicitReceiver &&
36922 !(ast.receiver instanceof ThisReceiver) && ast.name === EVENT_PARAMETER) {
36923 const event = ts$1.createIdentifier(EVENT_PARAMETER);
36924 addParseSpanInfo(event, ast.nameSpan);
36925 return event;
36926 }
36927 return super.resolve(ast);
36928 }
36929 }
36930
36931 /**
36932 * @license
36933 * Copyright Google LLC All Rights Reserved.
36934 *
36935 * Use of this source code is governed by an MIT-style license that can be
36936 * found in the LICENSE file at https://angular.io/license
36937 */
36938 /**
36939 * An `Environment` representing the single type-checking file into which most (if not all) Type
36940 * Check Blocks (TCBs) will be generated.
36941 *
36942 * The `TypeCheckFile` hosts multiple TCBs and allows the sharing of declarations (e.g. type
36943 * constructors) between them. Rather than return such declarations via `getPreludeStatements()`, it
36944 * hoists them to the top of the generated `ts.SourceFile`.
36945 */
36946 class TypeCheckFile extends Environment {
36947 constructor(fileName, config, refEmitter, reflector, compilerHost) {
36948 super(config, new ImportManager(new NoopImportRewriter(), 'i'), refEmitter, reflector, ts$1.createSourceFile(compilerHost.getCanonicalFileName(fileName), '', ts$1.ScriptTarget.Latest, true));
36949 this.fileName = fileName;
36950 this.nextTcbId = 1;
36951 this.tcbStatements = [];
36952 }
36953 addTypeCheckBlock(ref, meta, domSchemaChecker, oobRecorder) {
36954 const fnId = ts$1.createIdentifier(`_tcb${this.nextTcbId++}`);
36955 const fn = generateTypeCheckBlock(this, ref, fnId, meta, domSchemaChecker, oobRecorder);
36956 this.tcbStatements.push(fn);
36957 }
36958 render() {
36959 let source = this.importManager.getAllImports(this.contextFile.fileName)
36960 .map(i => `import * as ${i.qualifier.text} from '${i.specifier}';`)
36961 .join('\n') +
36962 '\n\n';
36963 const printer = ts$1.createPrinter();
36964 source += '\n';
36965 for (const stmt of this.pipeInstStatements) {
36966 source += printer.printNode(ts$1.EmitHint.Unspecified, stmt, this.contextFile) + '\n';
36967 }
36968 for (const stmt of this.typeCtorStatements) {
36969 source += printer.printNode(ts$1.EmitHint.Unspecified, stmt, this.contextFile) + '\n';
36970 }
36971 source += '\n';
36972 for (const stmt of this.tcbStatements) {
36973 source += printer.printNode(ts$1.EmitHint.Unspecified, stmt, this.contextFile) + '\n';
36974 }
36975 // Ensure the template type-checking file is an ES module. Otherwise, it's interpreted as some
36976 // kind of global namespace in TS, which forces a full re-typecheck of the user's program that
36977 // is somehow more expensive than the initial parse.
36978 source += '\nexport const IS_A_MODULE = true;\n';
36979 return source;
36980 }
36981 getPreludeStatements() {
36982 return [];
36983 }
36984 }
36985
36986 /**
36987 * @license
36988 * Copyright Google LLC All Rights Reserved.
36989 *
36990 * Use of this source code is governed by an MIT-style license that can be
36991 * found in the LICENSE file at https://angular.io/license
36992 */
36993 /**
36994 * How a type-checking context should handle operations which would require inlining.
36995 */
36996 var InliningMode;
36997 (function (InliningMode) {
36998 /**
36999 * Use inlining operations when required.
37000 */
37001 InliningMode[InliningMode["InlineOps"] = 0] = "InlineOps";
37002 /**
37003 * Produce diagnostics if an operation would require inlining.
37004 */
37005 InliningMode[InliningMode["Error"] = 1] = "Error";
37006 })(InliningMode || (InliningMode = {}));
37007 /**
37008 * A template type checking context for a program.
37009 *
37010 * The `TypeCheckContext` allows registration of components and their templates which need to be
37011 * type checked.
37012 */
37013 class TypeCheckContextImpl {
37014 constructor(config, compilerHost, componentMappingStrategy, refEmitter, reflector, host, inlining) {
37015 this.config = config;
37016 this.compilerHost = compilerHost;
37017 this.componentMappingStrategy = componentMappingStrategy;
37018 this.refEmitter = refEmitter;
37019 this.reflector = reflector;
37020 this.host = host;
37021 this.inlining = inlining;
37022 this.fileMap = new Map();
37023 /**
37024 * A `Map` of `ts.SourceFile`s that the context has seen to the operations (additions of methods
37025 * or type-check blocks) that need to be eventually performed on that file.
37026 */
37027 this.opMap = new Map();
37028 /**
37029 * Tracks when an a particular class has a pending type constructor patching operation already
37030 * queued.
37031 */
37032 this.typeCtorPending = new Set();
37033 }
37034 /**
37035 * Register a template to potentially be type-checked.
37036 *
37037 * Implements `TypeCheckContext.addTemplate`.
37038 */
37039 addTemplate(ref, binder, template, pipes, schemas, sourceMapping, file, parseErrors) {
37040 if (!this.host.shouldCheckComponent(ref.node)) {
37041 return;
37042 }
37043 const fileData = this.dataForFile(ref.node.getSourceFile());
37044 const shimData = this.pendingShimForComponent(ref.node);
37045 const templateId = fileData.sourceManager.getTemplateId(ref.node);
37046 const templateDiagnostics = [];
37047 if (parseErrors !== null) {
37048 templateDiagnostics.push(...this.getTemplateDiagnostics(parseErrors, templateId, sourceMapping));
37049 }
37050 // Accumulate a list of any directives which could not have type constructors generated due to
37051 // unsupported inlining operations.
37052 let missingInlines = [];
37053 const boundTarget = binder.bind({ template });
37054 // Get all of the directives used in the template and record type constructors for all of them.
37055 for (const dir of boundTarget.getUsedDirectives()) {
37056 const dirRef = dir.ref;
37057 const dirNode = dirRef.node;
37058 if (dir.isGeneric && requiresInlineTypeCtor(dirNode, this.reflector)) {
37059 if (this.inlining === InliningMode.Error) {
37060 missingInlines.push(dirNode);
37061 continue;
37062 }
37063 // Add a type constructor operation for the directive.
37064 this.addInlineTypeCtor(fileData, dirNode.getSourceFile(), dirRef, {
37065 fnName: 'ngTypeCtor',
37066 // The constructor should have a body if the directive comes from a .ts file, but not if
37067 // it comes from a .d.ts file. .d.ts declarations don't have bodies.
37068 body: !dirNode.getSourceFile().isDeclarationFile,
37069 fields: {
37070 inputs: dir.inputs.classPropertyNames,
37071 outputs: dir.outputs.classPropertyNames,
37072 // TODO(alxhub): support queries
37073 queries: dir.queries,
37074 },
37075 coercedInputFields: dir.coercedInputFields,
37076 });
37077 }
37078 }
37079 shimData.templates.set(templateId, {
37080 template,
37081 boundTarget,
37082 templateDiagnostics,
37083 });
37084 const tcbRequiresInline = requiresInlineTypeCheckBlock(ref.node, pipes);
37085 // If inlining is not supported, but is required for either the TCB or one of its directive
37086 // dependencies, then exit here with an error.
37087 if (this.inlining === InliningMode.Error && (tcbRequiresInline || missingInlines.length > 0)) {
37088 // This template cannot be supported because the underlying strategy does not support inlining
37089 // and inlining would be required.
37090 // Record diagnostics to indicate the issues with this template.
37091 if (tcbRequiresInline) {
37092 shimData.oobRecorder.requiresInlineTcb(templateId, ref.node);
37093 }
37094 if (missingInlines.length > 0) {
37095 shimData.oobRecorder.requiresInlineTypeConstructors(templateId, ref.node, missingInlines);
37096 }
37097 // Checking this template would be unsupported, so don't try.
37098 return;
37099 }
37100 const meta = {
37101 id: fileData.sourceManager.captureSource(ref.node, sourceMapping, file),
37102 boundTarget,
37103 pipes,
37104 schemas,
37105 };
37106 if (tcbRequiresInline) {
37107 // This class didn't meet the requirements for external type checking, so generate an inline
37108 // TCB for the class.
37109 this.addInlineTypeCheckBlock(fileData, shimData, ref, meta);
37110 }
37111 else {
37112 // The class can be type-checked externally as normal.
37113 shimData.file.addTypeCheckBlock(ref, meta, shimData.domSchemaChecker, shimData.oobRecorder);
37114 }
37115 }
37116 /**
37117 * Record a type constructor for the given `node` with the given `ctorMetadata`.
37118 */
37119 addInlineTypeCtor(fileData, sf, ref, ctorMeta) {
37120 if (this.typeCtorPending.has(ref.node)) {
37121 return;
37122 }
37123 this.typeCtorPending.add(ref.node);
37124 // Lazily construct the operation map.
37125 if (!this.opMap.has(sf)) {
37126 this.opMap.set(sf, []);
37127 }
37128 const ops = this.opMap.get(sf);
37129 // Push a `TypeCtorOp` into the operation queue for the source file.
37130 ops.push(new TypeCtorOp(ref, ctorMeta));
37131 fileData.hasInlines = true;
37132 }
37133 /**
37134 * Transform a `ts.SourceFile` into a version that includes type checking code.
37135 *
37136 * If this particular `ts.SourceFile` requires changes, the text representing its new contents
37137 * will be returned. Otherwise, a `null` return indicates no changes were necessary.
37138 */
37139 transform(sf) {
37140 // If there are no operations pending for this particular file, return `null` to indicate no
37141 // changes.
37142 if (!this.opMap.has(sf)) {
37143 return null;
37144 }
37145 // Imports may need to be added to the file to support type-checking of directives used in the
37146 // template within it.
37147 const importManager = new ImportManager(new NoopImportRewriter(), '_i');
37148 // Each Op has a splitPoint index into the text where it needs to be inserted. Split the
37149 // original source text into chunks at these split points, where code will be inserted between
37150 // the chunks.
37151 const ops = this.opMap.get(sf).sort(orderOps);
37152 const textParts = splitStringAtPoints(sf.text, ops.map(op => op.splitPoint));
37153 // Use a `ts.Printer` to generate source code.
37154 const printer = ts$1.createPrinter({ omitTrailingSemicolon: true });
37155 // Begin with the intial section of the code text.
37156 let code = textParts[0];
37157 // Process each operation and use the printer to generate source code for it, inserting it into
37158 // the source code in between the original chunks.
37159 ops.forEach((op, idx) => {
37160 const text = op.execute(importManager, sf, this.refEmitter, printer);
37161 code += '\n\n' + text + textParts[idx + 1];
37162 });
37163 // Write out the imports that need to be added to the beginning of the file.
37164 let imports = importManager.getAllImports(sf.fileName)
37165 .map(i => `import * as ${i.qualifier.text} from '${i.specifier}';`)
37166 .join('\n');
37167 code = imports + '\n' + code;
37168 return code;
37169 }
37170 finalize() {
37171 // First, build the map of updates to source files.
37172 const updates = new Map();
37173 for (const originalSf of this.opMap.keys()) {
37174 const newText = this.transform(originalSf);
37175 if (newText !== null) {
37176 updates.set(absoluteFromSourceFile(originalSf), newText);
37177 }
37178 }
37179 // Then go through each input file that has pending code generation operations.
37180 for (const [sfPath, pendingFileData] of this.fileMap) {
37181 // For each input file, consider generation operations for each of its shims.
37182 for (const pendingShimData of pendingFileData.shimData.values()) {
37183 this.host.recordShimData(sfPath, {
37184 genesisDiagnostics: [
37185 ...pendingShimData.domSchemaChecker.diagnostics,
37186 ...pendingShimData.oobRecorder.diagnostics,
37187 ],
37188 hasInlines: pendingFileData.hasInlines,
37189 path: pendingShimData.file.fileName,
37190 templates: pendingShimData.templates,
37191 });
37192 updates.set(pendingShimData.file.fileName, pendingShimData.file.render());
37193 }
37194 }
37195 return updates;
37196 }
37197 addInlineTypeCheckBlock(fileData, shimData, ref, tcbMeta) {
37198 const sf = ref.node.getSourceFile();
37199 if (!this.opMap.has(sf)) {
37200 this.opMap.set(sf, []);
37201 }
37202 const ops = this.opMap.get(sf);
37203 ops.push(new TcbOp$1(ref, tcbMeta, this.config, this.reflector, shimData.domSchemaChecker, shimData.oobRecorder));
37204 fileData.hasInlines = true;
37205 }
37206 pendingShimForComponent(node) {
37207 const fileData = this.dataForFile(node.getSourceFile());
37208 const shimPath = this.componentMappingStrategy.shimPathForComponent(node);
37209 if (!fileData.shimData.has(shimPath)) {
37210 fileData.shimData.set(shimPath, {
37211 domSchemaChecker: new RegistryDomSchemaChecker(fileData.sourceManager),
37212 oobRecorder: new OutOfBandDiagnosticRecorderImpl(fileData.sourceManager),
37213 file: new TypeCheckFile(shimPath, this.config, this.refEmitter, this.reflector, this.compilerHost),
37214 templates: new Map(),
37215 });
37216 }
37217 return fileData.shimData.get(shimPath);
37218 }
37219 dataForFile(sf) {
37220 const sfPath = absoluteFromSourceFile(sf);
37221 if (!this.fileMap.has(sfPath)) {
37222 const data = {
37223 hasInlines: false,
37224 sourceManager: this.host.getSourceManager(sfPath),
37225 shimData: new Map(),
37226 };
37227 this.fileMap.set(sfPath, data);
37228 }
37229 return this.fileMap.get(sfPath);
37230 }
37231 getTemplateDiagnostics(parseErrors, templateId, sourceMapping) {
37232 return parseErrors.map(error => {
37233 const span = error.span;
37234 if (span.start.offset === span.end.offset) {
37235 // Template errors can contain zero-length spans, if the error occurs at a single point.
37236 // However, TypeScript does not handle displaying a zero-length diagnostic very well, so
37237 // increase the ending offset by 1 for such errors, to ensure the position is shown in the
37238 // diagnostic.
37239 span.end.offset++;
37240 }
37241 return makeTemplateDiagnostic(templateId, sourceMapping, span, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.TEMPLATE_PARSE_ERROR), error.msg);
37242 });
37243 }
37244 }
37245 /**
37246 * A type check block operation which produces type check code for a particular component.
37247 */
37248 class TcbOp$1 {
37249 constructor(ref, meta, config, reflector, domSchemaChecker, oobRecorder) {
37250 this.ref = ref;
37251 this.meta = meta;
37252 this.config = config;
37253 this.reflector = reflector;
37254 this.domSchemaChecker = domSchemaChecker;
37255 this.oobRecorder = oobRecorder;
37256 }
37257 /**
37258 * Type check blocks are inserted immediately after the end of the component class.
37259 */
37260 get splitPoint() {
37261 return this.ref.node.end + 1;
37262 }
37263 execute(im, sf, refEmitter, printer) {
37264 const env = new Environment(this.config, im, refEmitter, this.reflector, sf);
37265 const fnName = ts$1.createIdentifier(`_tcb_${this.ref.node.pos}`);
37266 const fn = generateTypeCheckBlock(env, this.ref, fnName, this.meta, this.domSchemaChecker, this.oobRecorder);
37267 return printer.printNode(ts$1.EmitHint.Unspecified, fn, sf);
37268 }
37269 }
37270 /**
37271 * A type constructor operation which produces type constructor code for a particular directive.
37272 */
37273 class TypeCtorOp {
37274 constructor(ref, meta) {
37275 this.ref = ref;
37276 this.meta = meta;
37277 }
37278 /**
37279 * Type constructor operations are inserted immediately before the end of the directive class.
37280 */
37281 get splitPoint() {
37282 return this.ref.node.end - 1;
37283 }
37284 execute(im, sf, refEmitter, printer) {
37285 const tcb = generateInlineTypeCtor(this.ref.node, this.meta);
37286 return printer.printNode(ts$1.EmitHint.Unspecified, tcb, sf);
37287 }
37288 }
37289 /**
37290 * Compare two operations and return their split point ordering.
37291 */
37292 function orderOps(op1, op2) {
37293 return op1.splitPoint - op2.splitPoint;
37294 }
37295 /**
37296 * Split a string into chunks at any number of split points.
37297 */
37298 function splitStringAtPoints(str, points) {
37299 const splits = [];
37300 let start = 0;
37301 for (let i = 0; i < points.length; i++) {
37302 const point = points[i];
37303 splits.push(str.substring(start, point));
37304 start = point;
37305 }
37306 splits.push(str.substring(start));
37307 return splits;
37308 }
37309
37310 /**
37311 * @license
37312 * Copyright Google LLC All Rights Reserved.
37313 *
37314 * Use of this source code is governed by an MIT-style license that can be
37315 * found in the LICENSE file at https://angular.io/license
37316 */
37317 const LF_CHAR = 10;
37318 const CR_CHAR = 13;
37319 const LINE_SEP_CHAR = 8232;
37320 const PARAGRAPH_CHAR = 8233;
37321 /** Gets the line and character for the given position from the line starts map. */
37322 function getLineAndCharacterFromPosition(lineStartsMap, position) {
37323 const lineIndex = findClosestLineStartPosition(lineStartsMap, position);
37324 return { character: position - lineStartsMap[lineIndex], line: lineIndex };
37325 }
37326 /**
37327 * Computes the line start map of the given text. This can be used in order to
37328 * retrieve the line and character of a given text position index.
37329 */
37330 function computeLineStartsMap(text) {
37331 const result = [0];
37332 let pos = 0;
37333 while (pos < text.length) {
37334 const char = text.charCodeAt(pos++);
37335 // Handles the "CRLF" line break. In that case we peek the character
37336 // after the "CR" and check if it is a line feed.
37337 if (char === CR_CHAR) {
37338 if (text.charCodeAt(pos) === LF_CHAR) {
37339 pos++;
37340 }
37341 result.push(pos);
37342 }
37343 else if (char === LF_CHAR || char === LINE_SEP_CHAR || char === PARAGRAPH_CHAR) {
37344 result.push(pos);
37345 }
37346 }
37347 result.push(pos);
37348 return result;
37349 }
37350 /** Finds the closest line start for the given position. */
37351 function findClosestLineStartPosition(linesMap, position, low = 0, high = linesMap.length - 1) {
37352 while (low <= high) {
37353 const pivotIdx = Math.floor((low + high) / 2);
37354 const pivotEl = linesMap[pivotIdx];
37355 if (pivotEl === position) {
37356 return pivotIdx;
37357 }
37358 else if (position > pivotEl) {
37359 low = pivotIdx + 1;
37360 }
37361 else {
37362 high = pivotIdx - 1;
37363 }
37364 }
37365 // In case there was no exact match, return the closest "lower" line index. We also
37366 // subtract the index by one because want the index of the previous line start.
37367 return low - 1;
37368 }
37369
37370 /**
37371 * @license
37372 * Copyright Google LLC All Rights Reserved.
37373 *
37374 * Use of this source code is governed by an MIT-style license that can be
37375 * found in the LICENSE file at https://angular.io/license
37376 */
37377 /**
37378 * Represents the source of a template that was processed during type-checking. This information is
37379 * used when translating parse offsets in diagnostics back to their original line/column location.
37380 */
37381 class TemplateSource {
37382 constructor(mapping, file) {
37383 this.mapping = mapping;
37384 this.file = file;
37385 this.lineStarts = null;
37386 }
37387 toParseSourceSpan(start, end) {
37388 const startLoc = this.toParseLocation(start);
37389 const endLoc = this.toParseLocation(end);
37390 return new ParseSourceSpan(startLoc, endLoc);
37391 }
37392 toParseLocation(position) {
37393 const lineStarts = this.acquireLineStarts();
37394 const { line, character } = getLineAndCharacterFromPosition(lineStarts, position);
37395 return new ParseLocation(this.file, position, line, character);
37396 }
37397 acquireLineStarts() {
37398 if (this.lineStarts === null) {
37399 this.lineStarts = computeLineStartsMap(this.file.content);
37400 }
37401 return this.lineStarts;
37402 }
37403 }
37404 /**
37405 * Assigns IDs to templates and keeps track of their origins.
37406 *
37407 * Implements `TemplateSourceResolver` to resolve the source of a template based on these IDs.
37408 */
37409 class TemplateSourceManager {
37410 constructor() {
37411 /**
37412 * This map keeps track of all template sources that have been type-checked by the id that is
37413 * attached to a TCB's function declaration as leading trivia. This enables translation of
37414 * diagnostics produced for TCB code to their source location in the template.
37415 */
37416 this.templateSources = new Map();
37417 }
37418 getTemplateId(node) {
37419 return getTemplateId(node);
37420 }
37421 captureSource(node, mapping, file) {
37422 const id = getTemplateId(node);
37423 this.templateSources.set(id, new TemplateSource(mapping, file));
37424 return id;
37425 }
37426 getSourceMapping(id) {
37427 if (!this.templateSources.has(id)) {
37428 throw new Error(`Unexpected unknown template ID: ${id}`);
37429 }
37430 return this.templateSources.get(id).mapping;
37431 }
37432 toParseSourceSpan(id, span) {
37433 if (!this.templateSources.has(id)) {
37434 return null;
37435 }
37436 const templateSource = this.templateSources.get(id);
37437 return templateSource.toParseSourceSpan(span.start, span.end);
37438 }
37439 }
37440
37441 /**
37442 * @license
37443 * Copyright Google LLC All Rights Reserved.
37444 *
37445 * Use of this source code is governed by an MIT-style license that can be
37446 * found in the LICENSE file at https://angular.io/license
37447 */
37448 /**
37449 * Generates and caches `Symbol`s for various template structures for a given component.
37450 *
37451 * The `SymbolBuilder` internally caches the `Symbol`s it creates, and must be destroyed and
37452 * replaced if the component's template changes.
37453 */
37454 class SymbolBuilder {
37455 constructor(shimPath, typeCheckBlock, templateData, componentScopeReader,
37456 // The `ts.TypeChecker` depends on the current type-checking program, and so must be requested
37457 // on-demand instead of cached.
37458 getTypeChecker) {
37459 this.shimPath = shimPath;
37460 this.typeCheckBlock = typeCheckBlock;
37461 this.templateData = templateData;
37462 this.componentScopeReader = componentScopeReader;
37463 this.getTypeChecker = getTypeChecker;
37464 this.symbolCache = new Map();
37465 }
37466 getSymbol(node) {
37467 if (this.symbolCache.has(node)) {
37468 return this.symbolCache.get(node);
37469 }
37470 let symbol = null;
37471 if (node instanceof BoundAttribute || node instanceof TextAttribute) {
37472 // TODO(atscott): input and output bindings only return the first directive match but should
37473 // return a list of bindings for all of them.
37474 symbol = this.getSymbolOfInputBinding(node);
37475 }
37476 else if (node instanceof BoundEvent) {
37477 symbol = this.getSymbolOfBoundEvent(node);
37478 }
37479 else if (node instanceof Element) {
37480 symbol = this.getSymbolOfElement(node);
37481 }
37482 else if (node instanceof Template) {
37483 symbol = this.getSymbolOfAstTemplate(node);
37484 }
37485 else if (node instanceof Variable) {
37486 symbol = this.getSymbolOfVariable(node);
37487 }
37488 else if (node instanceof Reference) {
37489 symbol = this.getSymbolOfReference(node);
37490 }
37491 else if (node instanceof BindingPipe) {
37492 symbol = this.getSymbolOfPipe(node);
37493 }
37494 else if (node instanceof AST) {
37495 symbol = this.getSymbolOfTemplateExpression(node);
37496 }
37497 this.symbolCache.set(node, symbol);
37498 return symbol;
37499 }
37500 getSymbolOfAstTemplate(template) {
37501 const directives = this.getDirectivesOfNode(template);
37502 return { kind: SymbolKind.Template, directives, templateNode: template };
37503 }
37504 getSymbolOfElement(element) {
37505 var _a;
37506 const elementSourceSpan = (_a = element.startSourceSpan) !== null && _a !== void 0 ? _a : element.sourceSpan;
37507 const node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: elementSourceSpan, filter: ts$1.isVariableDeclaration });
37508 if (node === null) {
37509 return null;
37510 }
37511 const symbolFromDeclaration = this.getSymbolOfTsNode(node);
37512 if (symbolFromDeclaration === null || symbolFromDeclaration.tsSymbol === null) {
37513 return null;
37514 }
37515 const directives = this.getDirectivesOfNode(element);
37516 // All statements in the TCB are `Expression`s that optionally include more information.
37517 // An `ElementSymbol` uses the information returned for the variable declaration expression,
37518 // adds the directives for the element, and updates the `kind` to be `SymbolKind.Element`.
37519 return Object.assign(Object.assign({}, symbolFromDeclaration), { kind: SymbolKind.Element, directives, templateNode: element });
37520 }
37521 getDirectivesOfNode(element) {
37522 var _a;
37523 const elementSourceSpan = (_a = element.startSourceSpan) !== null && _a !== void 0 ? _a : element.sourceSpan;
37524 const tcbSourceFile = this.typeCheckBlock.getSourceFile();
37525 // directives could be either:
37526 // - var _t1: TestDir /*T:D*/ = (null!);
37527 // - var _t1 /*T:D*/ = _ctor1({});
37528 const isDirectiveDeclaration = (node) => (ts$1.isTypeNode(node) || ts$1.isIdentifier(node)) && ts$1.isVariableDeclaration(node.parent) &&
37529 hasExpressionIdentifier(tcbSourceFile, node, ExpressionIdentifier.DIRECTIVE);
37530 const nodes = findAllMatchingNodes(this.typeCheckBlock, { withSpan: elementSourceSpan, filter: isDirectiveDeclaration });
37531 return nodes
37532 .map(node => {
37533 var _a;
37534 const symbol = this.getSymbolOfTsNode(node.parent);
37535 if (symbol === null || symbol.tsSymbol === null ||
37536 symbol.tsSymbol.valueDeclaration === undefined ||
37537 !ts$1.isClassDeclaration(symbol.tsSymbol.valueDeclaration)) {
37538 return null;
37539 }
37540 const meta = this.getDirectiveMeta(element, symbol.tsSymbol.valueDeclaration);
37541 if (meta === null) {
37542 return null;
37543 }
37544 const ngModule = this.getDirectiveModule(symbol.tsSymbol.valueDeclaration);
37545 if (meta.selector === null) {
37546 return null;
37547 }
37548 const isComponent = (_a = meta.isComponent) !== null && _a !== void 0 ? _a : null;
37549 const directiveSymbol = Object.assign(Object.assign({}, symbol), { tsSymbol: symbol.tsSymbol, selector: meta.selector, isComponent,
37550 ngModule, kind: SymbolKind.Directive, isStructural: meta.isStructural });
37551 return directiveSymbol;
37552 })
37553 .filter((d) => d !== null);
37554 }
37555 getDirectiveMeta(host, directiveDeclaration) {
37556 var _a;
37557 const directives = this.templateData.boundTarget.getDirectivesOfNode(host);
37558 if (directives === null) {
37559 return null;
37560 }
37561 return (_a = directives.find(m => m.ref.node === directiveDeclaration)) !== null && _a !== void 0 ? _a : null;
37562 }
37563 getDirectiveModule(declaration) {
37564 const scope = this.componentScopeReader.getScopeForComponent(declaration);
37565 if (scope === null) {
37566 return null;
37567 }
37568 return scope.ngModule;
37569 }
37570 getSymbolOfBoundEvent(eventBinding) {
37571 const consumer = this.templateData.boundTarget.getConsumerOfBinding(eventBinding);
37572 if (consumer === null) {
37573 return null;
37574 }
37575 // Outputs in the TCB look like one of the two:
37576 // * _t1["outputField"].subscribe(handler);
37577 // * _t1.addEventListener(handler);
37578 // Even with strict null checks disabled, we still produce the access as a separate statement
37579 // so that it can be found here.
37580 let expectedAccess;
37581 if (consumer instanceof Template || consumer instanceof Element) {
37582 expectedAccess = 'addEventListener';
37583 }
37584 else {
37585 const bindingPropertyNames = consumer.outputs.getByBindingPropertyName(eventBinding.name);
37586 if (bindingPropertyNames === null || bindingPropertyNames.length === 0) {
37587 return null;
37588 }
37589 // Note that we only get the expectedAccess text from a single consumer of the binding. If
37590 // there are multiple consumers (not supported in the `boundTarget` API) and one of them has
37591 // an alias, it will not get matched here.
37592 expectedAccess = bindingPropertyNames[0].classPropertyName;
37593 }
37594 function filter(n) {
37595 if (!isAccessExpression(n)) {
37596 return false;
37597 }
37598 if (ts$1.isPropertyAccessExpression(n)) {
37599 return n.name.getText() === expectedAccess;
37600 }
37601 else {
37602 return ts$1.isStringLiteral(n.argumentExpression) &&
37603 n.argumentExpression.text === expectedAccess;
37604 }
37605 }
37606 const outputFieldAccesses = findAllMatchingNodes(this.typeCheckBlock, { withSpan: eventBinding.keySpan, filter });
37607 const bindings = [];
37608 for (const outputFieldAccess of outputFieldAccesses) {
37609 if (consumer instanceof Template || consumer instanceof Element) {
37610 if (!ts$1.isPropertyAccessExpression(outputFieldAccess)) {
37611 continue;
37612 }
37613 const addEventListener = outputFieldAccess.name;
37614 const tsSymbol = this.getTypeChecker().getSymbolAtLocation(addEventListener);
37615 const tsType = this.getTypeChecker().getTypeAtLocation(addEventListener);
37616 const positionInShimFile = this.getShimPositionForNode(addEventListener);
37617 const target = this.getSymbol(consumer);
37618 if (target === null || tsSymbol === undefined) {
37619 continue;
37620 }
37621 bindings.push({
37622 kind: SymbolKind.Binding,
37623 tsSymbol,
37624 tsType,
37625 target,
37626 shimLocation: { shimPath: this.shimPath, positionInShimFile },
37627 });
37628 }
37629 else {
37630 if (!ts$1.isElementAccessExpression(outputFieldAccess)) {
37631 continue;
37632 }
37633 const tsSymbol = this.getTypeChecker().getSymbolAtLocation(outputFieldAccess.argumentExpression);
37634 if (tsSymbol === undefined) {
37635 continue;
37636 }
37637 const target = this.getDirectiveSymbolForAccessExpression(outputFieldAccess, consumer);
37638 if (target === null) {
37639 continue;
37640 }
37641 const positionInShimFile = this.getShimPositionForNode(outputFieldAccess);
37642 const tsType = this.getTypeChecker().getTypeAtLocation(outputFieldAccess);
37643 bindings.push({
37644 kind: SymbolKind.Binding,
37645 tsSymbol,
37646 tsType,
37647 target,
37648 shimLocation: { shimPath: this.shimPath, positionInShimFile },
37649 });
37650 }
37651 }
37652 if (bindings.length === 0) {
37653 return null;
37654 }
37655 return { kind: SymbolKind.Output, bindings };
37656 }
37657 getSymbolOfInputBinding(binding) {
37658 const consumer = this.templateData.boundTarget.getConsumerOfBinding(binding);
37659 if (consumer === null) {
37660 return null;
37661 }
37662 if (consumer instanceof Element || consumer instanceof Template) {
37663 const host = this.getSymbol(consumer);
37664 return host !== null ? { kind: SymbolKind.DomBinding, host } : null;
37665 }
37666 const nodes = findAllMatchingNodes(this.typeCheckBlock, { withSpan: binding.sourceSpan, filter: isAssignment });
37667 const bindings = [];
37668 for (const node of nodes) {
37669 if (!isAccessExpression(node.left)) {
37670 continue;
37671 }
37672 const symbolInfo = this.getSymbolOfTsNode(node.left);
37673 if (symbolInfo === null || symbolInfo.tsSymbol === null) {
37674 continue;
37675 }
37676 const target = this.getDirectiveSymbolForAccessExpression(node.left, consumer);
37677 if (target === null) {
37678 continue;
37679 }
37680 bindings.push(Object.assign(Object.assign({}, symbolInfo), { tsSymbol: symbolInfo.tsSymbol, kind: SymbolKind.Binding, target }));
37681 }
37682 if (bindings.length === 0) {
37683 return null;
37684 }
37685 return { kind: SymbolKind.Input, bindings };
37686 }
37687 getDirectiveSymbolForAccessExpression(node, { isComponent, selector, isStructural }) {
37688 var _a;
37689 // In either case, `_t1["index"]` or `_t1.index`, `node.expression` is _t1.
37690 // The retrieved symbol for _t1 will be the variable declaration.
37691 const tsSymbol = this.getTypeChecker().getSymbolAtLocation(node.expression);
37692 if (tsSymbol === undefined || tsSymbol.declarations.length === 0 || selector === null) {
37693 return null;
37694 }
37695 const [declaration] = tsSymbol.declarations;
37696 if (!ts$1.isVariableDeclaration(declaration) ||
37697 !hasExpressionIdentifier(
37698 // The expression identifier could be on the type (for regular directives) or the name
37699 // (for generic directives and the ctor op).
37700 declaration.getSourceFile(), (_a = declaration.type) !== null && _a !== void 0 ? _a : declaration.name, ExpressionIdentifier.DIRECTIVE)) {
37701 return null;
37702 }
37703 const symbol = this.getSymbolOfTsNode(declaration);
37704 if (symbol === null || symbol.tsSymbol === null ||
37705 symbol.tsSymbol.valueDeclaration === undefined ||
37706 !ts$1.isClassDeclaration(symbol.tsSymbol.valueDeclaration)) {
37707 return null;
37708 }
37709 const ngModule = this.getDirectiveModule(symbol.tsSymbol.valueDeclaration);
37710 return {
37711 kind: SymbolKind.Directive,
37712 tsSymbol: symbol.tsSymbol,
37713 tsType: symbol.tsType,
37714 shimLocation: symbol.shimLocation,
37715 isComponent,
37716 isStructural,
37717 selector,
37718 ngModule,
37719 };
37720 }
37721 getSymbolOfVariable(variable) {
37722 const node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: variable.sourceSpan, filter: ts$1.isVariableDeclaration });
37723 if (node === null || node.initializer === undefined) {
37724 return null;
37725 }
37726 const expressionSymbol = this.getSymbolOfTsNode(node.initializer);
37727 if (expressionSymbol === null) {
37728 return null;
37729 }
37730 return {
37731 tsType: expressionSymbol.tsType,
37732 tsSymbol: expressionSymbol.tsSymbol,
37733 initializerLocation: expressionSymbol.shimLocation,
37734 kind: SymbolKind.Variable,
37735 declaration: variable,
37736 localVarLocation: {
37737 shimPath: this.shimPath,
37738 positionInShimFile: this.getShimPositionForNode(node.name),
37739 }
37740 };
37741 }
37742 getSymbolOfReference(ref) {
37743 const target = this.templateData.boundTarget.getReferenceTarget(ref);
37744 // Find the node for the reference declaration, i.e. `var _t2 = _t1;`
37745 let node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: ref.sourceSpan, filter: ts$1.isVariableDeclaration });
37746 if (node === null || target === null || node.initializer === undefined) {
37747 return null;
37748 }
37749 // Get the original declaration for the references variable, with the exception of template refs
37750 // which are of the form var _t3 = (_t2 as any as i2.TemplateRef<any>)
37751 // TODO(atscott): Consider adding an `ExpressionIdentifier` to tag variable declaration
37752 // initializers as invalid for symbol retrieval.
37753 const originalDeclaration = ts$1.isParenthesizedExpression(node.initializer) &&
37754 ts$1.isAsExpression(node.initializer.expression) ?
37755 this.getTypeChecker().getSymbolAtLocation(node.name) :
37756 this.getTypeChecker().getSymbolAtLocation(node.initializer);
37757 if (originalDeclaration === undefined || originalDeclaration.valueDeclaration === undefined) {
37758 return null;
37759 }
37760 const symbol = this.getSymbolOfTsNode(originalDeclaration.valueDeclaration);
37761 if (symbol === null || symbol.tsSymbol === null) {
37762 return null;
37763 }
37764 const referenceVarShimLocation = {
37765 shimPath: this.shimPath,
37766 positionInShimFile: this.getShimPositionForNode(node),
37767 };
37768 if (target instanceof Template || target instanceof Element) {
37769 return {
37770 kind: SymbolKind.Reference,
37771 tsSymbol: symbol.tsSymbol,
37772 tsType: symbol.tsType,
37773 target,
37774 declaration: ref,
37775 targetLocation: symbol.shimLocation,
37776 referenceVarLocation: referenceVarShimLocation,
37777 };
37778 }
37779 else {
37780 if (!ts$1.isClassDeclaration(target.directive.ref.node)) {
37781 return null;
37782 }
37783 return {
37784 kind: SymbolKind.Reference,
37785 tsSymbol: symbol.tsSymbol,
37786 tsType: symbol.tsType,
37787 declaration: ref,
37788 target: target.directive.ref.node,
37789 targetLocation: symbol.shimLocation,
37790 referenceVarLocation: referenceVarShimLocation,
37791 };
37792 }
37793 }
37794 getSymbolOfPipe(expression) {
37795 const node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: expression.sourceSpan, filter: ts$1.isCallExpression });
37796 if (node === null || !ts$1.isPropertyAccessExpression(node.expression)) {
37797 return null;
37798 }
37799 const methodAccess = node.expression;
37800 // Find the node for the pipe variable from the transform property access. This will be one of
37801 // two forms: `_pipe1.transform` or `(_pipe1 as any).transform`.
37802 const pipeVariableNode = ts$1.isParenthesizedExpression(methodAccess.expression) &&
37803 ts$1.isAsExpression(methodAccess.expression.expression) ?
37804 methodAccess.expression.expression.expression :
37805 methodAccess.expression;
37806 const pipeDeclaration = this.getTypeChecker().getSymbolAtLocation(pipeVariableNode);
37807 if (pipeDeclaration === undefined || pipeDeclaration.valueDeclaration === undefined) {
37808 return null;
37809 }
37810 const pipeInstance = this.getSymbolOfTsNode(pipeDeclaration.valueDeclaration);
37811 if (pipeInstance === null || pipeInstance.tsSymbol === null) {
37812 return null;
37813 }
37814 const symbolInfo = this.getSymbolOfTsNode(methodAccess);
37815 if (symbolInfo === null) {
37816 return null;
37817 }
37818 return Object.assign(Object.assign({ kind: SymbolKind.Pipe }, symbolInfo), { classSymbol: Object.assign(Object.assign({}, pipeInstance), { tsSymbol: pipeInstance.tsSymbol }) });
37819 }
37820 getSymbolOfTemplateExpression(expression) {
37821 if (expression instanceof ASTWithSource) {
37822 expression = expression.ast;
37823 }
37824 const expressionTarget = this.templateData.boundTarget.getExpressionTarget(expression);
37825 if (expressionTarget !== null) {
37826 return this.getSymbol(expressionTarget);
37827 }
37828 // The `name` part of a `PropertyWrite` and `MethodCall` does not have its own
37829 // AST so there is no way to retrieve a `Symbol` for just the `name` via a specific node.
37830 const withSpan = (expression instanceof PropertyWrite || expression instanceof MethodCall) ?
37831 expression.nameSpan :
37832 expression.sourceSpan;
37833 let node = findFirstMatchingNode(this.typeCheckBlock, { withSpan, filter: (n) => true });
37834 if (node === null) {
37835 return null;
37836 }
37837 while (ts$1.isParenthesizedExpression(node)) {
37838 node = node.expression;
37839 }
37840 // - If we have safe property read ("a?.b") we want to get the Symbol for b, the `whenTrue`
37841 // expression.
37842 // - If our expression is a pipe binding ("a | test:b:c"), we want the Symbol for the
37843 // `transform` on the pipe.
37844 // - Otherwise, we retrieve the symbol for the node itself with no special considerations
37845 if ((expression instanceof SafePropertyRead || expression instanceof SafeMethodCall) &&
37846 ts$1.isConditionalExpression(node)) {
37847 const whenTrueSymbol = (expression instanceof SafeMethodCall && ts$1.isCallExpression(node.whenTrue)) ?
37848 this.getSymbolOfTsNode(node.whenTrue.expression) :
37849 this.getSymbolOfTsNode(node.whenTrue);
37850 if (whenTrueSymbol === null) {
37851 return null;
37852 }
37853 return Object.assign(Object.assign({}, whenTrueSymbol), { kind: SymbolKind.Expression,
37854 // Rather than using the type of only the `whenTrue` part of the expression, we should
37855 // still get the type of the whole conditional expression to include `|undefined`.
37856 tsType: this.getTypeChecker().getTypeAtLocation(node) });
37857 }
37858 else {
37859 const symbolInfo = this.getSymbolOfTsNode(node);
37860 return symbolInfo === null ? null : Object.assign(Object.assign({}, symbolInfo), { kind: SymbolKind.Expression });
37861 }
37862 }
37863 getSymbolOfTsNode(node) {
37864 var _a;
37865 while (ts$1.isParenthesizedExpression(node)) {
37866 node = node.expression;
37867 }
37868 let tsSymbol;
37869 if (ts$1.isPropertyAccessExpression(node)) {
37870 tsSymbol = this.getTypeChecker().getSymbolAtLocation(node.name);
37871 }
37872 else if (ts$1.isElementAccessExpression(node)) {
37873 tsSymbol = this.getTypeChecker().getSymbolAtLocation(node.argumentExpression);
37874 }
37875 else {
37876 tsSymbol = this.getTypeChecker().getSymbolAtLocation(node);
37877 }
37878 const positionInShimFile = this.getShimPositionForNode(node);
37879 const type = this.getTypeChecker().getTypeAtLocation(node);
37880 return {
37881 // If we could not find a symbol, fall back to the symbol on the type for the node.
37882 // Some nodes won't have a "symbol at location" but will have a symbol for the type.
37883 // Examples of this would be literals and `document.createElement('div')`.
37884 tsSymbol: (_a = tsSymbol !== null && tsSymbol !== void 0 ? tsSymbol : type.symbol) !== null && _a !== void 0 ? _a : null,
37885 tsType: type,
37886 shimLocation: { shimPath: this.shimPath, positionInShimFile },
37887 };
37888 }
37889 getShimPositionForNode(node) {
37890 if (ts$1.isTypeReferenceNode(node)) {
37891 return this.getShimPositionForNode(node.typeName);
37892 }
37893 else if (ts$1.isQualifiedName(node)) {
37894 return node.right.getStart();
37895 }
37896 else if (ts$1.isPropertyAccessExpression(node)) {
37897 return node.name.getStart();
37898 }
37899 else if (ts$1.isElementAccessExpression(node)) {
37900 return node.argumentExpression.getStart();
37901 }
37902 else {
37903 return node.getStart();
37904 }
37905 }
37906 }
37907
37908 /**
37909 * @license
37910 * Copyright Google LLC All Rights Reserved.
37911 *
37912 * Use of this source code is governed by an MIT-style license that can be
37913 * found in the LICENSE file at https://angular.io/license
37914 */
37915 const REGISTRY$1 = new DomElementSchemaRegistry();
37916 /**
37917 * Primary template type-checking engine, which performs type-checking using a
37918 * `TypeCheckingProgramStrategy` for type-checking program maintenance, and the
37919 * `ProgramTypeCheckAdapter` for generation of template type-checking code.
37920 */
37921 class TemplateTypeCheckerImpl {
37922 constructor(originalProgram, typeCheckingStrategy, typeCheckAdapter, config, refEmitter, reflector, compilerHost, priorBuild, componentScopeReader, typeCheckScopeRegistry) {
37923 this.originalProgram = originalProgram;
37924 this.typeCheckingStrategy = typeCheckingStrategy;
37925 this.typeCheckAdapter = typeCheckAdapter;
37926 this.config = config;
37927 this.refEmitter = refEmitter;
37928 this.reflector = reflector;
37929 this.compilerHost = compilerHost;
37930 this.priorBuild = priorBuild;
37931 this.componentScopeReader = componentScopeReader;
37932 this.typeCheckScopeRegistry = typeCheckScopeRegistry;
37933 this.state = new Map();
37934 /**
37935 * Stores the `CompletionEngine` which powers autocompletion for each component class.
37936 *
37937 * Must be invalidated whenever the component's template or the `ts.Program` changes. Invalidation
37938 * on template changes is performed within this `TemplateTypeCheckerImpl` instance. When the
37939 * `ts.Program` changes, the `TemplateTypeCheckerImpl` as a whole is destroyed and replaced.
37940 */
37941 this.completionCache = new Map();
37942 /**
37943 * Stores the `SymbolBuilder` which creates symbols for each component class.
37944 *
37945 * Must be invalidated whenever the component's template or the `ts.Program` changes. Invalidation
37946 * on template changes is performed within this `TemplateTypeCheckerImpl` instance. When the
37947 * `ts.Program` changes, the `TemplateTypeCheckerImpl` as a whole is destroyed and replaced.
37948 */
37949 this.symbolBuilderCache = new Map();
37950 /**
37951 * Stores directives and pipes that are in scope for each component.
37952 *
37953 * Unlike other caches, the scope of a component is not affected by its template. It will be
37954 * destroyed when the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is
37955 * destroyed and replaced.
37956 */
37957 this.scopeCache = new Map();
37958 /**
37959 * Stores potential element tags for each component (a union of DOM tags as well as directive
37960 * tags).
37961 *
37962 * Unlike other caches, the scope of a component is not affected by its template. It will be
37963 * destroyed when the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is
37964 * destroyed and replaced.
37965 */
37966 this.elementTagCache = new Map();
37967 this.isComplete = false;
37968 }
37969 getTemplate(component) {
37970 const { data } = this.getLatestComponentState(component);
37971 if (data === null) {
37972 return null;
37973 }
37974 return data.template;
37975 }
37976 getLatestComponentState(component) {
37977 this.ensureShimForComponent(component);
37978 const sf = component.getSourceFile();
37979 const sfPath = absoluteFromSourceFile(sf);
37980 const shimPath = this.typeCheckingStrategy.shimPathForComponent(component);
37981 const fileRecord = this.getFileData(sfPath);
37982 if (!fileRecord.shimData.has(shimPath)) {
37983 return { data: null, tcb: null, shimPath };
37984 }
37985 const templateId = fileRecord.sourceManager.getTemplateId(component);
37986 const shimRecord = fileRecord.shimData.get(shimPath);
37987 const id = fileRecord.sourceManager.getTemplateId(component);
37988 const program = this.typeCheckingStrategy.getProgram();
37989 const shimSf = getSourceFileOrNull(program, shimPath);
37990 if (shimSf === null || !fileRecord.shimData.has(shimPath)) {
37991 throw new Error(`Error: no shim file in program: ${shimPath}`);
37992 }
37993 let tcb = findTypeCheckBlock(shimSf, id, /*isDiagnosticsRequest*/ false);
37994 if (tcb === null) {
37995 // Try for an inline block.
37996 const inlineSf = getSourceFileOrError(program, sfPath);
37997 tcb = findTypeCheckBlock(inlineSf, id, /*isDiagnosticsRequest*/ false);
37998 }
37999 let data = null;
38000 if (shimRecord.templates.has(templateId)) {
38001 data = shimRecord.templates.get(templateId);
38002 }
38003 return { data, tcb, shimPath };
38004 }
38005 isTrackedTypeCheckFile(filePath) {
38006 return this.getFileAndShimRecordsForPath(filePath) !== null;
38007 }
38008 getFileAndShimRecordsForPath(shimPath) {
38009 for (const fileRecord of this.state.values()) {
38010 if (fileRecord.shimData.has(shimPath)) {
38011 return { fileRecord, shimRecord: fileRecord.shimData.get(shimPath) };
38012 }
38013 }
38014 return null;
38015 }
38016 getTemplateMappingAtShimLocation({ shimPath, positionInShimFile }) {
38017 const records = this.getFileAndShimRecordsForPath(absoluteFrom(shimPath));
38018 if (records === null) {
38019 return null;
38020 }
38021 const { fileRecord } = records;
38022 const shimSf = this.typeCheckingStrategy.getProgram().getSourceFile(absoluteFrom(shimPath));
38023 if (shimSf === undefined) {
38024 return null;
38025 }
38026 return getTemplateMapping(shimSf, positionInShimFile, fileRecord.sourceManager, /*isDiagnosticsRequest*/ false);
38027 }
38028 generateAllTypeCheckBlocks() {
38029 this.ensureAllShimsForAllFiles();
38030 }
38031 /**
38032 * Retrieve type-checking and template parse diagnostics from the given `ts.SourceFile` using the
38033 * most recent type-checking program.
38034 */
38035 getDiagnosticsForFile(sf, optimizeFor) {
38036 switch (optimizeFor) {
38037 case OptimizeFor.WholeProgram:
38038 this.ensureAllShimsForAllFiles();
38039 break;
38040 case OptimizeFor.SingleFile:
38041 this.ensureAllShimsForOneFile(sf);
38042 break;
38043 }
38044 const sfPath = absoluteFromSourceFile(sf);
38045 const fileRecord = this.state.get(sfPath);
38046 const typeCheckProgram = this.typeCheckingStrategy.getProgram();
38047 const diagnostics = [];
38048 if (fileRecord.hasInlines) {
38049 const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
38050 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38051 }
38052 for (const [shimPath, shimRecord] of fileRecord.shimData) {
38053 const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
38054 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38055 diagnostics.push(...shimRecord.genesisDiagnostics);
38056 for (const templateData of shimRecord.templates.values()) {
38057 diagnostics.push(...templateData.templateDiagnostics);
38058 }
38059 }
38060 return diagnostics.filter((diag) => diag !== null);
38061 }
38062 getDiagnosticsForComponent(component) {
38063 this.ensureShimForComponent(component);
38064 const sf = component.getSourceFile();
38065 const sfPath = absoluteFromSourceFile(sf);
38066 const shimPath = this.typeCheckingStrategy.shimPathForComponent(component);
38067 const fileRecord = this.getFileData(sfPath);
38068 if (!fileRecord.shimData.has(shimPath)) {
38069 return [];
38070 }
38071 const templateId = fileRecord.sourceManager.getTemplateId(component);
38072 const shimRecord = fileRecord.shimData.get(shimPath);
38073 const typeCheckProgram = this.typeCheckingStrategy.getProgram();
38074 const diagnostics = [];
38075 if (shimRecord.hasInlines) {
38076 const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
38077 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38078 }
38079 const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
38080 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38081 diagnostics.push(...shimRecord.genesisDiagnostics);
38082 for (const templateData of shimRecord.templates.values()) {
38083 diagnostics.push(...templateData.templateDiagnostics);
38084 }
38085 return diagnostics.filter((diag) => diag !== null && diag.templateId === templateId);
38086 }
38087 getTypeCheckBlock(component) {
38088 return this.getLatestComponentState(component).tcb;
38089 }
38090 getGlobalCompletions(context, component) {
38091 const engine = this.getOrCreateCompletionEngine(component);
38092 if (engine === null) {
38093 return null;
38094 }
38095 return engine.getGlobalCompletions(context);
38096 }
38097 getExpressionCompletionLocation(ast, component) {
38098 const engine = this.getOrCreateCompletionEngine(component);
38099 if (engine === null) {
38100 return null;
38101 }
38102 return engine.getExpressionCompletionLocation(ast);
38103 }
38104 invalidateClass(clazz) {
38105 this.completionCache.delete(clazz);
38106 this.symbolBuilderCache.delete(clazz);
38107 this.scopeCache.delete(clazz);
38108 this.elementTagCache.delete(clazz);
38109 const sf = clazz.getSourceFile();
38110 const sfPath = absoluteFromSourceFile(sf);
38111 const shimPath = this.typeCheckingStrategy.shimPathForComponent(clazz);
38112 const fileData = this.getFileData(sfPath);
38113 const templateId = fileData.sourceManager.getTemplateId(clazz);
38114 fileData.shimData.delete(shimPath);
38115 fileData.isComplete = false;
38116 this.isComplete = false;
38117 }
38118 getOrCreateCompletionEngine(component) {
38119 if (this.completionCache.has(component)) {
38120 return this.completionCache.get(component);
38121 }
38122 const { tcb, data, shimPath } = this.getLatestComponentState(component);
38123 if (tcb === null || data === null) {
38124 return null;
38125 }
38126 const engine = new CompletionEngine(tcb, data, shimPath);
38127 this.completionCache.set(component, engine);
38128 return engine;
38129 }
38130 maybeAdoptPriorResultsForFile(sf) {
38131 const sfPath = absoluteFromSourceFile(sf);
38132 if (this.state.has(sfPath)) {
38133 const existingResults = this.state.get(sfPath);
38134 if (existingResults.isComplete) {
38135 // All data for this file has already been generated, so no need to adopt anything.
38136 return;
38137 }
38138 }
38139 const previousResults = this.priorBuild.priorTypeCheckingResultsFor(sf);
38140 if (previousResults === null || !previousResults.isComplete) {
38141 return;
38142 }
38143 this.state.set(sfPath, previousResults);
38144 }
38145 ensureAllShimsForAllFiles() {
38146 if (this.isComplete) {
38147 return;
38148 }
38149 const host = new WholeProgramTypeCheckingHost(this);
38150 const ctx = this.newContext(host);
38151 for (const sf of this.originalProgram.getSourceFiles()) {
38152 if (sf.isDeclarationFile || isShim(sf)) {
38153 continue;
38154 }
38155 this.maybeAdoptPriorResultsForFile(sf);
38156 const sfPath = absoluteFromSourceFile(sf);
38157 const fileData = this.getFileData(sfPath);
38158 if (fileData.isComplete) {
38159 continue;
38160 }
38161 this.typeCheckAdapter.typeCheck(sf, ctx);
38162 fileData.isComplete = true;
38163 }
38164 this.updateFromContext(ctx);
38165 this.isComplete = true;
38166 }
38167 ensureAllShimsForOneFile(sf) {
38168 this.maybeAdoptPriorResultsForFile(sf);
38169 const sfPath = absoluteFromSourceFile(sf);
38170 const fileData = this.getFileData(sfPath);
38171 if (fileData.isComplete) {
38172 // All data for this file is present and accounted for already.
38173 return;
38174 }
38175 const host = new SingleFileTypeCheckingHost(sfPath, fileData, this.typeCheckingStrategy, this);
38176 const ctx = this.newContext(host);
38177 this.typeCheckAdapter.typeCheck(sf, ctx);
38178 fileData.isComplete = true;
38179 this.updateFromContext(ctx);
38180 }
38181 ensureShimForComponent(component) {
38182 const sf = component.getSourceFile();
38183 const sfPath = absoluteFromSourceFile(sf);
38184 this.maybeAdoptPriorResultsForFile(sf);
38185 const fileData = this.getFileData(sfPath);
38186 const shimPath = this.typeCheckingStrategy.shimPathForComponent(component);
38187 if (fileData.shimData.has(shimPath)) {
38188 // All data for this component is available.
38189 return;
38190 }
38191 const host = new SingleShimTypeCheckingHost(sfPath, fileData, this.typeCheckingStrategy, this, shimPath);
38192 const ctx = this.newContext(host);
38193 this.typeCheckAdapter.typeCheck(sf, ctx);
38194 this.updateFromContext(ctx);
38195 }
38196 newContext(host) {
38197 const inlining = this.typeCheckingStrategy.supportsInlineOperations ? InliningMode.InlineOps :
38198 InliningMode.Error;
38199 return new TypeCheckContextImpl(this.config, this.compilerHost, this.typeCheckingStrategy, this.refEmitter, this.reflector, host, inlining);
38200 }
38201 /**
38202 * Remove any shim data that depends on inline operations applied to the type-checking program.
38203 *
38204 * This can be useful if new inlines need to be applied, and it's not possible to guarantee that
38205 * they won't overwrite or corrupt existing inlines that are used by such shims.
38206 */
38207 clearAllShimDataUsingInlines() {
38208 for (const fileData of this.state.values()) {
38209 if (!fileData.hasInlines) {
38210 continue;
38211 }
38212 for (const [shimFile, shimData] of fileData.shimData.entries()) {
38213 if (shimData.hasInlines) {
38214 fileData.shimData.delete(shimFile);
38215 }
38216 }
38217 fileData.hasInlines = false;
38218 fileData.isComplete = false;
38219 this.isComplete = false;
38220 }
38221 }
38222 updateFromContext(ctx) {
38223 const updates = ctx.finalize();
38224 this.typeCheckingStrategy.updateFiles(updates, UpdateMode.Incremental);
38225 this.priorBuild.recordSuccessfulTypeCheck(this.state);
38226 }
38227 getFileData(path) {
38228 if (!this.state.has(path)) {
38229 this.state.set(path, {
38230 hasInlines: false,
38231 sourceManager: new TemplateSourceManager(),
38232 isComplete: false,
38233 shimData: new Map(),
38234 });
38235 }
38236 return this.state.get(path);
38237 }
38238 getSymbolOfNode(node, component) {
38239 const builder = this.getOrCreateSymbolBuilder(component);
38240 if (builder === null) {
38241 return null;
38242 }
38243 return builder.getSymbol(node);
38244 }
38245 getOrCreateSymbolBuilder(component) {
38246 if (this.symbolBuilderCache.has(component)) {
38247 return this.symbolBuilderCache.get(component);
38248 }
38249 const { tcb, data, shimPath } = this.getLatestComponentState(component);
38250 if (tcb === null || data === null) {
38251 return null;
38252 }
38253 const builder = new SymbolBuilder(shimPath, tcb, data, this.componentScopeReader, () => this.typeCheckingStrategy.getProgram().getTypeChecker());
38254 this.symbolBuilderCache.set(component, builder);
38255 return builder;
38256 }
38257 getDirectivesInScope(component) {
38258 const data = this.getScopeData(component);
38259 if (data === null) {
38260 return null;
38261 }
38262 return data.directives;
38263 }
38264 getPipesInScope(component) {
38265 const data = this.getScopeData(component);
38266 if (data === null) {
38267 return null;
38268 }
38269 return data.pipes;
38270 }
38271 getDirectiveMetadata(dir) {
38272 if (!isNamedClassDeclaration(dir)) {
38273 return null;
38274 }
38275 return this.typeCheckScopeRegistry.getTypeCheckDirectiveMetadata(new Reference$1(dir));
38276 }
38277 getPotentialElementTags(component) {
38278 if (this.elementTagCache.has(component)) {
38279 return this.elementTagCache.get(component);
38280 }
38281 const tagMap = new Map();
38282 for (const tag of REGISTRY$1.allKnownElementNames()) {
38283 tagMap.set(tag, null);
38284 }
38285 const scope = this.getScopeData(component);
38286 if (scope !== null) {
38287 for (const directive of scope.directives) {
38288 for (const selector of CssSelector.parse(directive.selector)) {
38289 if (selector.element === null || tagMap.has(selector.element)) {
38290 // Skip this directive if it doesn't match an element tag, or if another directive has
38291 // already been included with the same element name.
38292 continue;
38293 }
38294 tagMap.set(selector.element, directive);
38295 }
38296 }
38297 }
38298 this.elementTagCache.set(component, tagMap);
38299 return tagMap;
38300 }
38301 getPotentialDomBindings(tagName) {
38302 const attributes = REGISTRY$1.allKnownAttributesOfElement(tagName);
38303 return attributes.map(attribute => ({
38304 attribute,
38305 property: REGISTRY$1.getMappedPropName(attribute),
38306 }));
38307 }
38308 getScopeData(component) {
38309 if (this.scopeCache.has(component)) {
38310 return this.scopeCache.get(component);
38311 }
38312 if (!isNamedClassDeclaration(component)) {
38313 throw new Error(`AssertionError: components must have names`);
38314 }
38315 const scope = this.componentScopeReader.getScopeForComponent(component);
38316 if (scope === null) {
38317 return null;
38318 }
38319 const data = {
38320 directives: [],
38321 pipes: [],
38322 isPoisoned: scope.compilation.isPoisoned,
38323 };
38324 const typeChecker = this.typeCheckingStrategy.getProgram().getTypeChecker();
38325 for (const dir of scope.compilation.directives) {
38326 if (dir.selector === null) {
38327 // Skip this directive, it can't be added to a template anyway.
38328 continue;
38329 }
38330 const tsSymbol = typeChecker.getSymbolAtLocation(dir.ref.node.name);
38331 if (tsSymbol === undefined) {
38332 continue;
38333 }
38334 let ngModule = null;
38335 const moduleScopeOfDir = this.componentScopeReader.getScopeForComponent(dir.ref.node);
38336 if (moduleScopeOfDir !== null) {
38337 ngModule = moduleScopeOfDir.ngModule;
38338 }
38339 data.directives.push({
38340 isComponent: dir.isComponent,
38341 isStructural: dir.isStructural,
38342 selector: dir.selector,
38343 tsSymbol,
38344 ngModule,
38345 });
38346 }
38347 for (const pipe of scope.compilation.pipes) {
38348 const tsSymbol = typeChecker.getSymbolAtLocation(pipe.ref.node.name);
38349 if (tsSymbol === undefined) {
38350 continue;
38351 }
38352 data.pipes.push({
38353 name: pipe.name,
38354 tsSymbol,
38355 });
38356 }
38357 this.scopeCache.set(component, data);
38358 return data;
38359 }
38360 }
38361 function convertDiagnostic(diag, sourceResolver) {
38362 if (!shouldReportDiagnostic(diag)) {
38363 return null;
38364 }
38365 return translateDiagnostic(diag, sourceResolver);
38366 }
38367 /**
38368 * Drives a `TypeCheckContext` to generate type-checking code for every component in the program.
38369 */
38370 class WholeProgramTypeCheckingHost {
38371 constructor(impl) {
38372 this.impl = impl;
38373 }
38374 getSourceManager(sfPath) {
38375 return this.impl.getFileData(sfPath).sourceManager;
38376 }
38377 shouldCheckComponent(node) {
38378 const fileData = this.impl.getFileData(absoluteFromSourceFile(node.getSourceFile()));
38379 const shimPath = this.impl.typeCheckingStrategy.shimPathForComponent(node);
38380 // The component needs to be checked unless the shim which would contain it already exists.
38381 return !fileData.shimData.has(shimPath);
38382 }
38383 recordShimData(sfPath, data) {
38384 const fileData = this.impl.getFileData(sfPath);
38385 fileData.shimData.set(data.path, data);
38386 if (data.hasInlines) {
38387 fileData.hasInlines = true;
38388 }
38389 }
38390 recordComplete(sfPath) {
38391 this.impl.getFileData(sfPath).isComplete = true;
38392 }
38393 }
38394 /**
38395 * Drives a `TypeCheckContext` to generate type-checking code efficiently for a single input file.
38396 */
38397 class SingleFileTypeCheckingHost {
38398 constructor(sfPath, fileData, strategy, impl) {
38399 this.sfPath = sfPath;
38400 this.fileData = fileData;
38401 this.strategy = strategy;
38402 this.impl = impl;
38403 this.seenInlines = false;
38404 }
38405 assertPath(sfPath) {
38406 if (this.sfPath !== sfPath) {
38407 throw new Error(`AssertionError: querying TypeCheckingHost outside of assigned file`);
38408 }
38409 }
38410 getSourceManager(sfPath) {
38411 this.assertPath(sfPath);
38412 return this.fileData.sourceManager;
38413 }
38414 shouldCheckComponent(node) {
38415 if (this.sfPath !== absoluteFromSourceFile(node.getSourceFile())) {
38416 return false;
38417 }
38418 const shimPath = this.strategy.shimPathForComponent(node);
38419 // Only need to generate a TCB for the class if no shim exists for it currently.
38420 return !this.fileData.shimData.has(shimPath);
38421 }
38422 recordShimData(sfPath, data) {
38423 this.assertPath(sfPath);
38424 // Previous type-checking state may have required the use of inlines (assuming they were
38425 // supported). If the current operation also requires inlines, this presents a problem:
38426 // generating new inlines may invalidate any old inlines that old state depends on.
38427 //
38428 // Rather than resolve this issue by tracking specific dependencies on inlines, if the new state
38429 // relies on inlines, any old state that relied on them is simply cleared. This happens when the
38430 // first new state that uses inlines is encountered.
38431 if (data.hasInlines && !this.seenInlines) {
38432 this.impl.clearAllShimDataUsingInlines();
38433 this.seenInlines = true;
38434 }
38435 this.fileData.shimData.set(data.path, data);
38436 if (data.hasInlines) {
38437 this.fileData.hasInlines = true;
38438 }
38439 }
38440 recordComplete(sfPath) {
38441 this.assertPath(sfPath);
38442 this.fileData.isComplete = true;
38443 }
38444 }
38445 /**
38446 * Drives a `TypeCheckContext` to generate type-checking code efficiently for only those components
38447 * which map to a single shim of a single input file.
38448 */
38449 class SingleShimTypeCheckingHost extends SingleFileTypeCheckingHost {
38450 constructor(sfPath, fileData, strategy, impl, shimPath) {
38451 super(sfPath, fileData, strategy, impl);
38452 this.shimPath = shimPath;
38453 }
38454 shouldCheckNode(node) {
38455 if (this.sfPath !== absoluteFromSourceFile(node.getSourceFile())) {
38456 return false;
38457 }
38458 // Only generate a TCB for the component if it maps to the requested shim file.
38459 const shimPath = this.strategy.shimPathForComponent(node);
38460 if (shimPath !== this.shimPath) {
38461 return false;
38462 }
38463 // Only need to generate a TCB for the class if no shim exists for it currently.
38464 return !this.fileData.shimData.has(shimPath);
38465 }
38466 }
38467
38468 /**
38469 * @license
38470 * Copyright Google LLC All Rights Reserved.
38471 *
38472 * Use of this source code is governed by an MIT-style license that can be
38473 * found in the LICENSE file at https://angular.io/license
38474 */
38475 // This file exists as a target for g3 patches which change the Angular compiler's behavior.
38476 // Separating the patched code in a separate file eliminates the possibility of conflicts with the
38477 // patch diffs when making changes to the rest of the compiler codebase.
38478 // In ngtsc we no longer want to compile undecorated classes with Angular features.
38479 // Migrations for these patterns ran as part of `ng update` and we want to ensure
38480 // that projects do not regress. See https://hackmd.io/@alx/ryfYYuvzH for more details.
38481 const compileUndecoratedClassesWithAngularFeatures = false;
38482
38483 /**
38484 * @license
38485 * Copyright Google LLC All Rights Reserved.
38486 *
38487 * Use of this source code is governed by an MIT-style license that can be
38488 * found in the LICENSE file at https://angular.io/license
38489 */
38490 /**
38491 * Discriminant type for a `CompilationTicket`.
38492 */
38493 var CompilationTicketKind;
38494 (function (CompilationTicketKind) {
38495 CompilationTicketKind[CompilationTicketKind["Fresh"] = 0] = "Fresh";
38496 CompilationTicketKind[CompilationTicketKind["IncrementalTypeScript"] = 1] = "IncrementalTypeScript";
38497 CompilationTicketKind[CompilationTicketKind["IncrementalResource"] = 2] = "IncrementalResource";
38498 })(CompilationTicketKind || (CompilationTicketKind = {}));
38499 /**
38500 * Create a `CompilationTicket` for a brand new compilation, using no prior state.
38501 */
38502 function freshCompilationTicket(tsProgram, options, incrementalBuildStrategy, typeCheckingProgramStrategy, enableTemplateTypeChecker, usePoisonedData) {
38503 return {
38504 kind: CompilationTicketKind.Fresh,
38505 tsProgram,
38506 options,
38507 incrementalBuildStrategy,
38508 typeCheckingProgramStrategy,
38509 enableTemplateTypeChecker,
38510 usePoisonedData,
38511 };
38512 }
38513 /**
38514 * Create a `CompilationTicket` as efficiently as possible, based on a previous `NgCompiler`
38515 * instance and a new `ts.Program`.
38516 */
38517 function incrementalFromCompilerTicket(oldCompiler, newProgram, incrementalBuildStrategy, typeCheckingProgramStrategy, modifiedResourceFiles) {
38518 const oldProgram = oldCompiler.getNextProgram();
38519 const oldDriver = oldCompiler.incrementalStrategy.getIncrementalDriver(oldProgram);
38520 if (oldDriver === null) {
38521 // No incremental step is possible here, since no IncrementalDriver was found for the old
38522 // program.
38523 return freshCompilationTicket(newProgram, oldCompiler.options, incrementalBuildStrategy, typeCheckingProgramStrategy, oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData);
38524 }
38525 const newDriver = IncrementalDriver.reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles);
38526 return {
38527 kind: CompilationTicketKind.IncrementalTypeScript,
38528 enableTemplateTypeChecker: oldCompiler.enableTemplateTypeChecker,
38529 usePoisonedData: oldCompiler.usePoisonedData,
38530 options: oldCompiler.options,
38531 incrementalBuildStrategy,
38532 typeCheckingProgramStrategy,
38533 newDriver,
38534 oldProgram,
38535 newProgram,
38536 };
38537 }
38538 function resourceChangeTicket(compiler, modifiedResourceFiles) {
38539 return {
38540 kind: CompilationTicketKind.IncrementalResource,
38541 compiler,
38542 modifiedResourceFiles,
38543 };
38544 }
38545 /**
38546 * The heart of the Angular Ivy compiler.
38547 *
38548 * The `NgCompiler` provides an API for performing Angular compilation within a custom TypeScript
38549 * compiler. Each instance of `NgCompiler` supports a single compilation, which might be
38550 * incremental.
38551 *
38552 * `NgCompiler` is lazy, and does not perform any of the work of the compilation until one of its
38553 * output methods (e.g. `getDiagnostics`) is called.
38554 *
38555 * See the README.md for more information.
38556 */
38557 class NgCompiler {
38558 constructor(adapter, options, tsProgram, typeCheckingProgramStrategy, incrementalStrategy, incrementalDriver, enableTemplateTypeChecker, usePoisonedData, perfRecorder = NOOP_PERF_RECORDER) {
38559 this.adapter = adapter;
38560 this.options = options;
38561 this.tsProgram = tsProgram;
38562 this.typeCheckingProgramStrategy = typeCheckingProgramStrategy;
38563 this.incrementalStrategy = incrementalStrategy;
38564 this.incrementalDriver = incrementalDriver;
38565 this.enableTemplateTypeChecker = enableTemplateTypeChecker;
38566 this.usePoisonedData = usePoisonedData;
38567 this.perfRecorder = perfRecorder;
38568 /**
38569 * Lazily evaluated state of the compilation.
38570 *
38571 * This is created on demand by calling `ensureAnalyzed`.
38572 */
38573 this.compilation = null;
38574 /**
38575 * Any diagnostics related to the construction of the compilation.
38576 *
38577 * These are diagnostics which arose during setup of the host and/or program.
38578 */
38579 this.constructionDiagnostics = [];
38580 /**
38581 * Non-template diagnostics related to the program itself. Does not include template
38582 * diagnostics because the template type checker memoizes them itself.
38583 *
38584 * This is set by (and memoizes) `getNonTemplateDiagnostics`.
38585 */
38586 this.nonTemplateDiagnostics = null;
38587 this.constructionDiagnostics.push(...this.adapter.constructionDiagnostics);
38588 const incompatibleTypeCheckOptionsDiagnostic = verifyCompatibleTypeCheckOptions(this.options);
38589 if (incompatibleTypeCheckOptionsDiagnostic !== null) {
38590 this.constructionDiagnostics.push(incompatibleTypeCheckOptionsDiagnostic);
38591 }
38592 this.nextProgram = tsProgram;
38593 this.closureCompilerEnabled = !!this.options.annotateForClosureCompiler;
38594 this.entryPoint =
38595 adapter.entryPoint !== null ? getSourceFileOrNull(tsProgram, adapter.entryPoint) : null;
38596 const moduleResolutionCache = ts$1.createModuleResolutionCache(this.adapter.getCurrentDirectory(),
38597 // Note: this used to be an arrow-function closure. However, JS engines like v8 have some
38598 // strange behaviors with retaining the lexical scope of the closure. Even if this function
38599 // doesn't retain a reference to `this`, if other closures in the constructor here reference
38600 // `this` internally then a closure created here would retain them. This can cause major
38601 // memory leak issues since the `moduleResolutionCache` is a long-lived object and finds its
38602 // way into all kinds of places inside TS internal objects.
38603 this.adapter.getCanonicalFileName.bind(this.adapter));
38604 this.moduleResolver =
38605 new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache);
38606 this.resourceManager = new AdapterResourceLoader(adapter, this.options);
38607 this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(this.moduleResolver));
38608 this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, tsProgram);
38609 this.ignoreForDiagnostics =
38610 new Set(tsProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf)));
38611 this.ignoreForEmit = this.adapter.ignoreForEmit;
38612 }
38613 /**
38614 * Convert a `CompilationTicket` into an `NgCompiler` instance for the requested compilation.
38615 *
38616 * Depending on the nature of the compilation request, the `NgCompiler` instance may be reused
38617 * from a previous compilation and updated with any changes, it may be a new instance which
38618 * incrementally reuses state from a previous compilation, or it may represent a fresh compilation
38619 * entirely.
38620 */
38621 static fromTicket(ticket, adapter, perfRecorder) {
38622 switch (ticket.kind) {
38623 case CompilationTicketKind.Fresh:
38624 return new NgCompiler(adapter, ticket.options, ticket.tsProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, IncrementalDriver.fresh(ticket.tsProgram), ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
38625 case CompilationTicketKind.IncrementalTypeScript:
38626 return new NgCompiler(adapter, ticket.options, ticket.newProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, ticket.newDriver, ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
38627 case CompilationTicketKind.IncrementalResource:
38628 const compiler = ticket.compiler;
38629 compiler.updateWithChangedResources(ticket.modifiedResourceFiles);
38630 return compiler;
38631 }
38632 }
38633 updateWithChangedResources(changedResources) {
38634 if (this.compilation === null) {
38635 // Analysis hasn't happened yet, so no update is necessary - any changes to resources will be
38636 // captured by the inital analysis pass itself.
38637 return;
38638 }
38639 this.resourceManager.invalidate();
38640 const classesToUpdate = new Set();
38641 for (const resourceFile of changedResources) {
38642 for (const templateClass of this.getComponentsWithTemplateFile(resourceFile)) {
38643 classesToUpdate.add(templateClass);
38644 }
38645 for (const styleClass of this.getComponentsWithStyleFile(resourceFile)) {
38646 classesToUpdate.add(styleClass);
38647 }
38648 }
38649 for (const clazz of classesToUpdate) {
38650 this.compilation.traitCompiler.updateResources(clazz);
38651 if (!ts$1.isClassDeclaration(clazz)) {
38652 continue;
38653 }
38654 this.compilation.templateTypeChecker.invalidateClass(clazz);
38655 }
38656 }
38657 /**
38658 * Get the resource dependencies of a file.
38659 *
38660 * If the file is not part of the compilation, an empty array will be returned.
38661 */
38662 getResourceDependencies(file) {
38663 this.ensureAnalyzed();
38664 return this.incrementalDriver.depGraph.getResourceDependencies(file);
38665 }
38666 /**
38667 * Get all Angular-related diagnostics for this compilation.
38668 */
38669 getDiagnostics() {
38670 return this.addMessageTextDetails([...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics()]);
38671 }
38672 /**
38673 * Get all Angular-related diagnostics for this compilation.
38674 *
38675 * If a `ts.SourceFile` is passed, only diagnostics related to that file are returned.
38676 */
38677 getDiagnosticsForFile(file, optimizeFor) {
38678 return this.addMessageTextDetails([
38679 ...this.getNonTemplateDiagnostics().filter(diag => diag.file === file),
38680 ...this.getTemplateDiagnosticsForFile(file, optimizeFor)
38681 ]);
38682 }
38683 /**
38684 * Add Angular.io error guide links to diagnostics for this compilation.
38685 */
38686 addMessageTextDetails(diagnostics) {
38687 return diagnostics.map(diag => {
38688 if (diag.code && COMPILER_ERRORS_WITH_GUIDES.has(ngErrorCode(diag.code))) {
38689 return Object.assign(Object.assign({}, diag), { messageText: diag.messageText +
38690 `. Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/NG${ngErrorCode(diag.code)}` });
38691 }
38692 return diag;
38693 });
38694 }
38695 /**
38696 * Get all setup-related diagnostics for this compilation.
38697 */
38698 getOptionDiagnostics() {
38699 return this.constructionDiagnostics;
38700 }
38701 /**
38702 * Get the `ts.Program` to use as a starting point when spawning a subsequent incremental
38703 * compilation.
38704 *
38705 * The `NgCompiler` spawns an internal incremental TypeScript compilation (inheriting the
38706 * consumer's `ts.Program` into a new one for the purposes of template type-checking). After this
38707 * operation, the consumer's `ts.Program` is no longer usable for starting a new incremental
38708 * compilation. `getNextProgram` retrieves the `ts.Program` which can be used instead.
38709 */
38710 getNextProgram() {
38711 return this.nextProgram;
38712 }
38713 getTemplateTypeChecker() {
38714 if (!this.enableTemplateTypeChecker) {
38715 throw new Error('The `TemplateTypeChecker` does not work without `enableTemplateTypeChecker`.');
38716 }
38717 return this.ensureAnalyzed().templateTypeChecker;
38718 }
38719 /**
38720 * Retrieves the `ts.Declaration`s for any component(s) which use the given template file.
38721 */
38722 getComponentsWithTemplateFile(templateFilePath) {
38723 const { resourceRegistry } = this.ensureAnalyzed();
38724 return resourceRegistry.getComponentsWithTemplate(resolve(templateFilePath));
38725 }
38726 /**
38727 * Retrieves the `ts.Declaration`s for any component(s) which use the given template file.
38728 */
38729 getComponentsWithStyleFile(styleFilePath) {
38730 const { resourceRegistry } = this.ensureAnalyzed();
38731 return resourceRegistry.getComponentsWithStyle(resolve(styleFilePath));
38732 }
38733 /**
38734 * Retrieves external resources for the given component.
38735 */
38736 getComponentResources(classDecl) {
38737 if (!isNamedClassDeclaration(classDecl)) {
38738 return null;
38739 }
38740 const { resourceRegistry } = this.ensureAnalyzed();
38741 const styles = resourceRegistry.getStyles(classDecl);
38742 const template = resourceRegistry.getTemplate(classDecl);
38743 if (template === null) {
38744 return null;
38745 }
38746 return { styles, template };
38747 }
38748 /**
38749 * Perform Angular's analysis step (as a precursor to `getDiagnostics` or `prepareEmit`)
38750 * asynchronously.
38751 *
38752 * Normally, this operation happens lazily whenever `getDiagnostics` or `prepareEmit` are called.
38753 * However, certain consumers may wish to allow for an asynchronous phase of analysis, where
38754 * resources such as `styleUrls` are resolved asynchonously. In these cases `analyzeAsync` must be
38755 * called first, and its `Promise` awaited prior to calling any other APIs of `NgCompiler`.
38756 */
38757 analyzeAsync() {
38758 return __awaiter(this, void 0, void 0, function* () {
38759 if (this.compilation !== null) {
38760 return;
38761 }
38762 this.compilation = this.makeCompilation();
38763 const analyzeSpan = this.perfRecorder.start('analyze');
38764 const promises = [];
38765 for (const sf of this.tsProgram.getSourceFiles()) {
38766 if (sf.isDeclarationFile) {
38767 continue;
38768 }
38769 const analyzeFileSpan = this.perfRecorder.start('analyzeFile', sf);
38770 let analysisPromise = this.compilation.traitCompiler.analyzeAsync(sf);
38771 this.scanForMwp(sf);
38772 if (analysisPromise === undefined) {
38773 this.perfRecorder.stop(analyzeFileSpan);
38774 }
38775 else if (this.perfRecorder.enabled) {
38776 analysisPromise = analysisPromise.then(() => this.perfRecorder.stop(analyzeFileSpan));
38777 }
38778 if (analysisPromise !== undefined) {
38779 promises.push(analysisPromise);
38780 }
38781 }
38782 yield Promise.all(promises);
38783 this.perfRecorder.stop(analyzeSpan);
38784 this.resolveCompilation(this.compilation.traitCompiler);
38785 });
38786 }
38787 /**
38788 * List lazy routes detected during analysis.
38789 *
38790 * This can be called for one specific route, or to retrieve all top-level routes.
38791 */
38792 listLazyRoutes(entryRoute) {
38793 if (entryRoute) {
38794 // Note:
38795 // This resolution step is here to match the implementation of the old `AotCompilerHost` (see
38796 // https://github.com/angular/angular/blob/50732e156/packages/compiler-cli/src/transformers/compiler_host.ts#L175-L188).
38797 //
38798 // `@angular/cli` will always call this API with an absolute path, so the resolution step is
38799 // not necessary, but keeping it backwards compatible in case someone else is using the API.
38800 // Relative entry paths are disallowed.
38801 if (entryRoute.startsWith('.')) {
38802 throw new Error(`Failed to list lazy routes: Resolution of relative paths (${entryRoute}) is not supported.`);
38803 }
38804 // Non-relative entry paths fall into one of the following categories:
38805 // - Absolute system paths (e.g. `/foo/bar/my-project/my-module`), which are unaffected by the
38806 // logic below.
38807 // - Paths to enternal modules (e.g. `some-lib`).
38808 // - Paths mapped to directories in `tsconfig.json` (e.g. `shared/my-module`).
38809 // (See https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping.)
38810 //
38811 // In all cases above, the `containingFile` argument is ignored, so we can just take the first
38812 // of the root files.
38813 const containingFile = this.tsProgram.getRootFileNames()[0];
38814 const [entryPath, moduleName] = entryRoute.split('#');
38815 const resolvedModule = resolveModuleName(entryPath, containingFile, this.options, this.adapter, null);
38816 if (resolvedModule) {
38817 entryRoute = entryPointKeyFor(resolvedModule.resolvedFileName, moduleName);
38818 }
38819 }
38820 const compilation = this.ensureAnalyzed();
38821 return compilation.routeAnalyzer.listLazyRoutes(entryRoute);
38822 }
38823 /**
38824 * Fetch transformers and other information which is necessary for a consumer to `emit` the
38825 * program with Angular-added definitions.
38826 */
38827 prepareEmit() {
38828 const compilation = this.ensureAnalyzed();
38829 const coreImportsFrom = compilation.isCore ? getR3SymbolsFile(this.tsProgram) : null;
38830 let importRewriter;
38831 if (coreImportsFrom !== null) {
38832 importRewriter = new R3SymbolsImportRewriter(coreImportsFrom.fileName);
38833 }
38834 else {
38835 importRewriter = new NoopImportRewriter();
38836 }
38837 const before = [
38838 ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, compilation.defaultImportTracker, compilation.isCore, this.closureCompilerEnabled),
38839 aliasTransformFactory(compilation.traitCompiler.exportStatements),
38840 compilation.defaultImportTracker.importPreservingTransformer(),
38841 ];
38842 const afterDeclarations = [];
38843 if (compilation.dtsTransforms !== null) {
38844 afterDeclarations.push(declarationTransformFactory(compilation.dtsTransforms, importRewriter));
38845 }
38846 // Only add aliasing re-exports to the .d.ts output if the `AliasingHost` requests it.
38847 if (compilation.aliasingHost !== null && compilation.aliasingHost.aliasExportsInDts) {
38848 afterDeclarations.push(aliasTransformFactory(compilation.traitCompiler.exportStatements));
38849 }
38850 if (this.adapter.factoryTracker !== null) {
38851 before.push(generatedFactoryTransform(this.adapter.factoryTracker.sourceInfo, importRewriter));
38852 }
38853 before.push(ivySwitchTransform);
38854 return { transformers: { before, afterDeclarations } };
38855 }
38856 /**
38857 * Run the indexing process and return a `Map` of all indexed components.
38858 *
38859 * See the `indexing` package for more details.
38860 */
38861 getIndexedComponents() {
38862 const compilation = this.ensureAnalyzed();
38863 const context = new IndexingContext();
38864 compilation.traitCompiler.index(context);
38865 return generateAnalysis(context);
38866 }
38867 ensureAnalyzed() {
38868 if (this.compilation === null) {
38869 this.analyzeSync();
38870 }
38871 return this.compilation;
38872 }
38873 analyzeSync() {
38874 const analyzeSpan = this.perfRecorder.start('analyze');
38875 this.compilation = this.makeCompilation();
38876 for (const sf of this.tsProgram.getSourceFiles()) {
38877 if (sf.isDeclarationFile) {
38878 continue;
38879 }
38880 const analyzeFileSpan = this.perfRecorder.start('analyzeFile', sf);
38881 this.compilation.traitCompiler.analyzeSync(sf);
38882 this.scanForMwp(sf);
38883 this.perfRecorder.stop(analyzeFileSpan);
38884 }
38885 this.perfRecorder.stop(analyzeSpan);
38886 this.resolveCompilation(this.compilation.traitCompiler);
38887 }
38888 resolveCompilation(traitCompiler) {
38889 traitCompiler.resolve();
38890 this.recordNgModuleScopeDependencies();
38891 // At this point, analysis is complete and the compiler can now calculate which files need to
38892 // be emitted, so do that.
38893 this.incrementalDriver.recordSuccessfulAnalysis(traitCompiler);
38894 }
38895 get fullTemplateTypeCheck() {
38896 // Determine the strictness level of type checking based on compiler options. As
38897 // `strictTemplates` is a superset of `fullTemplateTypeCheck`, the former implies the latter.
38898 // Also see `verifyCompatibleTypeCheckOptions` where it is verified that `fullTemplateTypeCheck`
38899 // is not disabled when `strictTemplates` is enabled.
38900 const strictTemplates = !!this.options.strictTemplates;
38901 return strictTemplates || !!this.options.fullTemplateTypeCheck;
38902 }
38903 getTypeCheckingConfig() {
38904 // Determine the strictness level of type checking based on compiler options. As
38905 // `strictTemplates` is a superset of `fullTemplateTypeCheck`, the former implies the latter.
38906 // Also see `verifyCompatibleTypeCheckOptions` where it is verified that `fullTemplateTypeCheck`
38907 // is not disabled when `strictTemplates` is enabled.
38908 const strictTemplates = !!this.options.strictTemplates;
38909 // First select a type-checking configuration, based on whether full template type-checking is
38910 // requested.
38911 let typeCheckingConfig;
38912 if (this.fullTemplateTypeCheck) {
38913 typeCheckingConfig = {
38914 applyTemplateContextGuards: strictTemplates,
38915 checkQueries: false,
38916 checkTemplateBodies: true,
38917 alwaysCheckSchemaInTemplateBodies: true,
38918 checkTypeOfInputBindings: strictTemplates,
38919 honorAccessModifiersForInputBindings: false,
38920 strictNullInputBindings: strictTemplates,
38921 checkTypeOfAttributes: strictTemplates,
38922 // Even in full template type-checking mode, DOM binding checks are not quite ready yet.
38923 checkTypeOfDomBindings: false,
38924 checkTypeOfOutputEvents: strictTemplates,
38925 checkTypeOfAnimationEvents: strictTemplates,
38926 // Checking of DOM events currently has an adverse effect on developer experience,
38927 // e.g. for `<input (blur)="update($event.target.value)">` enabling this check results in:
38928 // - error TS2531: Object is possibly 'null'.
38929 // - error TS2339: Property 'value' does not exist on type 'EventTarget'.
38930 checkTypeOfDomEvents: strictTemplates,
38931 checkTypeOfDomReferences: strictTemplates,
38932 // Non-DOM references have the correct type in View Engine so there is no strictness flag.
38933 checkTypeOfNonDomReferences: true,
38934 // Pipes are checked in View Engine so there is no strictness flag.
38935 checkTypeOfPipes: true,
38936 strictSafeNavigationTypes: strictTemplates,
38937 useContextGenericType: strictTemplates,
38938 strictLiteralTypes: true,
38939 enableTemplateTypeChecker: this.enableTemplateTypeChecker,
38940 };
38941 }
38942 else {
38943 typeCheckingConfig = {
38944 applyTemplateContextGuards: false,
38945 checkQueries: false,
38946 checkTemplateBodies: false,
38947 // Enable deep schema checking in "basic" template type-checking mode only if Closure
38948 // compilation is requested, which is a good proxy for "only in google3".
38949 alwaysCheckSchemaInTemplateBodies: this.closureCompilerEnabled,
38950 checkTypeOfInputBindings: false,
38951 strictNullInputBindings: false,
38952 honorAccessModifiersForInputBindings: false,
38953 checkTypeOfAttributes: false,
38954 checkTypeOfDomBindings: false,
38955 checkTypeOfOutputEvents: false,
38956 checkTypeOfAnimationEvents: false,
38957 checkTypeOfDomEvents: false,
38958 checkTypeOfDomReferences: false,
38959 checkTypeOfNonDomReferences: false,
38960 checkTypeOfPipes: false,
38961 strictSafeNavigationTypes: false,
38962 useContextGenericType: false,
38963 strictLiteralTypes: false,
38964 enableTemplateTypeChecker: this.enableTemplateTypeChecker,
38965 };
38966 }
38967 // Apply explicitly configured strictness flags on top of the default configuration
38968 // based on "fullTemplateTypeCheck".
38969 if (this.options.strictInputTypes !== undefined) {
38970 typeCheckingConfig.checkTypeOfInputBindings = this.options.strictInputTypes;
38971 typeCheckingConfig.applyTemplateContextGuards = this.options.strictInputTypes;
38972 }
38973 if (this.options.strictInputAccessModifiers !== undefined) {
38974 typeCheckingConfig.honorAccessModifiersForInputBindings =
38975 this.options.strictInputAccessModifiers;
38976 }
38977 if (this.options.strictNullInputTypes !== undefined) {
38978 typeCheckingConfig.strictNullInputBindings = this.options.strictNullInputTypes;
38979 }
38980 if (this.options.strictOutputEventTypes !== undefined) {
38981 typeCheckingConfig.checkTypeOfOutputEvents = this.options.strictOutputEventTypes;
38982 typeCheckingConfig.checkTypeOfAnimationEvents = this.options.strictOutputEventTypes;
38983 }
38984 if (this.options.strictDomEventTypes !== undefined) {
38985 typeCheckingConfig.checkTypeOfDomEvents = this.options.strictDomEventTypes;
38986 }
38987 if (this.options.strictSafeNavigationTypes !== undefined) {
38988 typeCheckingConfig.strictSafeNavigationTypes = this.options.strictSafeNavigationTypes;
38989 }
38990 if (this.options.strictDomLocalRefTypes !== undefined) {
38991 typeCheckingConfig.checkTypeOfDomReferences = this.options.strictDomLocalRefTypes;
38992 }
38993 if (this.options.strictAttributeTypes !== undefined) {
38994 typeCheckingConfig.checkTypeOfAttributes = this.options.strictAttributeTypes;
38995 }
38996 if (this.options.strictContextGenerics !== undefined) {
38997 typeCheckingConfig.useContextGenericType = this.options.strictContextGenerics;
38998 }
38999 if (this.options.strictLiteralTypes !== undefined) {
39000 typeCheckingConfig.strictLiteralTypes = this.options.strictLiteralTypes;
39001 }
39002 return typeCheckingConfig;
39003 }
39004 getTemplateDiagnostics() {
39005 const compilation = this.ensureAnalyzed();
39006 // Get the diagnostics.
39007 const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics');
39008 const diagnostics = [];
39009 for (const sf of this.tsProgram.getSourceFiles()) {
39010 if (sf.isDeclarationFile || this.adapter.isShim(sf)) {
39011 continue;
39012 }
39013 diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
39014 }
39015 const program = this.typeCheckingProgramStrategy.getProgram();
39016 this.perfRecorder.stop(typeCheckSpan);
39017 this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, program);
39018 this.nextProgram = program;
39019 return diagnostics;
39020 }
39021 getTemplateDiagnosticsForFile(sf, optimizeFor) {
39022 const compilation = this.ensureAnalyzed();
39023 // Get the diagnostics.
39024 const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics');
39025 const diagnostics = [];
39026 if (!sf.isDeclarationFile && !this.adapter.isShim(sf)) {
39027 diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, optimizeFor));
39028 }
39029 const program = this.typeCheckingProgramStrategy.getProgram();
39030 this.perfRecorder.stop(typeCheckSpan);
39031 this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, program);
39032 this.nextProgram = program;
39033 return diagnostics;
39034 }
39035 getNonTemplateDiagnostics() {
39036 if (this.nonTemplateDiagnostics === null) {
39037 const compilation = this.ensureAnalyzed();
39038 this.nonTemplateDiagnostics = [...compilation.traitCompiler.diagnostics];
39039 if (this.entryPoint !== null && compilation.exportReferenceGraph !== null) {
39040 this.nonTemplateDiagnostics.push(...checkForPrivateExports(this.entryPoint, this.tsProgram.getTypeChecker(), compilation.exportReferenceGraph));
39041 }
39042 }
39043 return this.nonTemplateDiagnostics;
39044 }
39045 /**
39046 * Reifies the inter-dependencies of NgModules and the components within their compilation scopes
39047 * into the `IncrementalDriver`'s dependency graph.
39048 */
39049 recordNgModuleScopeDependencies() {
39050 const recordSpan = this.perfRecorder.start('recordDependencies');
39051 const depGraph = this.incrementalDriver.depGraph;
39052 for (const scope of this.compilation.scopeRegistry.getCompilationScopes()) {
39053 const file = scope.declaration.getSourceFile();
39054 const ngModuleFile = scope.ngModule.getSourceFile();
39055 // A change to any dependency of the declaration causes the declaration to be invalidated,
39056 // which requires the NgModule to be invalidated as well.
39057 depGraph.addTransitiveDependency(ngModuleFile, file);
39058 // A change to the NgModule file should cause the declaration itself to be invalidated.
39059 depGraph.addDependency(file, ngModuleFile);
39060 const meta = this.compilation.metaReader.getDirectiveMetadata(new Reference$1(scope.declaration));
39061 if (meta !== null && meta.isComponent) {
39062 // If a component's template changes, it might have affected the import graph, and thus the
39063 // remote scoping feature which is activated in the event of potential import cycles. Thus,
39064 // the module depends not only on the transitive dependencies of the component, but on its
39065 // resources as well.
39066 depGraph.addTransitiveResources(ngModuleFile, file);
39067 // A change to any directive/pipe in the compilation scope should cause the component to be
39068 // invalidated.
39069 for (const directive of scope.directives) {
39070 // When a directive in scope is updated, the component needs to be recompiled as e.g. a
39071 // selector may have changed.
39072 depGraph.addTransitiveDependency(file, directive.ref.node.getSourceFile());
39073 }
39074 for (const pipe of scope.pipes) {
39075 // When a pipe in scope is updated, the component needs to be recompiled as e.g. the
39076 // pipe's name may have changed.
39077 depGraph.addTransitiveDependency(file, pipe.ref.node.getSourceFile());
39078 }
39079 // Components depend on the entire export scope. In addition to transitive dependencies on
39080 // all directives/pipes in the export scope, they also depend on every NgModule in the
39081 // scope, as changes to a module may add new directives/pipes to the scope.
39082 for (const depModule of scope.ngModules) {
39083 // There is a correctness issue here. To be correct, this should be a transitive
39084 // dependency on the depModule file, since the depModule's exports might change via one of
39085 // its dependencies, even if depModule's file itself doesn't change. However, doing this
39086 // would also trigger recompilation if a non-exported component or directive changed,
39087 // which causes performance issues for rebuilds.
39088 //
39089 // Given the rebuild issue is an edge case, currently we err on the side of performance
39090 // instead of correctness. A correct and performant design would distinguish between
39091 // changes to the depModule which affect its export scope and changes which do not, and
39092 // only add a dependency for the former. This concept is currently in development.
39093 //
39094 // TODO(alxhub): fix correctness issue by understanding the semantics of the dependency.
39095 depGraph.addDependency(file, depModule.getSourceFile());
39096 }
39097 }
39098 else {
39099 // Directives (not components) and pipes only depend on the NgModule which directly declares
39100 // them.
39101 depGraph.addDependency(file, ngModuleFile);
39102 }
39103 }
39104 this.perfRecorder.stop(recordSpan);
39105 }
39106 scanForMwp(sf) {
39107 this.compilation.mwpScanner.scan(sf, {
39108 addTypeReplacement: (node, type) => {
39109 // Only obtain the return type transform for the source file once there's a type to replace,
39110 // so that no transform is allocated when there's nothing to do.
39111 this.compilation.dtsTransforms.getReturnTypeTransform(sf).addTypeReplacement(node, type);
39112 }
39113 });
39114 }
39115 makeCompilation() {
39116 const checker = this.tsProgram.getTypeChecker();
39117 const reflector = new TypeScriptReflectionHost(checker);
39118 // Construct the ReferenceEmitter.
39119 let refEmitter;
39120 let aliasingHost = null;
39121 if (this.adapter.unifiedModulesHost === null || !this.options._useHostForImportGeneration) {
39122 let localImportStrategy;
39123 // The strategy used for local, in-project imports depends on whether TS has been configured
39124 // with rootDirs. If so, then multiple directories may be mapped in the same "module
39125 // namespace" and the logic of `LogicalProjectStrategy` is required to generate correct
39126 // imports which may cross these multiple directories. Otherwise, plain relative imports are
39127 // sufficient.
39128 if (this.options.rootDir !== undefined ||
39129 (this.options.rootDirs !== undefined && this.options.rootDirs.length > 0)) {
39130 // rootDirs logic is in effect - use the `LogicalProjectStrategy` for in-project relative
39131 // imports.
39132 localImportStrategy = new LogicalProjectStrategy(reflector, new LogicalFileSystem([...this.adapter.rootDirs], this.adapter));
39133 }
39134 else {
39135 // Plain relative imports are all that's needed.
39136 localImportStrategy = new RelativePathStrategy(reflector);
39137 }
39138 // The CompilerHost doesn't have fileNameToModuleName, so build an NPM-centric reference
39139 // resolution strategy.
39140 refEmitter = new ReferenceEmitter([
39141 // First, try to use local identifiers if available.
39142 new LocalIdentifierStrategy(),
39143 // Next, attempt to use an absolute import.
39144 new AbsoluteModuleStrategy(this.tsProgram, checker, this.moduleResolver, reflector),
39145 // Finally, check if the reference is being written into a file within the project's .ts
39146 // sources, and use a relative import if so. If this fails, ReferenceEmitter will throw
39147 // an error.
39148 localImportStrategy,
39149 ]);
39150 // If an entrypoint is present, then all user imports should be directed through the
39151 // entrypoint and private exports are not needed. The compiler will validate that all publicly
39152 // visible directives/pipes are importable via this entrypoint.
39153 if (this.entryPoint === null && this.options.generateDeepReexports === true) {
39154 // No entrypoint is present and deep re-exports were requested, so configure the aliasing
39155 // system to generate them.
39156 aliasingHost = new PrivateExportAliasingHost(reflector);
39157 }
39158 }
39159 else {
39160 // The CompilerHost supports fileNameToModuleName, so use that to emit imports.
39161 refEmitter = new ReferenceEmitter([
39162 // First, try to use local identifiers if available.
39163 new LocalIdentifierStrategy(),
39164 // Then use aliased references (this is a workaround to StrictDeps checks).
39165 new AliasStrategy(),
39166 // Then use fileNameToModuleName to emit imports.
39167 new UnifiedModulesStrategy(reflector, this.adapter.unifiedModulesHost),
39168 ]);
39169 aliasingHost = new UnifiedModulesAliasingHost(this.adapter.unifiedModulesHost);
39170 }
39171 const evaluator = new PartialEvaluator(reflector, checker, this.incrementalDriver.depGraph);
39172 const dtsReader = new DtsMetadataReader(checker, reflector);
39173 const localMetaRegistry = new LocalMetadataRegistry();
39174 const localMetaReader = localMetaRegistry;
39175 const depScopeReader = new MetadataDtsModuleScopeResolver(dtsReader, aliasingHost);
39176 const scopeRegistry = new LocalModuleScopeRegistry(localMetaReader, depScopeReader, refEmitter, aliasingHost);
39177 const scopeReader = scopeRegistry;
39178 const metaRegistry = new CompoundMetadataRegistry([localMetaRegistry, scopeRegistry]);
39179 const injectableRegistry = new InjectableClassRegistry(reflector);
39180 const metaReader = new CompoundMetadataReader([localMetaReader, dtsReader]);
39181 const typeCheckScopeRegistry = new TypeCheckScopeRegistry(scopeReader, metaReader);
39182 // If a flat module entrypoint was specified, then track references via a `ReferenceGraph` in
39183 // order to produce proper diagnostics for incorrectly exported directives/pipes/etc. If there
39184 // is no flat module entrypoint then don't pay the cost of tracking references.
39185 let referencesRegistry;
39186 let exportReferenceGraph = null;
39187 if (this.entryPoint !== null) {
39188 exportReferenceGraph = new ReferenceGraph();
39189 referencesRegistry = new ReferenceGraphAdapter(exportReferenceGraph);
39190 }
39191 else {
39192 referencesRegistry = new NoopReferencesRegistry();
39193 }
39194 const routeAnalyzer = new NgModuleRouteAnalyzer(this.moduleResolver, evaluator);
39195 const dtsTransforms = new DtsTransformRegistry();
39196 const mwpScanner = new ModuleWithProvidersScanner(reflector, evaluator, refEmitter);
39197 const isCore = isAngularCorePackage(this.tsProgram);
39198 const defaultImportTracker = new DefaultImportTracker();
39199 const resourceRegistry = new ResourceRegistry();
39200 const compilationMode = this.options.compilationMode === 'partial' ? CompilationMode.PARTIAL : CompilationMode.FULL;
39201 // Cycles are handled in full compilation mode by "remote scoping".
39202 // "Remote scoping" does not work well with tree shaking for libraries.
39203 // So in partial compilation mode, when building a library, a cycle will cause an error.
39204 const cycleHandlingStrategy = compilationMode === CompilationMode.FULL ?
39205 0 /* UseRemoteScoping */ :
39206 1 /* Error */;
39207 // Set up the IvyCompilation, which manages state for the Ivy transformer.
39208 const handlers = [
39209 new ComponentDecoratorHandler(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, this.resourceManager, this.adapter.rootDirs, this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, cycleHandlingStrategy, refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, injectableRegistry, this.closureCompilerEnabled),
39210 // TODO(alxhub): understand why the cast here is necessary (something to do with `null`
39211 // not being assignable to `unknown` when wrapped in `Readonly`).
39212 // clang-format off
39213 new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportTracker, injectableRegistry, isCore, this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures),
39214 // clang-format on
39215 // Pipe handler must be before injectable handler in list so pipe factories are printed
39216 // before injectable factories (so injectable factories can delegate to them)
39217 new PipeDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportTracker, injectableRegistry, isCore),
39218 new InjectableDecoratorHandler(reflector, defaultImportTracker, isCore, this.options.strictInjectionParameters || false, injectableRegistry),
39219 new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, this.adapter.factoryTracker, defaultImportTracker, this.closureCompilerEnabled, injectableRegistry, this.options.i18nInLocale),
39220 ];
39221 const traitCompiler = new TraitCompiler(handlers, reflector, this.perfRecorder, this.incrementalDriver, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms);
39222 const templateTypeChecker = new TemplateTypeCheckerImpl(this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver, scopeRegistry, typeCheckScopeRegistry);
39223 return {
39224 isCore,
39225 traitCompiler,
39226 reflector,
39227 scopeRegistry,
39228 dtsTransforms,
39229 exportReferenceGraph,
39230 routeAnalyzer,
39231 mwpScanner,
39232 metaReader,
39233 typeCheckScopeRegistry,
39234 defaultImportTracker,
39235 aliasingHost,
39236 refEmitter,
39237 templateTypeChecker,
39238 resourceRegistry,
39239 };
39240 }
39241 }
39242 /**
39243 * Determine if the given `Program` is @angular/core.
39244 */
39245 function isAngularCorePackage(program) {
39246 // Look for its_just_angular.ts somewhere in the program.
39247 const r3Symbols = getR3SymbolsFile(program);
39248 if (r3Symbols === null) {
39249 return false;
39250 }
39251 // Look for the constant ITS_JUST_ANGULAR in that file.
39252 return r3Symbols.statements.some(stmt => {
39253 // The statement must be a variable declaration statement.
39254 if (!ts$1.isVariableStatement(stmt)) {
39255 return false;
39256 }
39257 // It must be exported.
39258 if (stmt.modifiers === undefined ||
39259 !stmt.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.ExportKeyword)) {
39260 return false;
39261 }
39262 // It must declare ITS_JUST_ANGULAR.
39263 return stmt.declarationList.declarations.some(decl => {
39264 // The declaration must match the name.
39265 if (!ts$1.isIdentifier(decl.name) || decl.name.text !== 'ITS_JUST_ANGULAR') {
39266 return false;
39267 }
39268 // It must initialize the variable to true.
39269 if (decl.initializer === undefined || decl.initializer.kind !== ts$1.SyntaxKind.TrueKeyword) {
39270 return false;
39271 }
39272 // This definition matches.
39273 return true;
39274 });
39275 });
39276 }
39277 /**
39278 * Find the 'r3_symbols.ts' file in the given `Program`, or return `null` if it wasn't there.
39279 */
39280 function getR3SymbolsFile(program) {
39281 return program.getSourceFiles().find(file => file.fileName.indexOf('r3_symbols.ts') >= 0) || null;
39282 }
39283 /**
39284 * Since "strictTemplates" is a true superset of type checking capabilities compared to
39285 * "fullTemplateTypeCheck", it is required that the latter is not explicitly disabled if the
39286 * former is enabled.
39287 */
39288 function verifyCompatibleTypeCheckOptions(options) {
39289 if (options.fullTemplateTypeCheck === false && options.strictTemplates === true) {
39290 return {
39291 category: ts$1.DiagnosticCategory.Error,
39292 code: ngErrorCode(ErrorCode.CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK),
39293 file: undefined,
39294 start: undefined,
39295 length: undefined,
39296 messageText: `Angular compiler option "strictTemplates" is enabled, however "fullTemplateTypeCheck" is disabled.
39297
39298Having the "strictTemplates" flag enabled implies that "fullTemplateTypeCheck" is also enabled, so
39299the latter can not be explicitly disabled.
39300
39301One of the following actions is required:
393021. Remove the "fullTemplateTypeCheck" option.
393032. Remove "strictTemplates" or set it to 'false'.
39304
39305More information about the template type checking compiler options can be found in the documentation:
39306https://v9.angular.io/guide/template-typecheck#template-type-checking`,
39307 };
39308 }
39309 return null;
39310 }
39311 class ReferenceGraphAdapter {
39312 constructor(graph) {
39313 this.graph = graph;
39314 }
39315 add(source, ...references) {
39316 for (const { node } of references) {
39317 let sourceFile = node.getSourceFile();
39318 if (sourceFile === undefined) {
39319 sourceFile = ts$1.getOriginalNode(node).getSourceFile();
39320 }
39321 // Only record local references (not references into .d.ts files).
39322 if (sourceFile === undefined || !isDtsPath(sourceFile.fileName)) {
39323 this.graph.add(source, node);
39324 }
39325 }
39326 }
39327 }
39328
39329 /**
39330 * @license
39331 * Copyright Google LLC All Rights Reserved.
39332 *
39333 * Use of this source code is governed by an MIT-style license that can be
39334 * found in the LICENSE file at https://angular.io/license
39335 */
39336 function calcProjectFileAndBasePath(project, host = getFileSystem()) {
39337 const absProject = host.resolve(project);
39338 const projectIsDir = host.lstat(absProject).isDirectory();
39339 const projectFile = projectIsDir ? host.join(absProject, 'tsconfig.json') : absProject;
39340 const projectDir = projectIsDir ? absProject : host.dirname(absProject);
39341 const basePath = host.resolve(projectDir);
39342 return { projectFile, basePath };
39343 }
39344 function readConfiguration(project, existingOptions, host = getFileSystem()) {
39345 var _a;
39346 try {
39347 const fs = getFileSystem();
39348 const readConfigFile = (configFile) => ts$1.readConfigFile(configFile, file => host.readFile(host.resolve(file)));
39349 const readAngularCompilerOptions = (configFile, parentOptions = {}) => {
39350 const { config, error } = readConfigFile(configFile);
39351 if (error) {
39352 // Errors are handled later on by 'parseJsonConfigFileContent'
39353 return parentOptions;
39354 }
39355 // we are only interested into merging 'angularCompilerOptions' as
39356 // other options like 'compilerOptions' are merged by TS
39357 const existingNgCompilerOptions = Object.assign(Object.assign({}, config.angularCompilerOptions), parentOptions);
39358 if (config.extends && typeof config.extends === 'string') {
39359 const extendedConfigPath = getExtendedConfigPath(configFile, config.extends, host, fs);
39360 if (extendedConfigPath !== null) {
39361 // Call readAngularCompilerOptions recursively to merge NG Compiler options
39362 return readAngularCompilerOptions(extendedConfigPath, existingNgCompilerOptions);
39363 }
39364 }
39365 return existingNgCompilerOptions;
39366 };
39367 const { projectFile, basePath } = calcProjectFileAndBasePath(project, host);
39368 const configFileName = host.resolve(host.pwd(), projectFile);
39369 const { config, error } = readConfigFile(projectFile);
39370 if (error) {
39371 return {
39372 project,
39373 errors: [error],
39374 rootNames: [],
39375 options: {},
39376 emitFlags: EmitFlags.Default
39377 };
39378 }
39379 const existingCompilerOptions = Object.assign(Object.assign({ genDir: basePath, basePath }, readAngularCompilerOptions(configFileName)), existingOptions);
39380 const parseConfigHost = createParseConfigHost(host, fs);
39381 const { options, errors, fileNames: rootNames, projectReferences } = ts$1.parseJsonConfigFileContent(config, parseConfigHost, basePath, existingCompilerOptions, configFileName);
39382 // Coerce to boolean as `enableIvy` can be `ngtsc|true|false|undefined` here.
39383 options.enableIvy = !!((_a = options.enableIvy) !== null && _a !== void 0 ? _a : true);
39384 let emitFlags = EmitFlags.Default;
39385 if (!(options.skipMetadataEmit || options.flatModuleOutFile)) {
39386 emitFlags |= EmitFlags.Metadata;
39387 }
39388 if (options.skipTemplateCodegen) {
39389 emitFlags = emitFlags & ~EmitFlags.Codegen;
39390 }
39391 return { project: projectFile, rootNames, projectReferences, options, errors, emitFlags };
39392 }
39393 catch (e) {
39394 const errors = [{
39395 category: ts$1.DiagnosticCategory.Error,
39396 messageText: e.stack,
39397 file: undefined,
39398 start: undefined,
39399 length: undefined,
39400 source: 'angular',
39401 code: UNKNOWN_ERROR_CODE,
39402 }];
39403 return { project: '', errors, rootNames: [], options: {}, emitFlags: EmitFlags.Default };
39404 }
39405 }
39406 function createParseConfigHost(host, fs = getFileSystem()) {
39407 return {
39408 fileExists: host.exists.bind(host),
39409 readDirectory: ts$1.sys.readDirectory,
39410 readFile: host.readFile.bind(host),
39411 useCaseSensitiveFileNames: fs.isCaseSensitive(),
39412 };
39413 }
39414 function getExtendedConfigPath(configFile, extendsValue, host, fs) {
39415 let extendedConfigPath = null;
39416 if (extendsValue.startsWith('.') || fs.isRooted(extendsValue)) {
39417 extendedConfigPath = host.resolve(host.dirname(configFile), extendsValue);
39418 extendedConfigPath = host.extname(extendedConfigPath) ?
39419 extendedConfigPath :
39420 absoluteFrom(`${extendedConfigPath}.json`);
39421 }
39422 else {
39423 const parseConfigHost = createParseConfigHost(host, fs);
39424 // Path isn't a rooted or relative path, resolve like a module.
39425 const { resolvedModule, } = ts$1.nodeModuleNameResolver(extendsValue, configFile, { moduleResolution: ts$1.ModuleResolutionKind.NodeJs, resolveJsonModule: true }, parseConfigHost);
39426 if (resolvedModule) {
39427 extendedConfigPath = absoluteFrom(resolvedModule.resolvedFileName);
39428 }
39429 }
39430 if (extendedConfigPath !== null && host.exists(extendedConfigPath)) {
39431 return extendedConfigPath;
39432 }
39433 return null;
39434 }
39435
39436 /**
39437 * @license
39438 * Copyright Google LLC All Rights Reserved.
39439 *
39440 * Use of this source code is governed by an MIT-style license that can be
39441 * found in the LICENSE file at https://angular.io/license
39442 */
39443 /**
39444 * Known values for global variables in `@angular/core` that Terser should set using
39445 * https://github.com/terser-js/terser#conditional-compilation
39446 */
39447 const GLOBAL_DEFS_FOR_TERSER = {
39448 ngDevMode: false,
39449 ngI18nClosureMode: false,
39450 };
39451 const GLOBAL_DEFS_FOR_TERSER_WITH_AOT = Object.assign(Object.assign({}, GLOBAL_DEFS_FOR_TERSER), { ngJitMode: false });
39452
39453 /**
39454 * @license
39455 * Copyright Google LLC All Rights Reserved.
39456 *
39457 * Use of this source code is governed by an MIT-style license that can be
39458 * found in the LICENSE file at https://angular.io/license
39459 */
39460 setFileSystem(new NodeJSFileSystem());
39461
39462 /**
39463 * @license
39464 * Copyright Google LLC All Rights Reserved.
39465 *
39466 * Use of this source code is governed by an MIT-style license that can be
39467 * found in the LICENSE file at https://angular.io/license
39468 */
39469 // Reverse mappings of enum would generate strings
39470 const ALIAS_NAME = ts$1.SymbolDisplayPartKind[ts$1.SymbolDisplayPartKind.aliasName];
39471 const SYMBOL_INTERFACE = ts$1.SymbolDisplayPartKind[ts$1.SymbolDisplayPartKind.interfaceName];
39472 const SYMBOL_PUNC = ts$1.SymbolDisplayPartKind[ts$1.SymbolDisplayPartKind.punctuation];
39473 const SYMBOL_SPACE = ts$1.SymbolDisplayPartKind[ts$1.SymbolDisplayPartKind.space];
39474 const SYMBOL_TEXT = ts$1.SymbolDisplayPartKind[ts$1.SymbolDisplayPartKind.text];
39475 /**
39476 * Label for various kinds of Angular entities for TS display info.
39477 */
39478 var DisplayInfoKind;
39479 (function (DisplayInfoKind) {
39480 DisplayInfoKind["ATTRIBUTE"] = "attribute";
39481 DisplayInfoKind["COMPONENT"] = "component";
39482 DisplayInfoKind["DIRECTIVE"] = "directive";
39483 DisplayInfoKind["EVENT"] = "event";
39484 DisplayInfoKind["REFERENCE"] = "reference";
39485 DisplayInfoKind["ELEMENT"] = "element";
39486 DisplayInfoKind["VARIABLE"] = "variable";
39487 DisplayInfoKind["PIPE"] = "pipe";
39488 DisplayInfoKind["PROPERTY"] = "property";
39489 DisplayInfoKind["METHOD"] = "method";
39490 DisplayInfoKind["TEMPLATE"] = "template";
39491 })(DisplayInfoKind || (DisplayInfoKind = {}));
39492 function getSymbolDisplayInfo(tsLS, typeChecker, symbol) {
39493 let kind;
39494 if (symbol.kind === SymbolKind.Reference) {
39495 kind = DisplayInfoKind.REFERENCE;
39496 }
39497 else if (symbol.kind === SymbolKind.Variable) {
39498 kind = DisplayInfoKind.VARIABLE;
39499 }
39500 else {
39501 throw new Error(`AssertionError: unexpected symbol kind ${SymbolKind[symbol.kind]}`);
39502 }
39503 const displayParts = createDisplayParts(symbol.declaration.name, kind, /* containerName */ undefined, typeChecker.typeToString(symbol.tsType));
39504 const documentation = symbol.kind === SymbolKind.Reference ?
39505 getDocumentationFromTypeDefAtLocation(tsLS, symbol.targetLocation) :
39506 getDocumentationFromTypeDefAtLocation(tsLS, symbol.initializerLocation);
39507 return {
39508 kind,
39509 displayParts,
39510 documentation,
39511 };
39512 }
39513 /**
39514 * Construct a compound `ts.SymbolDisplayPart[]` which incorporates the container and type of a
39515 * target declaration.
39516 * @param name Name of the target
39517 * @param kind component, directive, pipe, etc.
39518 * @param containerName either the Symbol's container or the NgModule that contains the directive
39519 * @param type user-friendly name of the type
39520 * @param documentation docstring or comment
39521 */
39522 function createDisplayParts(name, kind, containerName, type) {
39523 const containerDisplayParts = containerName !== undefined ?
39524 [
39525 { text: containerName, kind: SYMBOL_INTERFACE },
39526 { text: '.', kind: SYMBOL_PUNC },
39527 ] :
39528 [];
39529 const typeDisplayParts = type !== undefined ?
39530 [
39531 { text: ':', kind: SYMBOL_PUNC },
39532 { text: ' ', kind: SYMBOL_SPACE },
39533 { text: type, kind: SYMBOL_INTERFACE },
39534 ] :
39535 [];
39536 return [
39537 { text: '(', kind: SYMBOL_PUNC },
39538 { text: kind, kind: SYMBOL_TEXT },
39539 { text: ')', kind: SYMBOL_PUNC },
39540 { text: ' ', kind: SYMBOL_SPACE },
39541 ...containerDisplayParts,
39542 { text: name, kind: SYMBOL_INTERFACE },
39543 ...typeDisplayParts,
39544 ];
39545 }
39546 /**
39547 * Convert a `SymbolDisplayInfoKind` to a `ts.ScriptElementKind` type, allowing it to pass through
39548 * TypeScript APIs.
39549 *
39550 * In practice, this is an "illegal" type cast. Since `ts.ScriptElementKind` is a string, this is
39551 * safe to do if TypeScript only uses the value in a string context. Consumers of this conversion
39552 * function are responsible for ensuring this is the case.
39553 */
39554 function unsafeCastDisplayInfoKindToScriptElementKind(kind) {
39555 return kind;
39556 }
39557 function getDocumentationFromTypeDefAtLocation(tsLS, shimLocation) {
39558 var _a;
39559 const typeDefs = tsLS.getTypeDefinitionAtPosition(shimLocation.shimPath, shimLocation.positionInShimFile);
39560 if (typeDefs === undefined || typeDefs.length === 0) {
39561 return undefined;
39562 }
39563 return (_a = tsLS.getQuickInfoAtPosition(typeDefs[0].fileName, typeDefs[0].textSpan.start)) === null || _a === void 0 ? void 0 : _a.documentation;
39564 }
39565 function getDirectiveDisplayInfo(tsLS, dir) {
39566 var _a, _b;
39567 const kind = dir.isComponent ? DisplayInfoKind.COMPONENT : DisplayInfoKind.DIRECTIVE;
39568 const decl = dir.tsSymbol.declarations.find(ts$1.isClassDeclaration);
39569 if (decl === undefined || decl.name === undefined) {
39570 return { kind, displayParts: [], documentation: [] };
39571 }
39572 const res = tsLS.getQuickInfoAtPosition(decl.getSourceFile().fileName, decl.name.getStart());
39573 if (res === undefined) {
39574 return { kind, displayParts: [], documentation: [] };
39575 }
39576 const displayParts = createDisplayParts(dir.tsSymbol.name, kind, (_b = (_a = dir.ngModule) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.text, undefined);
39577 return {
39578 kind,
39579 displayParts,
39580 documentation: res.documentation,
39581 };
39582 }
39583 function getTsSymbolDisplayInfo(tsLS, checker, symbol, kind, ownerName) {
39584 const decl = symbol.valueDeclaration;
39585 if (decl === undefined || (!ts$1.isPropertyDeclaration(decl) && !ts$1.isMethodDeclaration(decl)) ||
39586 !ts$1.isIdentifier(decl.name)) {
39587 return null;
39588 }
39589 const res = tsLS.getQuickInfoAtPosition(decl.getSourceFile().fileName, decl.name.getStart());
39590 if (res === undefined) {
39591 return { kind, displayParts: [], documentation: [] };
39592 }
39593 const type = checker.getDeclaredTypeOfSymbol(symbol);
39594 const typeString = checker.typeToString(type);
39595 const displayParts = createDisplayParts(symbol.name, kind, ownerName !== null && ownerName !== void 0 ? ownerName : undefined, typeString);
39596 return {
39597 kind,
39598 displayParts,
39599 documentation: res.documentation,
39600 };
39601 }
39602
39603 /**
39604 * @license
39605 * Copyright Google LLC All Rights Reserved.
39606 *
39607 * Use of this source code is governed by an MIT-style license that can be
39608 * found in the LICENSE file at https://angular.io/license
39609 */
39610 /**
39611 * Return the node that most tightly encompasses the specified `position`.
39612 * @param node The starting node to start the top-down search.
39613 * @param position The target position within the `node`.
39614 */
39615 function findTightestNode(node, position) {
39616 var _a;
39617 if (node.getStart() <= position && position < node.getEnd()) {
39618 return (_a = node.forEachChild(c => findTightestNode(c, position))) !== null && _a !== void 0 ? _a : node;
39619 }
39620 return undefined;
39621 }
39622 function getParentClassDeclaration(startNode) {
39623 while (startNode) {
39624 if (ts$1.isClassDeclaration(startNode)) {
39625 return startNode;
39626 }
39627 startNode = startNode.parent;
39628 }
39629 return undefined;
39630 }
39631 /**
39632 * Returns a property assignment from the assignment value if the property name
39633 * matches the specified `key`, or `null` if there is no match.
39634 */
39635 function getPropertyAssignmentFromValue(value, key) {
39636 const propAssignment = value.parent;
39637 if (!propAssignment || !ts$1.isPropertyAssignment(propAssignment) ||
39638 propAssignment.name.getText() !== key) {
39639 return null;
39640 }
39641 return propAssignment;
39642 }
39643 /**
39644 * Given a decorator property assignment, return the ClassDeclaration node that corresponds to the
39645 * directive class the property applies to.
39646 * If the property assignment is not on a class decorator, no declaration is returned.
39647 *
39648 * For example,
39649 *
39650 * @Component({
39651 * template: '<div></div>'
39652 * ^^^^^^^^^^^^^^^^^^^^^^^---- property assignment
39653 * })
39654 * class AppComponent {}
39655 * ^---- class declaration node
39656 *
39657 * @param propAsgnNode property assignment
39658 */
39659 function getClassDeclFromDecoratorProp(propAsgnNode) {
39660 if (!propAsgnNode.parent || !ts$1.isObjectLiteralExpression(propAsgnNode.parent)) {
39661 return;
39662 }
39663 const objLitExprNode = propAsgnNode.parent;
39664 if (!objLitExprNode.parent || !ts$1.isCallExpression(objLitExprNode.parent)) {
39665 return;
39666 }
39667 const callExprNode = objLitExprNode.parent;
39668 if (!callExprNode.parent || !ts$1.isDecorator(callExprNode.parent)) {
39669 return;
39670 }
39671 const decorator = callExprNode.parent;
39672 if (!decorator.parent || !ts$1.isClassDeclaration(decorator.parent)) {
39673 return;
39674 }
39675 const classDeclNode = decorator.parent;
39676 return classDeclNode;
39677 }
39678
39679 /**
39680 * @license
39681 * Copyright Google LLC All Rights Reserved.
39682 *
39683 * Use of this source code is governed by an MIT-style license that can be
39684 * found in the LICENSE file at https://angular.io/license
39685 */
39686 function getTextSpanOfNode(node) {
39687 if (isTemplateNodeWithKeyAndValue(node)) {
39688 return toTextSpan(node.keySpan);
39689 }
39690 else if (node instanceof PropertyWrite || node instanceof MethodCall ||
39691 node instanceof BindingPipe || node instanceof PropertyRead) {
39692 // The `name` part of a `PropertyWrite`, `MethodCall`, and `BindingPipe` does not
39693 // have its own AST so there is no way to retrieve a `Symbol` for just the `name` via a specific
39694 // node.
39695 return toTextSpan(node.nameSpan);
39696 }
39697 else {
39698 return toTextSpan(node.sourceSpan);
39699 }
39700 }
39701 function toTextSpan(span) {
39702 let start, end;
39703 if (span instanceof AbsoluteSourceSpan || span instanceof ParseSpan) {
39704 start = span.start;
39705 end = span.end;
39706 }
39707 else {
39708 start = span.start.offset;
39709 end = span.end.offset;
39710 }
39711 return { start, length: end - start };
39712 }
39713 function isTemplateNodeWithKeyAndValue(node) {
39714 return isTemplateNode(node) && node.hasOwnProperty('keySpan');
39715 }
39716 function isWithinKeyValue(position, node) {
39717 let { keySpan, valueSpan } = node;
39718 if (valueSpan === undefined && node instanceof BoundEvent) {
39719 valueSpan = node.handlerSpan;
39720 }
39721 const isWithinKeyValue = isWithin(position, keySpan) || !!(valueSpan && isWithin(position, valueSpan));
39722 return isWithinKeyValue;
39723 }
39724 function isTemplateNode(node) {
39725 // Template node implements the Node interface so we cannot use instanceof.
39726 return node.sourceSpan instanceof ParseSourceSpan;
39727 }
39728 function getInlineTemplateInfoAtPosition(sf, position, compiler) {
39729 const expression = findTightestNode(sf, position);
39730 if (expression === undefined) {
39731 return undefined;
39732 }
39733 const classDecl = getParentClassDeclaration(expression);
39734 if (classDecl === undefined) {
39735 return undefined;
39736 }
39737 // Return `undefined` if the position is not on the template expression or the template resource
39738 // is not inline.
39739 const resources = compiler.getComponentResources(classDecl);
39740 if (resources === null || isExternalResource(resources.template) ||
39741 expression !== resources.template.expression) {
39742 return undefined;
39743 }
39744 const template = compiler.getTemplateTypeChecker().getTemplate(classDecl);
39745 if (template === null) {
39746 return undefined;
39747 }
39748 return { template, component: classDecl };
39749 }
39750 /**
39751 * Retrieves the `ts.ClassDeclaration` at a location along with its template nodes.
39752 */
39753 function getTemplateInfoAtPosition(fileName, position, compiler) {
39754 if (isTypeScriptFile(fileName)) {
39755 const sf = compiler.getNextProgram().getSourceFile(fileName);
39756 if (sf === undefined) {
39757 return undefined;
39758 }
39759 return getInlineTemplateInfoAtPosition(sf, position, compiler);
39760 }
39761 else {
39762 return getFirstComponentForTemplateFile(fileName, compiler);
39763 }
39764 }
39765 /**
39766 * First, attempt to sort component declarations by file name.
39767 * If the files are the same, sort by start location of the declaration.
39768 */
39769 function tsDeclarationSortComparator(a, b) {
39770 const aFile = a.getSourceFile().fileName;
39771 const bFile = b.getSourceFile().fileName;
39772 if (aFile < bFile) {
39773 return -1;
39774 }
39775 else if (aFile > bFile) {
39776 return 1;
39777 }
39778 else {
39779 return b.getFullStart() - a.getFullStart();
39780 }
39781 }
39782 function getFirstComponentForTemplateFile(fileName, compiler) {
39783 const templateTypeChecker = compiler.getTemplateTypeChecker();
39784 const components = compiler.getComponentsWithTemplateFile(fileName);
39785 const sortedComponents = Array.from(components).sort(tsDeclarationSortComparator);
39786 for (const component of sortedComponents) {
39787 if (!ts$1.isClassDeclaration(component)) {
39788 continue;
39789 }
39790 const template = templateTypeChecker.getTemplate(component);
39791 if (template === null) {
39792 continue;
39793 }
39794 return { template, component };
39795 }
39796 return undefined;
39797 }
39798 /**
39799 * Given an attribute node, converts it to string form.
39800 */
39801 function toAttributeString(attribute) {
39802 var _a, _b;
39803 if (attribute instanceof BoundEvent) {
39804 return `[${attribute.name}]`;
39805 }
39806 else {
39807 return `[${attribute.name}=${(_b = (_a = attribute.valueSpan) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : ''}]`;
39808 }
39809 }
39810 function getNodeName(node) {
39811 return node instanceof Template ? node.tagName : node.name;
39812 }
39813 /**
39814 * Given a template or element node, returns all attributes on the node.
39815 */
39816 function getAttributes(node) {
39817 const attributes = [...node.attributes, ...node.inputs, ...node.outputs];
39818 if (node instanceof Template) {
39819 attributes.push(...node.templateAttrs);
39820 }
39821 return attributes;
39822 }
39823 /**
39824 * Given two `Set`s, returns all items in the `left` which do not appear in the `right`.
39825 */
39826 function difference(left, right) {
39827 const result = new Set();
39828 for (const dir of left) {
39829 if (!right.has(dir)) {
39830 result.add(dir);
39831 }
39832 }
39833 return result;
39834 }
39835 /**
39836 * Given an element or template, determines which directives match because the tag is present. For
39837 * example, if a directive selector is `div[myAttr]`, this would match div elements but would not if
39838 * the selector were just `[myAttr]`. We find which directives are applied because of this tag by
39839 * elimination: compare the directive matches with the tag present against the directive matches
39840 * without it. The difference would be the directives which match because the tag is present.
39841 *
39842 * @param element The element or template node that the attribute/tag is part of.
39843 * @param directives The list of directives to match against.
39844 * @returns The list of directives matching the tag name via the strategy described above.
39845 */
39846 // TODO(atscott): Add unit tests for this and the one for attributes
39847 function getDirectiveMatchesForElementTag(element, directives) {
39848 const attributes = getAttributes(element);
39849 const allAttrs = attributes.map(toAttributeString);
39850 const allDirectiveMatches = getDirectiveMatchesForSelector(directives, getNodeName(element) + allAttrs.join(''));
39851 const matchesWithoutElement = getDirectiveMatchesForSelector(directives, allAttrs.join(''));
39852 return difference(allDirectiveMatches, matchesWithoutElement);
39853 }
39854 function makeElementSelector(element) {
39855 const attributes = getAttributes(element);
39856 const allAttrs = attributes.map(toAttributeString);
39857 return getNodeName(element) + allAttrs.join('');
39858 }
39859 /**
39860 * Given an attribute name, determines which directives match because the attribute is present. We
39861 * find which directives are applied because of this attribute by elimination: compare the directive
39862 * matches with the attribute present against the directive matches without it. The difference would
39863 * be the directives which match because the attribute is present.
39864 *
39865 * @param name The name of the attribute
39866 * @param hostNode The node which the attribute appears on
39867 * @param directives The list of directives to match against.
39868 * @returns The list of directives matching the tag name via the strategy described above.
39869 */
39870 function getDirectiveMatchesForAttribute(name, hostNode, directives) {
39871 const attributes = getAttributes(hostNode);
39872 const allAttrs = attributes.map(toAttributeString);
39873 const allDirectiveMatches = getDirectiveMatchesForSelector(directives, getNodeName(hostNode) + allAttrs.join(''));
39874 const attrsExcludingName = attributes.filter(a => a.name !== name).map(toAttributeString);
39875 const matchesWithoutAttr = getDirectiveMatchesForSelector(directives, getNodeName(hostNode) + attrsExcludingName.join(''));
39876 return difference(allDirectiveMatches, matchesWithoutAttr);
39877 }
39878 /**
39879 * Given a list of directives and a text to use as a selector, returns the directives which match
39880 * for the selector.
39881 */
39882 function getDirectiveMatchesForSelector(directives, selector) {
39883 const selectors = CssSelector.parse(selector);
39884 if (selectors.length === 0) {
39885 return new Set();
39886 }
39887 return new Set(directives.filter((dir) => {
39888 if (dir.selector === null) {
39889 return false;
39890 }
39891 const matcher = new SelectorMatcher();
39892 matcher.addSelectables(CssSelector.parse(dir.selector));
39893 return selectors.some(selector => matcher.match(selector, null));
39894 }));
39895 }
39896 /**
39897 * Returns a new `ts.SymbolDisplayPart` array which has the alias imports from the tcb filtered
39898 * out, i.e. `i0.NgForOf`.
39899 */
39900 function filterAliasImports(displayParts) {
39901 const tcbAliasImportRegex = /i\d+/;
39902 function isImportAlias(part) {
39903 return part.kind === ALIAS_NAME && tcbAliasImportRegex.test(part.text);
39904 }
39905 function isDotPunctuation(part) {
39906 return part.kind === SYMBOL_PUNC && part.text === '.';
39907 }
39908 return displayParts.filter((part, i) => {
39909 const previousPart = displayParts[i - 1];
39910 const nextPart = displayParts[i + 1];
39911 const aliasNameFollowedByDot = isImportAlias(part) && nextPart !== undefined && isDotPunctuation(nextPart);
39912 const dotPrecededByAlias = isDotPunctuation(part) && previousPart !== undefined && isImportAlias(previousPart);
39913 return !aliasNameFollowedByDot && !dotPrecededByAlias;
39914 });
39915 }
39916 function isDollarEvent(n) {
39917 return n instanceof PropertyRead && n.name === '$event' &&
39918 n.receiver instanceof ImplicitReceiver && !(n.receiver instanceof ThisReceiver);
39919 }
39920 /**
39921 * Returns a new array formed by applying a given callback function to each element of the array,
39922 * and then flattening the result by one level.
39923 */
39924 function flatMap(items, f) {
39925 const results = [];
39926 for (const x of items) {
39927 results.push(...f(x));
39928 }
39929 return results;
39930 }
39931 function isTypeScriptFile(fileName) {
39932 return fileName.endsWith('.ts');
39933 }
39934 function isWithin(position, span) {
39935 let start, end;
39936 if (span instanceof ParseSourceSpan) {
39937 start = span.start.offset;
39938 end = span.end.offset;
39939 }
39940 else {
39941 start = span.start;
39942 end = span.end;
39943 }
39944 // Note both start and end are inclusive because we want to match conditions
39945 // like ¦start and end¦ where ¦ is the cursor.
39946 return start <= position && position <= end;
39947 }
39948 /**
39949 * For a given location in a shim file, retrieves the corresponding file url for the template and
39950 * the span in the template.
39951 */
39952 function getTemplateLocationFromShimLocation(templateTypeChecker, shimPath, positionInShimFile) {
39953 const mapping = templateTypeChecker.getTemplateMappingAtShimLocation({ shimPath, positionInShimFile });
39954 if (mapping === null) {
39955 return null;
39956 }
39957 const { templateSourceMapping, span } = mapping;
39958 let templateUrl;
39959 if (templateSourceMapping.type === 'direct') {
39960 templateUrl = absoluteFromSourceFile(templateSourceMapping.node.getSourceFile());
39961 }
39962 else if (templateSourceMapping.type === 'external') {
39963 templateUrl = absoluteFrom(templateSourceMapping.templateUrl);
39964 }
39965 else {
39966 // This includes indirect mappings, which are difficult to map directly to the code
39967 // location. Diagnostics similarly return a synthetic template string for this case rather
39968 // than a real location.
39969 return null;
39970 }
39971 return { templateUrl, span };
39972 }
39973
39974 /**
39975 * @license
39976 * Copyright Google LLC All Rights Reserved.
39977 *
39978 * Use of this source code is governed by an MIT-style license that can be
39979 * found in the LICENSE file at https://angular.io/license
39980 */
39981 class LanguageServiceAdapter {
39982 constructor(project) {
39983 this.project = project;
39984 this.entryPoint = null;
39985 this.constructionDiagnostics = [];
39986 this.ignoreForEmit = new Set();
39987 this.factoryTracker = null; // no .ngfactory shims
39988 this.unifiedModulesHost = null; // only used in Bazel
39989 /**
39990 * Map of resource filenames to the version of the file last read via `readResource`.
39991 *
39992 * Used to implement `getModifiedResourceFiles`.
39993 */
39994 this.lastReadResourceVersion = new Map();
39995 this.rootDirs = getRootDirs(this, project.getCompilationSettings());
39996 }
39997 isShim(sf) {
39998 return isShim(sf);
39999 }
40000 fileExists(fileName) {
40001 return this.project.fileExists(fileName);
40002 }
40003 readFile(fileName) {
40004 return this.project.readFile(fileName);
40005 }
40006 getCurrentDirectory() {
40007 return this.project.getCurrentDirectory();
40008 }
40009 getCanonicalFileName(fileName) {
40010 return this.project.projectService.toCanonicalFileName(fileName);
40011 }
40012 /**
40013 * Return the real path of a symlink. This method is required in order to
40014 * resolve symlinks in node_modules.
40015 */
40016 realpath(path) {
40017 var _a, _b, _c;
40018 return (_c = (_b = (_a = this.project).realpath) === null || _b === void 0 ? void 0 : _b.call(_a, path)) !== null && _c !== void 0 ? _c : path;
40019 }
40020 /**
40021 * readResource() is an Angular-specific method for reading files that are not
40022 * managed by the TS compiler host, namely templates and stylesheets.
40023 * It is a method on ExtendedTsCompilerHost, see
40024 * packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts
40025 */
40026 readResource(fileName) {
40027 if (isTypeScriptFile(fileName)) {
40028 throw new Error(`readResource() should not be called on TS file: ${fileName}`);
40029 }
40030 // Calling getScriptSnapshot() will actually create a ScriptInfo if it does
40031 // not exist! The same applies for getScriptVersion().
40032 // getScriptInfo() will not create one if it does not exist.
40033 // In this case, we *want* a script info to be created so that we could
40034 // keep track of its version.
40035 const snapshot = this.project.getScriptSnapshot(fileName);
40036 if (!snapshot) {
40037 // This would fail if the file does not exist, or readFile() fails for
40038 // whatever reasons.
40039 throw new Error(`Failed to get script snapshot while trying to read ${fileName}`);
40040 }
40041 const version = this.project.getScriptVersion(fileName);
40042 this.lastReadResourceVersion.set(fileName, version);
40043 return snapshot.getText(0, snapshot.getLength());
40044 }
40045 getModifiedResourceFiles() {
40046 const modifiedFiles = new Set();
40047 for (const [fileName, oldVersion] of this.lastReadResourceVersion) {
40048 if (this.project.getScriptVersion(fileName) !== oldVersion) {
40049 modifiedFiles.add(fileName);
40050 }
40051 }
40052 return modifiedFiles.size > 0 ? modifiedFiles : undefined;
40053 }
40054 }
40055 /**
40056 * Used to read configuration files.
40057 *
40058 * A language service parse configuration host is independent of the adapter
40059 * because signatures of calls like `FileSystem#readFile` are a bit stricter
40060 * than those on the adapter.
40061 */
40062 class LSParseConfigHost {
40063 constructor(serverHost) {
40064 this.serverHost = serverHost;
40065 }
40066 exists(path) {
40067 return this.serverHost.fileExists(path) || this.serverHost.directoryExists(path);
40068 }
40069 readFile(path) {
40070 const content = this.serverHost.readFile(path);
40071 if (content === undefined) {
40072 throw new Error(`LanguageServiceFS#readFile called on unavailable file ${path}`);
40073 }
40074 return content;
40075 }
40076 lstat(path) {
40077 return {
40078 isFile: () => {
40079 return this.serverHost.fileExists(path);
40080 },
40081 isDirectory: () => {
40082 return this.serverHost.directoryExists(path);
40083 },
40084 isSymbolicLink: () => {
40085 throw new Error(`LanguageServiceFS#lstat#isSymbolicLink not implemented`);
40086 },
40087 };
40088 }
40089 pwd() {
40090 return this.serverHost.getCurrentDirectory();
40091 }
40092 extname(path$1) {
40093 return path.extname(path$1);
40094 }
40095 resolve(...paths) {
40096 return path.resolve(...paths);
40097 }
40098 dirname(file) {
40099 return path.dirname(file);
40100 }
40101 join(basePath, ...paths) {
40102 return path.join(basePath, ...paths);
40103 }
40104 }
40105
40106 /**
40107 * @license
40108 * Copyright Google LLC All Rights Reserved.
40109 *
40110 * Use of this source code is governed by an MIT-style license that can be
40111 * found in the LICENSE file at https://angular.io/license
40112 */
40113 /**
40114 * Manages the `NgCompiler` instance which backs the language service, updating or replacing it as
40115 * needed to produce an up-to-date understanding of the current program.
40116 *
40117 * TODO(alxhub): currently the options used for the compiler are specified at `CompilerFactory`
40118 * construction, and are not changable. In a real project, users can update `tsconfig.json`. We need
40119 * to properly handle a change in the compiler options, either by having an API to update the
40120 * `CompilerFactory` to use new options, or by replacing it entirely.
40121 */
40122 class CompilerFactory {
40123 constructor(adapter, programStrategy, options) {
40124 this.adapter = adapter;
40125 this.programStrategy = programStrategy;
40126 this.options = options;
40127 this.incrementalStrategy = new TrackedIncrementalBuildStrategy();
40128 this.compiler = null;
40129 this.lastKnownProgram = null;
40130 }
40131 getOrCreate() {
40132 var _a;
40133 const program = this.programStrategy.getProgram();
40134 const modifiedResourceFiles = (_a = this.adapter.getModifiedResourceFiles()) !== null && _a !== void 0 ? _a : new Set();
40135 if (this.compiler !== null && program === this.lastKnownProgram) {
40136 if (modifiedResourceFiles.size > 0) {
40137 // Only resource files have changed since the last NgCompiler was created.
40138 const ticket = resourceChangeTicket(this.compiler, modifiedResourceFiles);
40139 this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
40140 }
40141 return this.compiler;
40142 }
40143 let ticket;
40144 if (this.compiler === null || this.lastKnownProgram === null) {
40145 ticket = freshCompilationTicket(program, this.options, this.incrementalStrategy, this.programStrategy, true, true);
40146 }
40147 else {
40148 ticket = incrementalFromCompilerTicket(this.compiler, program, this.incrementalStrategy, this.programStrategy, modifiedResourceFiles);
40149 }
40150 this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
40151 this.lastKnownProgram = program;
40152 return this.compiler;
40153 }
40154 registerLastKnownProgram() {
40155 this.lastKnownProgram = this.programStrategy.getProgram();
40156 }
40157 }
40158
40159 /**
40160 * @license
40161 * Copyright Google LLC All Rights Reserved.
40162 *
40163 * Use of this source code is governed by an MIT-style license that can be
40164 * found in the LICENSE file at https://angular.io/license
40165 */
40166 /**
40167 * Differentiates different kinds of `AttributeCompletion`s.
40168 */
40169 var AttributeCompletionKind;
40170 (function (AttributeCompletionKind) {
40171 /**
40172 * Completion of an attribute from the HTML schema.
40173 *
40174 * Attributes often have a corresponding DOM property of the same name.
40175 */
40176 AttributeCompletionKind[AttributeCompletionKind["DomAttribute"] = 0] = "DomAttribute";
40177 /**
40178 * Completion of a property from the DOM schema.
40179 *
40180 * `DomProperty` completions are generated only for properties which don't share their name with
40181 * an HTML attribute.
40182 */
40183 AttributeCompletionKind[AttributeCompletionKind["DomProperty"] = 1] = "DomProperty";
40184 /**
40185 * Completion of an attribute that results in a new directive being matched on an element.
40186 */
40187 AttributeCompletionKind[AttributeCompletionKind["DirectiveAttribute"] = 2] = "DirectiveAttribute";
40188 /**
40189 * Completion of an attribute that results in a new structural directive being matched on an
40190 * element.
40191 */
40192 AttributeCompletionKind[AttributeCompletionKind["StructuralDirectiveAttribute"] = 3] = "StructuralDirectiveAttribute";
40193 /**
40194 * Completion of an input from a directive which is either present on the element, or becomes
40195 * present after the addition of this attribute.
40196 */
40197 AttributeCompletionKind[AttributeCompletionKind["DirectiveInput"] = 4] = "DirectiveInput";
40198 /**
40199 * Completion of an output from a directive which is either present on the element, or becomes
40200 * present after the addition of this attribute.
40201 */
40202 AttributeCompletionKind[AttributeCompletionKind["DirectiveOutput"] = 5] = "DirectiveOutput";
40203 })(AttributeCompletionKind || (AttributeCompletionKind = {}));
40204 /**
40205 * Given an element and its context, produce a `Map` of all possible attribute completions.
40206 *
40207 * 3 kinds of attributes are considered for completion, from highest to lowest priority:
40208 *
40209 * 1. Inputs/outputs of directives present on the element already.
40210 * 2. Inputs/outputs of directives that are not present on the element, but which would become
40211 * present if such a binding is added.
40212 * 3. Attributes from the DOM schema for the element.
40213 *
40214 * The priority of these options determines which completions are added to the `Map`. If a directive
40215 * input shares the same name as a DOM attribute, the `Map` will reflect the directive input
40216 * completion, not the DOM completion for that name.
40217 */
40218 function buildAttributeCompletionTable(component, element, checker) {
40219 const table = new Map();
40220 // Use the `ElementSymbol` or `TemplateSymbol` to iterate over directives present on the node, and
40221 // their inputs/outputs. These have the highest priority of completion results.
40222 const symbol = checker.getSymbolOfNode(element, component);
40223 const presentDirectives = new Set();
40224 if (symbol !== null) {
40225 // An `ElementSymbol` was available. This means inputs and outputs for directives on the
40226 // element can be added to the completion table.
40227 for (const dirSymbol of symbol.directives) {
40228 const directive = dirSymbol.tsSymbol.valueDeclaration;
40229 if (!ts$1.isClassDeclaration(directive)) {
40230 continue;
40231 }
40232 presentDirectives.add(directive);
40233 const meta = checker.getDirectiveMetadata(directive);
40234 if (meta === null) {
40235 continue;
40236 }
40237 for (const [classPropertyName, propertyName] of meta.inputs) {
40238 if (table.has(propertyName)) {
40239 continue;
40240 }
40241 table.set(propertyName, {
40242 kind: AttributeCompletionKind.DirectiveInput,
40243 propertyName,
40244 directive: dirSymbol,
40245 classPropertyName,
40246 twoWayBindingSupported: meta.outputs.hasBindingPropertyName(propertyName + 'Change'),
40247 });
40248 }
40249 for (const [classPropertyName, propertyName] of meta.outputs) {
40250 if (table.has(propertyName)) {
40251 continue;
40252 }
40253 table.set(propertyName, {
40254 kind: AttributeCompletionKind.DirectiveOutput,
40255 eventName: propertyName,
40256 directive: dirSymbol,
40257 classPropertyName,
40258 });
40259 }
40260 }
40261 }
40262 // Next, explore hypothetical directives and determine if the addition of any single attributes
40263 // can cause the directive to match the element.
40264 const directivesInScope = checker.getDirectivesInScope(component);
40265 if (directivesInScope !== null) {
40266 const elementSelector = makeElementSelector(element);
40267 for (const dirInScope of directivesInScope) {
40268 const directive = dirInScope.tsSymbol.valueDeclaration;
40269 // Skip directives that are present on the element.
40270 if (!ts$1.isClassDeclaration(directive) || presentDirectives.has(directive)) {
40271 continue;
40272 }
40273 const meta = checker.getDirectiveMetadata(directive);
40274 if (meta === null || meta.selector === null) {
40275 continue;
40276 }
40277 if (!meta.isStructural) {
40278 // For non-structural directives, the directive's attribute selector(s) are matched against
40279 // a hypothetical version of the element with those attributes. A match indicates that
40280 // adding that attribute/input/output binding would cause the directive to become present,
40281 // meaning that such a binding is a valid completion.
40282 const selectors = CssSelector.parse(meta.selector);
40283 const matcher = new SelectorMatcher();
40284 matcher.addSelectables(selectors);
40285 for (const selector of selectors) {
40286 for (const [attrName, attrValue] of selectorAttributes(selector)) {
40287 if (attrValue !== '') {
40288 // This attribute selector requires a value, which is not supported in completion.
40289 continue;
40290 }
40291 if (table.has(attrName)) {
40292 // Skip this attribute as there's already a binding for it.
40293 continue;
40294 }
40295 // Check whether adding this attribute would cause the directive to start matching.
40296 const newElementSelector = elementSelector + `[${attrName}]`;
40297 if (!matcher.match(CssSelector.parse(newElementSelector)[0], null)) {
40298 // Nope, move on with our lives.
40299 continue;
40300 }
40301 // Adding this attribute causes a new directive to be matched. Decide how to categorize
40302 // it based on the directive's inputs and outputs.
40303 if (meta.inputs.hasBindingPropertyName(attrName)) {
40304 // This attribute corresponds to an input binding.
40305 table.set(attrName, {
40306 kind: AttributeCompletionKind.DirectiveInput,
40307 directive: dirInScope,
40308 propertyName: attrName,
40309 classPropertyName: meta.inputs.getByBindingPropertyName(attrName)[0].classPropertyName,
40310 twoWayBindingSupported: meta.outputs.hasBindingPropertyName(attrName + 'Change'),
40311 });
40312 }
40313 else if (meta.outputs.hasBindingPropertyName(attrName)) {
40314 // This attribute corresponds to an output binding.
40315 table.set(attrName, {
40316 kind: AttributeCompletionKind.DirectiveOutput,
40317 directive: dirInScope,
40318 eventName: attrName,
40319 classPropertyName: meta.outputs.getByBindingPropertyName(attrName)[0].classPropertyName,
40320 });
40321 }
40322 else {
40323 // This attribute causes a new directive to be matched, but does not also correspond
40324 // to an input or output binding.
40325 table.set(attrName, {
40326 kind: AttributeCompletionKind.DirectiveAttribute,
40327 attribute: attrName,
40328 directive: dirInScope,
40329 });
40330 }
40331 }
40332 }
40333 }
40334 else {
40335 // Hypothetically matching a structural directive is a litle different than a plain
40336 // directive. Use of the '*' structural directive syntactic sugar means that the actual
40337 // directive is applied to a plain <ng-template> node, not the existing element with any
40338 // other attributes it might already have.
40339 // Additionally, more than one attribute/input might need to be present in order for the
40340 // directive to match (e.g. `ngFor` has a selector of `[ngFor][ngForOf]`). This gets a
40341 // little tricky.
40342 const structuralAttributes = getStructuralAttributes(meta);
40343 for (const attrName of structuralAttributes) {
40344 table.set(attrName, {
40345 kind: AttributeCompletionKind.StructuralDirectiveAttribute,
40346 attribute: attrName,
40347 directive: dirInScope,
40348 });
40349 }
40350 }
40351 }
40352 }
40353 // Finally, add any DOM attributes not already covered by inputs.
40354 if (element instanceof Element) {
40355 for (const { attribute, property } of checker.getPotentialDomBindings(element.name)) {
40356 const isAlsoProperty = attribute === property;
40357 if (!table.has(attribute)) {
40358 table.set(attribute, {
40359 kind: AttributeCompletionKind.DomAttribute,
40360 attribute,
40361 isAlsoProperty,
40362 });
40363 }
40364 if (!isAlsoProperty && !table.has(property)) {
40365 table.set(property, {
40366 kind: AttributeCompletionKind.DomProperty,
40367 property,
40368 });
40369 }
40370 }
40371 }
40372 return table;
40373 }
40374 /**
40375 * Given an `AttributeCompletion`, add any available completions to a `ts.CompletionEntry` array of
40376 * results.
40377 *
40378 * The kind of completions generated depends on whether the current context is an attribute context
40379 * or not. For example, completing on `<element attr|>` will generate two results: `attribute` and
40380 * `[attribute]` - either a static attribute can be generated, or a property binding. However,
40381 * `<element [attr|]>` is not an attribute context, and so only the property completion `attribute`
40382 * is generated. Note that this completion does not have the `[]` property binding sugar as its
40383 * implicitly present in a property binding context (we're already completing within an `[attr|]`
40384 * expression).
40385 */
40386 function addAttributeCompletionEntries(entries, completion, isAttributeContext, isElementContext, replacementSpan) {
40387 switch (completion.kind) {
40388 case AttributeCompletionKind.DirectiveAttribute: {
40389 entries.push({
40390 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.DIRECTIVE),
40391 name: completion.attribute,
40392 sortText: completion.attribute,
40393 replacementSpan,
40394 });
40395 break;
40396 }
40397 case AttributeCompletionKind.StructuralDirectiveAttribute: {
40398 // In an element, the completion is offered with a leading '*' to activate the structural
40399 // directive. Once present, the structural attribute will be parsed as a template and not an
40400 // element, and the prefix is no longer necessary.
40401 const prefix = isElementContext ? '*' : '';
40402 entries.push({
40403 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.DIRECTIVE),
40404 name: prefix + completion.attribute,
40405 sortText: prefix + completion.attribute,
40406 replacementSpan,
40407 });
40408 break;
40409 }
40410 case AttributeCompletionKind.DirectiveInput: {
40411 if (isAttributeContext) {
40412 // Offer a completion of a property binding.
40413 entries.push({
40414 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40415 name: `[${completion.propertyName}]`,
40416 sortText: completion.propertyName,
40417 replacementSpan,
40418 });
40419 // If the directive supports banana-in-a-box for this input, offer that as well.
40420 if (completion.twoWayBindingSupported) {
40421 entries.push({
40422 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40423 name: `[(${completion.propertyName})]`,
40424 // This completion should sort after the property binding.
40425 sortText: completion.propertyName + '_1',
40426 replacementSpan,
40427 });
40428 }
40429 // Offer a completion of the input binding as an attribute.
40430 entries.push({
40431 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.ATTRIBUTE),
40432 name: completion.propertyName,
40433 // This completion should sort after both property binding options (one-way and two-way).
40434 sortText: completion.propertyName + '_2',
40435 replacementSpan,
40436 });
40437 }
40438 else {
40439 entries.push({
40440 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40441 name: completion.propertyName,
40442 sortText: completion.propertyName,
40443 replacementSpan,
40444 });
40445 }
40446 break;
40447 }
40448 case AttributeCompletionKind.DirectiveOutput: {
40449 if (isAttributeContext) {
40450 entries.push({
40451 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.EVENT),
40452 name: `(${completion.eventName})`,
40453 sortText: completion.eventName,
40454 replacementSpan,
40455 });
40456 }
40457 else {
40458 entries.push({
40459 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.EVENT),
40460 name: completion.eventName,
40461 sortText: completion.eventName,
40462 replacementSpan,
40463 });
40464 }
40465 break;
40466 }
40467 case AttributeCompletionKind.DomAttribute: {
40468 if (isAttributeContext) {
40469 // Offer a completion of an attribute binding.
40470 entries.push({
40471 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.ATTRIBUTE),
40472 name: completion.attribute,
40473 sortText: completion.attribute,
40474 replacementSpan,
40475 });
40476 if (completion.isAlsoProperty) {
40477 // Offer a completion of a property binding to the DOM property.
40478 entries.push({
40479 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40480 name: `[${completion.attribute}]`,
40481 // In the case of DOM attributes, the property binding should sort after the attribute
40482 // binding.
40483 sortText: completion.attribute + '_1',
40484 replacementSpan,
40485 });
40486 }
40487 }
40488 else if (completion.isAlsoProperty) {
40489 entries.push({
40490 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40491 name: completion.attribute,
40492 sortText: completion.attribute,
40493 replacementSpan,
40494 });
40495 }
40496 break;
40497 }
40498 case AttributeCompletionKind.DomProperty: {
40499 if (!isAttributeContext) {
40500 entries.push({
40501 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40502 name: completion.property,
40503 sortText: completion.property,
40504 replacementSpan,
40505 });
40506 }
40507 }
40508 }
40509 }
40510 function getAttributeCompletionSymbol(completion, checker) {
40511 var _a;
40512 switch (completion.kind) {
40513 case AttributeCompletionKind.DomAttribute:
40514 case AttributeCompletionKind.DomProperty:
40515 return null;
40516 case AttributeCompletionKind.DirectiveAttribute:
40517 case AttributeCompletionKind.StructuralDirectiveAttribute:
40518 return completion.directive.tsSymbol;
40519 case AttributeCompletionKind.DirectiveInput:
40520 case AttributeCompletionKind.DirectiveOutput:
40521 return (_a = checker.getDeclaredTypeOfSymbol(completion.directive.tsSymbol)
40522 .getProperty(completion.classPropertyName)) !== null && _a !== void 0 ? _a : null;
40523 }
40524 }
40525 /**
40526 * Iterates over `CssSelector` attributes, which are internally represented in a zipped array style
40527 * which is not conducive to straightforward iteration.
40528 */
40529 function* selectorAttributes(selector) {
40530 for (let i = 0; i < selector.attrs.length; i += 2) {
40531 yield [selector.attrs[0], selector.attrs[1]];
40532 }
40533 }
40534 function getStructuralAttributes(meta) {
40535 if (meta.selector === null) {
40536 return [];
40537 }
40538 const structuralAttributes = [];
40539 const selectors = CssSelector.parse(meta.selector);
40540 for (const selector of selectors) {
40541 if (selector.element !== null && selector.element !== 'ng-template') {
40542 // This particular selector does not apply under structural directive syntax.
40543 continue;
40544 }
40545 // Every attribute of this selector must be name-only - no required values.
40546 const attributeSelectors = Array.from(selectorAttributes(selector));
40547 if (!attributeSelectors.every(([_, attrValue]) => attrValue === '')) {
40548 continue;
40549 }
40550 // Get every named selector.
40551 const attributes = attributeSelectors.map(([attrName, _]) => attrName);
40552 // Find the shortest attribute. This is the structural directive "base", and all potential
40553 // input bindings must begin with the base. E.g. in `*ngFor="let a of b"`, `ngFor` is the
40554 // base attribute, and the `of` binding key corresponds to an input of `ngForOf`.
40555 const baseAttr = attributes.reduce((prev, curr) => prev === null || curr.length < prev.length ? curr : prev, null);
40556 if (baseAttr === null) {
40557 // No attributes in this selector?
40558 continue;
40559 }
40560 // Validate that the attributes are compatible with use as a structural directive.
40561 const isValid = (attr) => {
40562 // The base attribute is valid by default.
40563 if (attr === baseAttr) {
40564 return true;
40565 }
40566 // Non-base attributes must all be prefixed with the base attribute.
40567 if (!attr.startsWith(baseAttr)) {
40568 return false;
40569 }
40570 // Non-base attributes must also correspond to directive inputs.
40571 if (!meta.inputs.hasBindingPropertyName(attr)) {
40572 return false;
40573 }
40574 // This attribute is compatible.
40575 return true;
40576 };
40577 if (!attributes.every(isValid)) {
40578 continue;
40579 }
40580 // This attribute is valid as a structural attribute for this directive.
40581 structuralAttributes.push(baseAttr);
40582 }
40583 return structuralAttributes;
40584 }
40585
40586 /**
40587 * @license
40588 * Copyright Google LLC All Rights Reserved.
40589 *
40590 * Use of this source code is governed by an MIT-style license that can be
40591 * found in the LICENSE file at https://angular.io/license
40592 */
40593 /**
40594 * Differentiates the various kinds of `TargetNode`s.
40595 */
40596 var TargetNodeKind;
40597 (function (TargetNodeKind) {
40598 TargetNodeKind[TargetNodeKind["RawExpression"] = 0] = "RawExpression";
40599 TargetNodeKind[TargetNodeKind["RawTemplateNode"] = 1] = "RawTemplateNode";
40600 TargetNodeKind[TargetNodeKind["ElementInTagContext"] = 2] = "ElementInTagContext";
40601 TargetNodeKind[TargetNodeKind["ElementInBodyContext"] = 3] = "ElementInBodyContext";
40602 TargetNodeKind[TargetNodeKind["AttributeInKeyContext"] = 4] = "AttributeInKeyContext";
40603 TargetNodeKind[TargetNodeKind["AttributeInValueContext"] = 5] = "AttributeInValueContext";
40604 TargetNodeKind[TargetNodeKind["TwoWayBindingContext"] = 6] = "TwoWayBindingContext";
40605 })(TargetNodeKind || (TargetNodeKind = {}));
40606 /**
40607 * This special marker is added to the path when the cursor is within the sourceSpan but not the key
40608 * or value span of a node with key/value spans.
40609 */
40610 const OUTSIDE_K_V_MARKER = new AST(new ParseSpan(-1, -1), new AbsoluteSourceSpan(-1, -1));
40611 /**
40612 * Return the template AST node or expression AST node that most accurately
40613 * represents the node at the specified cursor `position`.
40614 *
40615 * @param template AST tree of the template
40616 * @param position target cursor position
40617 */
40618 function getTargetAtPosition(template, position) {
40619 const path = TemplateTargetVisitor.visitTemplate(template, position);
40620 if (path.length === 0) {
40621 return null;
40622 }
40623 const candidate = path[path.length - 1];
40624 // Walk up the result nodes to find the nearest `t.Template` which contains the targeted node.
40625 let context = null;
40626 for (let i = path.length - 2; i >= 0; i--) {
40627 const node = path[i];
40628 if (node instanceof Template) {
40629 context = node;
40630 break;
40631 }
40632 }
40633 // Given the candidate node, determine the full targeted context.
40634 let nodeInContext;
40635 if (candidate instanceof AST) {
40636 nodeInContext = {
40637 kind: TargetNodeKind.RawExpression,
40638 node: candidate,
40639 };
40640 }
40641 else if (candidate instanceof Element) {
40642 // Elements have two contexts: the tag context (position is within the element tag) or the
40643 // element body context (position is outside of the tag name, but still in the element).
40644 // Calculate the end of the element tag name. Any position beyond this is in the element body.
40645 const tagEndPos = candidate.sourceSpan.start.offset + 1 /* '<' element open */ + candidate.name.length;
40646 if (position > tagEndPos) {
40647 // Position is within the element body
40648 nodeInContext = {
40649 kind: TargetNodeKind.ElementInBodyContext,
40650 node: candidate,
40651 };
40652 }
40653 else {
40654 nodeInContext = {
40655 kind: TargetNodeKind.ElementInTagContext,
40656 node: candidate,
40657 };
40658 }
40659 }
40660 else if ((candidate instanceof BoundAttribute || candidate instanceof BoundEvent ||
40661 candidate instanceof TextAttribute) &&
40662 candidate.keySpan !== undefined) {
40663 const previousCandidate = path[path.length - 2];
40664 if (candidate instanceof BoundEvent && previousCandidate instanceof BoundAttribute &&
40665 candidate.name === previousCandidate.name + 'Change') {
40666 const boundAttribute = previousCandidate;
40667 const boundEvent = candidate;
40668 nodeInContext = {
40669 kind: TargetNodeKind.TwoWayBindingContext,
40670 nodes: [boundAttribute, boundEvent],
40671 };
40672 }
40673 else if (isWithin(position, candidate.keySpan)) {
40674 nodeInContext = {
40675 kind: TargetNodeKind.AttributeInKeyContext,
40676 node: candidate,
40677 };
40678 }
40679 else {
40680 nodeInContext = {
40681 kind: TargetNodeKind.AttributeInValueContext,
40682 node: candidate,
40683 };
40684 }
40685 }
40686 else {
40687 nodeInContext = {
40688 kind: TargetNodeKind.RawTemplateNode,
40689 node: candidate,
40690 };
40691 }
40692 let parent = null;
40693 if (nodeInContext.kind === TargetNodeKind.TwoWayBindingContext && path.length >= 3) {
40694 parent = path[path.length - 3];
40695 }
40696 else if (path.length >= 2) {
40697 parent = path[path.length - 2];
40698 }
40699 return { position, context: nodeInContext, template: context, parent };
40700 }
40701 /**
40702 * Visitor which, given a position and a template, identifies the node within the template at that
40703 * position, as well as records the path of increasingly nested nodes that were traversed to reach
40704 * that position.
40705 */
40706 class TemplateTargetVisitor {
40707 // Position must be absolute in the source file.
40708 constructor(position) {
40709 this.position = position;
40710 // We need to keep a path instead of the last node because we might need more
40711 // context for the last node, for example what is the parent node?
40712 this.path = [];
40713 }
40714 static visitTemplate(template, position) {
40715 const visitor = new TemplateTargetVisitor(position);
40716 visitor.visitAll(template);
40717 const { path } = visitor;
40718 const strictPath = path.filter(v => v !== OUTSIDE_K_V_MARKER);
40719 const candidate = strictPath[strictPath.length - 1];
40720 const matchedASourceSpanButNotAKvSpan = path.some(v => v === OUTSIDE_K_V_MARKER);
40721 if (matchedASourceSpanButNotAKvSpan &&
40722 (candidate instanceof Template || candidate instanceof Element)) {
40723 // Template nodes with key and value spans are always defined on a `t.Template` or
40724 // `t.Element`. If we found a node on a template with a `sourceSpan` that includes the cursor,
40725 // it is possible that we are outside the k/v spans (i.e. in-between them). If this is the
40726 // case and we do not have any other candidate matches on the `t.Element` or `t.Template`, we
40727 // want to return no results. Otherwise, the `t.Element`/`t.Template` result is incorrect for
40728 // that cursor position.
40729 return [];
40730 }
40731 return strictPath;
40732 }
40733 visit(node) {
40734 const { start, end } = getSpanIncludingEndTag(node);
40735 if (!isWithin(this.position, { start, end })) {
40736 return;
40737 }
40738 const last = this.path[this.path.length - 1];
40739 const withinKeySpanOfLastNode = last && isTemplateNodeWithKeyAndValue(last) && isWithin(this.position, last.keySpan);
40740 const withinKeySpanOfCurrentNode = isTemplateNodeWithKeyAndValue(node) && isWithin(this.position, node.keySpan);
40741 if (withinKeySpanOfLastNode && !withinKeySpanOfCurrentNode) {
40742 // We've already identified that we are within a `keySpan` of a node.
40743 // Unless we are _also_ in the `keySpan` of the current node (happens with two way bindings),
40744 // we should stop processing nodes at this point to prevent matching any other nodes. This can
40745 // happen when the end span of a different node touches the start of the keySpan for the
40746 // candidate node. Because our `isWithin` logic is inclusive on both ends, we can match both
40747 // nodes.
40748 return;
40749 }
40750 if (isTemplateNodeWithKeyAndValue(node) && !isWithinKeyValue(this.position, node)) {
40751 // If cursor is within source span but not within key span or value span,
40752 // do not return the node.
40753 this.path.push(OUTSIDE_K_V_MARKER);
40754 }
40755 else {
40756 this.path.push(node);
40757 node.visit(this);
40758 }
40759 }
40760 visitElement(element) {
40761 this.visitElementOrTemplate(element);
40762 }
40763 visitTemplate(template) {
40764 this.visitElementOrTemplate(template);
40765 }
40766 visitElementOrTemplate(element) {
40767 this.visitAll(element.attributes);
40768 this.visitAll(element.inputs);
40769 this.visitAll(element.outputs);
40770 if (element instanceof Template) {
40771 this.visitAll(element.templateAttrs);
40772 }
40773 this.visitAll(element.references);
40774 if (element instanceof Template) {
40775 this.visitAll(element.variables);
40776 }
40777 // If we get here and have not found a candidate node on the element itself, proceed with
40778 // looking for a more specific node on the element children.
40779 if (this.path[this.path.length - 1] !== element) {
40780 return;
40781 }
40782 this.visitAll(element.children);
40783 }
40784 visitContent(content) {
40785 visitAll(this, content.attributes);
40786 }
40787 visitVariable(variable) {
40788 // Variable has no template nodes or expression nodes.
40789 }
40790 visitReference(reference) {
40791 // Reference has no template nodes or expression nodes.
40792 }
40793 visitTextAttribute(attribute) {
40794 // Text attribute has no template nodes or expression nodes.
40795 }
40796 visitBoundAttribute(attribute) {
40797 const visitor = new ExpressionVisitor$1(this.position);
40798 visitor.visit(attribute.value, this.path);
40799 }
40800 visitBoundEvent(event) {
40801 // An event binding with no value (e.g. `(event|)`) parses to a `BoundEvent` with a
40802 // `LiteralPrimitive` handler with value `'ERROR'`, as opposed to a property binding with no
40803 // value which has an `EmptyExpr` as its value. This is a synthetic node created by the binding
40804 // parser, and is not suitable to use for Language Service analysis. Skip it.
40805 //
40806 // TODO(alxhub): modify the parser to generate an `EmptyExpr` instead.
40807 let handler = event.handler;
40808 if (handler instanceof ASTWithSource) {
40809 handler = handler.ast;
40810 }
40811 if (handler instanceof LiteralPrimitive && handler.value === 'ERROR') {
40812 return;
40813 }
40814 const visitor = new ExpressionVisitor$1(this.position);
40815 visitor.visit(event.handler, this.path);
40816 }
40817 visitText(text) {
40818 // Text has no template nodes or expression nodes.
40819 }
40820 visitBoundText(text) {
40821 const visitor = new ExpressionVisitor$1(this.position);
40822 visitor.visit(text.value, this.path);
40823 }
40824 visitIcu(icu) {
40825 for (const boundText of Object.values(icu.vars)) {
40826 this.visit(boundText);
40827 }
40828 for (const boundTextOrText of Object.values(icu.placeholders)) {
40829 this.visit(boundTextOrText);
40830 }
40831 }
40832 visitAll(nodes) {
40833 for (const node of nodes) {
40834 this.visit(node);
40835 }
40836 }
40837 }
40838 class ExpressionVisitor$1 extends RecursiveAstVisitor {
40839 // Position must be absolute in the source file.
40840 constructor(position) {
40841 super();
40842 this.position = position;
40843 }
40844 visit(node, path) {
40845 if (node instanceof ASTWithSource) {
40846 // In order to reduce noise, do not include `ASTWithSource` in the path.
40847 // For the purpose of source spans, there is no difference between
40848 // `ASTWithSource` and and underlying node that it wraps.
40849 node = node.ast;
40850 }
40851 // The third condition is to account for the implicit receiver, which should
40852 // not be visited.
40853 if (isWithin(this.position, node.sourceSpan) && !(node instanceof ImplicitReceiver)) {
40854 path.push(node);
40855 node.visit(this, path);
40856 }
40857 }
40858 }
40859 function getSpanIncludingEndTag(ast) {
40860 const result = {
40861 start: ast.sourceSpan.start.offset,
40862 end: ast.sourceSpan.end.offset,
40863 };
40864 // For Element and Template node, sourceSpan.end is the end of the opening
40865 // tag. For the purpose of language service, we need to actually recognize
40866 // the end of the closing tag. Otherwise, for situation like
40867 // <my-component></my-comp¦onent> where the cursor is in the closing tag
40868 // we will not be able to return any information.
40869 if ((ast instanceof Element || ast instanceof Template) && ast.endSourceSpan) {
40870 result.end = ast.endSourceSpan.end.offset;
40871 }
40872 return result;
40873 }
40874
40875 /**
40876 * @license
40877 * Copyright Google LLC All Rights Reserved.
40878 *
40879 * Use of this source code is governed by an MIT-style license that can be
40880 * found in the LICENSE file at https://angular.io/license
40881 */
40882 var CompletionNodeContext;
40883 (function (CompletionNodeContext) {
40884 CompletionNodeContext[CompletionNodeContext["None"] = 0] = "None";
40885 CompletionNodeContext[CompletionNodeContext["ElementTag"] = 1] = "ElementTag";
40886 CompletionNodeContext[CompletionNodeContext["ElementAttributeKey"] = 2] = "ElementAttributeKey";
40887 CompletionNodeContext[CompletionNodeContext["ElementAttributeValue"] = 3] = "ElementAttributeValue";
40888 CompletionNodeContext[CompletionNodeContext["EventValue"] = 4] = "EventValue";
40889 CompletionNodeContext[CompletionNodeContext["TwoWayBinding"] = 5] = "TwoWayBinding";
40890 })(CompletionNodeContext || (CompletionNodeContext = {}));
40891 /**
40892 * Performs autocompletion operations on a given node in the template.
40893 *
40894 * This class acts as a closure around all of the context required to perform the 3 autocompletion
40895 * operations (completions, get details, and get symbol) at a specific node.
40896 *
40897 * The generic `N` type for the template node is narrowed internally for certain operations, as the
40898 * compiler operations required to implement completion may be different for different node types.
40899 *
40900 * @param N type of the template node in question, narrowed accordingly.
40901 */
40902 class CompletionBuilder {
40903 constructor(tsLS, compiler, component, node, targetDetails) {
40904 this.tsLS = tsLS;
40905 this.compiler = compiler;
40906 this.component = component;
40907 this.node = node;
40908 this.targetDetails = targetDetails;
40909 this.typeChecker = this.compiler.getNextProgram().getTypeChecker();
40910 this.templateTypeChecker = this.compiler.getTemplateTypeChecker();
40911 this.nodeParent = this.targetDetails.parent;
40912 this.nodeContext = nodeContextFromTarget(this.targetDetails.context);
40913 this.template = this.targetDetails.template;
40914 this.position = this.targetDetails.position;
40915 }
40916 /**
40917 * Analogue for `ts.LanguageService.getCompletionsAtPosition`.
40918 */
40919 getCompletionsAtPosition(options) {
40920 if (this.isPropertyExpressionCompletion()) {
40921 return this.getPropertyExpressionCompletion(options);
40922 }
40923 else if (this.isElementTagCompletion()) {
40924 return this.getElementTagCompletion();
40925 }
40926 else if (this.isElementAttributeCompletion()) {
40927 return this.getElementAttributeCompletions();
40928 }
40929 else if (this.isPipeCompletion()) {
40930 return this.getPipeCompletions();
40931 }
40932 else {
40933 return undefined;
40934 }
40935 }
40936 /**
40937 * Analogue for `ts.LanguageService.getCompletionEntryDetails`.
40938 */
40939 getCompletionEntryDetails(entryName, formatOptions, preferences) {
40940 if (this.isPropertyExpressionCompletion()) {
40941 return this.getPropertyExpressionCompletionDetails(entryName, formatOptions, preferences);
40942 }
40943 else if (this.isElementTagCompletion()) {
40944 return this.getElementTagCompletionDetails(entryName);
40945 }
40946 else if (this.isElementAttributeCompletion()) {
40947 return this.getElementAttributeCompletionDetails(entryName);
40948 }
40949 }
40950 /**
40951 * Analogue for `ts.LanguageService.getCompletionEntrySymbol`.
40952 */
40953 getCompletionEntrySymbol(name) {
40954 if (this.isPropertyExpressionCompletion()) {
40955 return this.getPropertyExpressionCompletionSymbol(name);
40956 }
40957 else if (this.isElementTagCompletion()) {
40958 return this.getElementTagCompletionSymbol(name);
40959 }
40960 else if (this.isElementAttributeCompletion()) {
40961 return this.getElementAttributeCompletionSymbol(name);
40962 }
40963 else {
40964 return undefined;
40965 }
40966 }
40967 /**
40968 * Determine if the current node is the completion of a property expression, and narrow the type
40969 * of `this.node` if so.
40970 *
40971 * This narrowing gives access to additional methods related to completion of property
40972 * expressions.
40973 */
40974 isPropertyExpressionCompletion() {
40975 return this.node instanceof PropertyRead || this.node instanceof MethodCall ||
40976 this.node instanceof SafePropertyRead || this.node instanceof SafeMethodCall ||
40977 this.node instanceof PropertyWrite || this.node instanceof EmptyExpr ||
40978 // BoundEvent nodes only count as property completions if in an EventValue context.
40979 (this.node instanceof BoundEvent && this.nodeContext === CompletionNodeContext.EventValue);
40980 }
40981 /**
40982 * Get completions for property expressions.
40983 */
40984 getPropertyExpressionCompletion(options) {
40985 if (this.node instanceof EmptyExpr || this.node instanceof BoundEvent ||
40986 this.node.receiver instanceof ImplicitReceiver) {
40987 return this.getGlobalPropertyExpressionCompletion(options);
40988 }
40989 else {
40990 const location = this.compiler.getTemplateTypeChecker().getExpressionCompletionLocation(this.node, this.component);
40991 if (location === null) {
40992 return undefined;
40993 }
40994 const tsResults = this.tsLS.getCompletionsAtPosition(location.shimPath, location.positionInShimFile, options);
40995 if (tsResults === undefined) {
40996 return undefined;
40997 }
40998 const replacementSpan = makeReplacementSpanFromAst(this.node);
40999 let ngResults = [];
41000 for (const result of tsResults.entries) {
41001 ngResults.push(Object.assign(Object.assign({}, result), { replacementSpan }));
41002 }
41003 return Object.assign(Object.assign({}, tsResults), { entries: ngResults });
41004 }
41005 }
41006 /**
41007 * Get the details of a specific completion for a property expression.
41008 */
41009 getPropertyExpressionCompletionDetails(entryName, formatOptions, preferences) {
41010 let details = undefined;
41011 if (this.node instanceof EmptyExpr || this.node instanceof BoundEvent ||
41012 this.node.receiver instanceof ImplicitReceiver) {
41013 details =
41014 this.getGlobalPropertyExpressionCompletionDetails(entryName, formatOptions, preferences);
41015 }
41016 else {
41017 const location = this.compiler.getTemplateTypeChecker().getExpressionCompletionLocation(this.node, this.component);
41018 if (location === null) {
41019 return undefined;
41020 }
41021 details = this.tsLS.getCompletionEntryDetails(location.shimPath, location.positionInShimFile, entryName, formatOptions,
41022 /* source */ undefined, preferences);
41023 }
41024 if (details !== undefined) {
41025 details.displayParts = filterAliasImports(details.displayParts);
41026 }
41027 return details;
41028 }
41029 /**
41030 * Get the `ts.Symbol` for a specific completion for a property expression.
41031 */
41032 getPropertyExpressionCompletionSymbol(name) {
41033 if (this.node instanceof EmptyExpr || this.node instanceof LiteralPrimitive ||
41034 this.node instanceof BoundEvent || this.node.receiver instanceof ImplicitReceiver) {
41035 return this.getGlobalPropertyExpressionCompletionSymbol(name);
41036 }
41037 else {
41038 const location = this.compiler.getTemplateTypeChecker().getExpressionCompletionLocation(this.node, this.component);
41039 if (location === null) {
41040 return undefined;
41041 }
41042 return this.tsLS.getCompletionEntrySymbol(location.shimPath, location.positionInShimFile, name, /* source */ undefined);
41043 }
41044 }
41045 /**
41046 * Get completions for a property expression in a global context (e.g. `{{y|}}`).
41047 */
41048 getGlobalPropertyExpressionCompletion(options) {
41049 const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component);
41050 if (completions === null) {
41051 return undefined;
41052 }
41053 const { componentContext, templateContext } = completions;
41054 const replacementSpan = makeReplacementSpanFromAst(this.node);
41055 // Merge TS completion results with results from the template scope.
41056 let entries = [];
41057 const tsLsCompletions = this.tsLS.getCompletionsAtPosition(componentContext.shimPath, componentContext.positionInShimFile, options);
41058 if (tsLsCompletions !== undefined) {
41059 for (const tsCompletion of tsLsCompletions.entries) {
41060 // Skip completions that are shadowed by a template entity definition.
41061 if (templateContext.has(tsCompletion.name)) {
41062 continue;
41063 }
41064 entries.push(Object.assign(Object.assign({}, tsCompletion), {
41065 // Substitute the TS completion's `replacementSpan` (which uses offsets within the TCB)
41066 // with the `replacementSpan` within the template source.
41067 replacementSpan }));
41068 }
41069 }
41070 for (const [name, entity] of templateContext) {
41071 entries.push({
41072 name,
41073 sortText: name,
41074 replacementSpan,
41075 kindModifiers: ts$1.ScriptElementKindModifier.none,
41076 kind: unsafeCastDisplayInfoKindToScriptElementKind(entity.kind === CompletionKind.Reference ? DisplayInfoKind.REFERENCE :
41077 DisplayInfoKind.VARIABLE),
41078 });
41079 }
41080 return {
41081 entries,
41082 // Although this completion is "global" in the sense of an Angular expression (there is no
41083 // explicit receiver), it is not "global" in a TypeScript sense since Angular expressions have
41084 // the component as an implicit receiver.
41085 isGlobalCompletion: false,
41086 isMemberCompletion: true,
41087 isNewIdentifierLocation: false,
41088 };
41089 }
41090 /**
41091 * Get the details of a specific completion for a property expression in a global context (e.g.
41092 * `{{y|}}`).
41093 */
41094 getGlobalPropertyExpressionCompletionDetails(entryName, formatOptions, preferences) {
41095 const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component);
41096 if (completions === null) {
41097 return undefined;
41098 }
41099 const { componentContext, templateContext } = completions;
41100 if (templateContext.has(entryName)) {
41101 const entry = templateContext.get(entryName);
41102 // Entries that reference a symbol in the template context refer either to local references or
41103 // variables.
41104 const symbol = this.templateTypeChecker.getSymbolOfNode(entry.node, this.component);
41105 if (symbol === null) {
41106 return undefined;
41107 }
41108 const { kind, displayParts, documentation } = getSymbolDisplayInfo(this.tsLS, this.typeChecker, symbol);
41109 return {
41110 kind: unsafeCastDisplayInfoKindToScriptElementKind(kind),
41111 name: entryName,
41112 kindModifiers: ts$1.ScriptElementKindModifier.none,
41113 displayParts,
41114 documentation,
41115 };
41116 }
41117 else {
41118 return this.tsLS.getCompletionEntryDetails(componentContext.shimPath, componentContext.positionInShimFile, entryName, formatOptions,
41119 /* source */ undefined, preferences);
41120 }
41121 }
41122 /**
41123 * Get the `ts.Symbol` of a specific completion for a property expression in a global context
41124 * (e.g.
41125 * `{{y|}}`).
41126 */
41127 getGlobalPropertyExpressionCompletionSymbol(entryName) {
41128 const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component);
41129 if (completions === null) {
41130 return undefined;
41131 }
41132 const { componentContext, templateContext } = completions;
41133 if (templateContext.has(entryName)) {
41134 const node = templateContext.get(entryName).node;
41135 const symbol = this.templateTypeChecker.getSymbolOfNode(node, this.component);
41136 if (symbol === null || symbol.tsSymbol === null) {
41137 return undefined;
41138 }
41139 return symbol.tsSymbol;
41140 }
41141 else {
41142 return this.tsLS.getCompletionEntrySymbol(componentContext.shimPath, componentContext.positionInShimFile, entryName,
41143 /* source */ undefined);
41144 }
41145 }
41146 isElementTagCompletion() {
41147 if (this.node instanceof Text) {
41148 const positionInTextNode = this.position - this.node.sourceSpan.start.offset;
41149 // We only provide element completions in a text node when there is an open tag immediately to
41150 // the left of the position.
41151 return this.node.value.substring(0, positionInTextNode).endsWith('<');
41152 }
41153 else if (this.node instanceof Element) {
41154 return this.nodeContext === CompletionNodeContext.ElementTag;
41155 }
41156 return false;
41157 }
41158 getElementTagCompletion() {
41159 const templateTypeChecker = this.compiler.getTemplateTypeChecker();
41160 let start;
41161 let length;
41162 if (this.node instanceof Element) {
41163 // The replacementSpan is the tag name.
41164 start = this.node.sourceSpan.start.offset + 1; // account for leading '<'
41165 length = this.node.name.length;
41166 }
41167 else {
41168 const positionInTextNode = this.position - this.node.sourceSpan.start.offset;
41169 const textToLeftOfPosition = this.node.value.substring(0, positionInTextNode);
41170 start = this.node.sourceSpan.start.offset + textToLeftOfPosition.lastIndexOf('<') + 1;
41171 // We only autocomplete immediately after the < so we don't replace any existing text
41172 length = 0;
41173 }
41174 const replacementSpan = { start, length };
41175 const entries = Array.from(templateTypeChecker.getPotentialElementTags(this.component))
41176 .map(([tag, directive]) => ({
41177 kind: tagCompletionKind(directive),
41178 name: tag,
41179 sortText: tag,
41180 replacementSpan,
41181 }));
41182 return {
41183 entries,
41184 isGlobalCompletion: false,
41185 isMemberCompletion: false,
41186 isNewIdentifierLocation: false,
41187 };
41188 }
41189 getElementTagCompletionDetails(entryName) {
41190 const templateTypeChecker = this.compiler.getTemplateTypeChecker();
41191 const tagMap = templateTypeChecker.getPotentialElementTags(this.component);
41192 if (!tagMap.has(entryName)) {
41193 return undefined;
41194 }
41195 const directive = tagMap.get(entryName);
41196 let displayParts;
41197 let documentation = undefined;
41198 if (directive === null) {
41199 displayParts = [];
41200 }
41201 else {
41202 const displayInfo = getDirectiveDisplayInfo(this.tsLS, directive);
41203 displayParts = displayInfo.displayParts;
41204 documentation = displayInfo.documentation;
41205 }
41206 return {
41207 kind: tagCompletionKind(directive),
41208 name: entryName,
41209 kindModifiers: ts$1.ScriptElementKindModifier.none,
41210 displayParts,
41211 documentation,
41212 };
41213 }
41214 getElementTagCompletionSymbol(entryName) {
41215 const templateTypeChecker = this.compiler.getTemplateTypeChecker();
41216 const tagMap = templateTypeChecker.getPotentialElementTags(this.component);
41217 if (!tagMap.has(entryName)) {
41218 return undefined;
41219 }
41220 const directive = tagMap.get(entryName);
41221 return directive === null || directive === void 0 ? void 0 : directive.tsSymbol;
41222 }
41223 isElementAttributeCompletion() {
41224 return (this.nodeContext === CompletionNodeContext.ElementAttributeKey ||
41225 this.nodeContext === CompletionNodeContext.TwoWayBinding) &&
41226 (this.node instanceof Element || this.node instanceof BoundAttribute ||
41227 this.node instanceof TextAttribute || this.node instanceof BoundEvent);
41228 }
41229 getElementAttributeCompletions() {
41230 let element;
41231 if (this.node instanceof Element) {
41232 element = this.node;
41233 }
41234 else if (this.nodeParent instanceof Element || this.nodeParent instanceof Template) {
41235 element = this.nodeParent;
41236 }
41237 else {
41238 // Nothing to do without an element to process.
41239 return undefined;
41240 }
41241 let replacementSpan = undefined;
41242 if ((this.node instanceof BoundAttribute || this.node instanceof BoundEvent ||
41243 this.node instanceof TextAttribute) &&
41244 this.node.keySpan !== undefined) {
41245 replacementSpan = makeReplacementSpanFromParseSourceSpan(this.node.keySpan);
41246 }
41247 const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
41248 let entries = [];
41249 for (const completion of attrTable.values()) {
41250 // First, filter out completions that don't make sense for the current node. For example, if
41251 // the user is completing on a property binding `[foo|]`, don't offer output event
41252 // completions.
41253 switch (completion.kind) {
41254 case AttributeCompletionKind.DomAttribute:
41255 case AttributeCompletionKind.DomProperty:
41256 if (this.node instanceof BoundEvent) {
41257 continue;
41258 }
41259 break;
41260 case AttributeCompletionKind.DirectiveInput:
41261 if (this.node instanceof BoundEvent) {
41262 continue;
41263 }
41264 if (!completion.twoWayBindingSupported &&
41265 this.nodeContext === CompletionNodeContext.TwoWayBinding) {
41266 continue;
41267 }
41268 break;
41269 case AttributeCompletionKind.DirectiveOutput:
41270 if (this.node instanceof BoundAttribute) {
41271 continue;
41272 }
41273 break;
41274 case AttributeCompletionKind.DirectiveAttribute:
41275 if (this.node instanceof BoundAttribute ||
41276 this.node instanceof BoundEvent) {
41277 continue;
41278 }
41279 break;
41280 }
41281 // Is the completion in an attribute context (instead of a property context)?
41282 const isAttributeContext = (this.node instanceof Element || this.node instanceof TextAttribute);
41283 // Is the completion for an element (not an <ng-template>)?
41284 const isElementContext = this.node instanceof Element || this.nodeParent instanceof Element;
41285 addAttributeCompletionEntries(entries, completion, isAttributeContext, isElementContext, replacementSpan);
41286 }
41287 return {
41288 entries,
41289 isGlobalCompletion: false,
41290 isMemberCompletion: false,
41291 isNewIdentifierLocation: true,
41292 };
41293 }
41294 getElementAttributeCompletionDetails(entryName) {
41295 // `entryName` here may be `foo` or `[foo]`, depending on which suggested completion the user
41296 // chose. Strip off any binding syntax to get the real attribute name.
41297 const { name, kind } = stripBindingSugar(entryName);
41298 let element;
41299 if (this.node instanceof Element || this.node instanceof Template) {
41300 element = this.node;
41301 }
41302 else if (this.nodeParent instanceof Element || this.nodeParent instanceof Template) {
41303 element = this.nodeParent;
41304 }
41305 else {
41306 // Nothing to do without an element to process.
41307 return undefined;
41308 }
41309 const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
41310 if (!attrTable.has(name)) {
41311 return undefined;
41312 }
41313 const completion = attrTable.get(name);
41314 let displayParts;
41315 let documentation = undefined;
41316 let info;
41317 switch (completion.kind) {
41318 case AttributeCompletionKind.DomAttribute:
41319 case AttributeCompletionKind.DomProperty:
41320 // TODO(alxhub): ideally we would show the same documentation as quick info here. However,
41321 // since these bindings don't exist in the TCB, there is no straightforward way to retrieve
41322 // a `ts.Symbol` for the field in the TS DOM definition.
41323 displayParts = [];
41324 break;
41325 case AttributeCompletionKind.DirectiveAttribute:
41326 info = getDirectiveDisplayInfo(this.tsLS, completion.directive);
41327 displayParts = info.displayParts;
41328 documentation = info.documentation;
41329 break;
41330 case AttributeCompletionKind.DirectiveInput:
41331 case AttributeCompletionKind.DirectiveOutput:
41332 const propertySymbol = getAttributeCompletionSymbol(completion, this.typeChecker);
41333 if (propertySymbol === null) {
41334 return undefined;
41335 }
41336 info = getTsSymbolDisplayInfo(this.tsLS, this.typeChecker, propertySymbol, completion.kind === AttributeCompletionKind.DirectiveInput ? DisplayInfoKind.PROPERTY :
41337 DisplayInfoKind.EVENT, completion.directive.tsSymbol.name);
41338 if (info === null) {
41339 return undefined;
41340 }
41341 displayParts = info.displayParts;
41342 documentation = info.documentation;
41343 }
41344 return {
41345 name: entryName,
41346 kind: unsafeCastDisplayInfoKindToScriptElementKind(kind),
41347 kindModifiers: ts$1.ScriptElementKindModifier.none,
41348 displayParts: [],
41349 documentation,
41350 };
41351 }
41352 getElementAttributeCompletionSymbol(attribute) {
41353 var _a;
41354 const { name } = stripBindingSugar(attribute);
41355 let element;
41356 if (this.node instanceof Element || this.node instanceof Template) {
41357 element = this.node;
41358 }
41359 else if (this.nodeParent instanceof Element || this.nodeParent instanceof Template) {
41360 element = this.nodeParent;
41361 }
41362 else {
41363 // Nothing to do without an element to process.
41364 return undefined;
41365 }
41366 const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
41367 if (!attrTable.has(name)) {
41368 return undefined;
41369 }
41370 const completion = attrTable.get(name);
41371 return (_a = getAttributeCompletionSymbol(completion, this.typeChecker)) !== null && _a !== void 0 ? _a : undefined;
41372 }
41373 isPipeCompletion() {
41374 return this.node instanceof BindingPipe;
41375 }
41376 getPipeCompletions() {
41377 const pipes = this.templateTypeChecker.getPipesInScope(this.component);
41378 if (pipes === null) {
41379 return undefined;
41380 }
41381 const replacementSpan = makeReplacementSpanFromAst(this.node);
41382 const entries = pipes.map(pipe => ({
41383 name: pipe.name,
41384 sortText: pipe.name,
41385 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PIPE),
41386 replacementSpan,
41387 }));
41388 return {
41389 entries,
41390 isGlobalCompletion: false,
41391 isMemberCompletion: false,
41392 isNewIdentifierLocation: false,
41393 };
41394 }
41395 }
41396 function makeReplacementSpanFromParseSourceSpan(span) {
41397 return {
41398 start: span.start.offset,
41399 length: span.end.offset - span.start.offset,
41400 };
41401 }
41402 function makeReplacementSpanFromAst(node) {
41403 if ((node instanceof EmptyExpr || node instanceof LiteralPrimitive ||
41404 node instanceof BoundEvent)) {
41405 // empty nodes do not replace any existing text
41406 return undefined;
41407 }
41408 return {
41409 start: node.nameSpan.start,
41410 length: node.nameSpan.end - node.nameSpan.start,
41411 };
41412 }
41413 function tagCompletionKind(directive) {
41414 let kind;
41415 if (directive === null) {
41416 kind = DisplayInfoKind.ELEMENT;
41417 }
41418 else if (directive.isComponent) {
41419 kind = DisplayInfoKind.COMPONENT;
41420 }
41421 else {
41422 kind = DisplayInfoKind.DIRECTIVE;
41423 }
41424 return unsafeCastDisplayInfoKindToScriptElementKind(kind);
41425 }
41426 const BINDING_SUGAR = /[\[\(\)\]]/g;
41427 function stripBindingSugar(binding) {
41428 const name = binding.replace(BINDING_SUGAR, '');
41429 if (binding.startsWith('[')) {
41430 return { name, kind: DisplayInfoKind.PROPERTY };
41431 }
41432 else if (binding.startsWith('(')) {
41433 return { name, kind: DisplayInfoKind.EVENT };
41434 }
41435 else {
41436 return { name, kind: DisplayInfoKind.ATTRIBUTE };
41437 }
41438 }
41439 function nodeContextFromTarget(target) {
41440 switch (target.kind) {
41441 case TargetNodeKind.ElementInTagContext:
41442 return CompletionNodeContext.ElementTag;
41443 case TargetNodeKind.ElementInBodyContext:
41444 // Completions in element bodies are for new attributes.
41445 return CompletionNodeContext.ElementAttributeKey;
41446 case TargetNodeKind.TwoWayBindingContext:
41447 return CompletionNodeContext.TwoWayBinding;
41448 case TargetNodeKind.AttributeInKeyContext:
41449 return CompletionNodeContext.ElementAttributeKey;
41450 case TargetNodeKind.AttributeInValueContext:
41451 if (target.node instanceof BoundEvent) {
41452 return CompletionNodeContext.EventValue;
41453 }
41454 else {
41455 return CompletionNodeContext.None;
41456 }
41457 default:
41458 // No special context is available.
41459 return CompletionNodeContext.None;
41460 }
41461 }
41462
41463 /**
41464 * @license
41465 * Copyright Google LLC All Rights Reserved.
41466 *
41467 * Use of this source code is governed by an MIT-style license that can be
41468 * found in the LICENSE file at https://angular.io/license
41469 */
41470 class DefinitionBuilder {
41471 constructor(tsLS, compiler) {
41472 this.tsLS = tsLS;
41473 this.compiler = compiler;
41474 }
41475 getDefinitionAndBoundSpan(fileName, position) {
41476 var _a;
41477 const templateInfo = getTemplateInfoAtPosition(fileName, position, this.compiler);
41478 if (templateInfo === undefined) {
41479 // We were unable to get a template at the given position. If we are in a TS file, instead
41480 // attempt to get an Angular definition at the location inside a TS file (examples of this
41481 // would be templateUrl or a url in styleUrls).
41482 if (!isTypeScriptFile(fileName)) {
41483 return;
41484 }
41485 return getDefinitionForExpressionAtPosition(fileName, position, this.compiler);
41486 }
41487 const definitionMetas = this.getDefinitionMetaAtPosition(templateInfo, position);
41488 if (definitionMetas === undefined) {
41489 return undefined;
41490 }
41491 const definitions = [];
41492 for (const definitionMeta of definitionMetas) {
41493 // The `$event` of event handlers would point to the $event parameter in the shim file, as in
41494 // `_t3["x"].subscribe(function ($event): any { $event }) ;`
41495 // If we wanted to return something for this, it would be more appropriate for something like
41496 // `getTypeDefinition`.
41497 if (isDollarEvent(definitionMeta.node)) {
41498 continue;
41499 }
41500 definitions.push(...((_a = this.getDefinitionsForSymbol(Object.assign(Object.assign({}, definitionMeta), templateInfo))) !== null && _a !== void 0 ? _a : []));
41501 }
41502 if (definitions.length === 0) {
41503 return undefined;
41504 }
41505 return { definitions, textSpan: getTextSpanOfNode(definitionMetas[0].node) };
41506 }
41507 getDefinitionsForSymbol({ symbol, node, parent, component }) {
41508 switch (symbol.kind) {
41509 case SymbolKind.Directive:
41510 case SymbolKind.Element:
41511 case SymbolKind.Template:
41512 case SymbolKind.DomBinding:
41513 // Though it is generally more appropriate for the above symbol definitions to be
41514 // associated with "type definitions" since the location in the template is the
41515 // actual definition location, the better user experience would be to allow
41516 // LS users to "go to definition" on an item in the template that maps to a class and be
41517 // taken to the directive or HTML class.
41518 return this.getTypeDefinitionsForTemplateInstance(symbol, node);
41519 case SymbolKind.Pipe: {
41520 if (symbol.tsSymbol !== null) {
41521 return this.getDefinitionsForSymbols(symbol);
41522 }
41523 else {
41524 // If there is no `ts.Symbol` for the pipe transform, we want to return the
41525 // type definition (the pipe class).
41526 return this.getTypeDefinitionsForSymbols(symbol.classSymbol);
41527 }
41528 }
41529 case SymbolKind.Output:
41530 case SymbolKind.Input: {
41531 const bindingDefs = this.getDefinitionsForSymbols(...symbol.bindings);
41532 // Also attempt to get directive matches for the input name. If there is a directive that
41533 // has the input name as part of the selector, we want to return that as well.
41534 const directiveDefs = this.getDirectiveTypeDefsForBindingNode(node, parent, component);
41535 return [...bindingDefs, ...directiveDefs];
41536 }
41537 case SymbolKind.Variable:
41538 case SymbolKind.Reference: {
41539 const definitions = [];
41540 if (symbol.declaration !== node) {
41541 const shimLocation = symbol.kind === SymbolKind.Variable ? symbol.localVarLocation :
41542 symbol.referenceVarLocation;
41543 const mapping = getTemplateLocationFromShimLocation(this.compiler.getTemplateTypeChecker(), shimLocation.shimPath, shimLocation.positionInShimFile);
41544 if (mapping !== null) {
41545 definitions.push({
41546 name: symbol.declaration.name,
41547 containerName: '',
41548 containerKind: ts$1.ScriptElementKind.unknown,
41549 kind: ts$1.ScriptElementKind.variableElement,
41550 textSpan: getTextSpanOfNode(symbol.declaration),
41551 contextSpan: toTextSpan(symbol.declaration.sourceSpan),
41552 fileName: mapping.templateUrl,
41553 });
41554 }
41555 }
41556 if (symbol.kind === SymbolKind.Variable) {
41557 definitions.push(...this.getDefinitionsForSymbols({ shimLocation: symbol.initializerLocation }));
41558 }
41559 return definitions;
41560 }
41561 case SymbolKind.Expression: {
41562 return this.getDefinitionsForSymbols(symbol);
41563 }
41564 }
41565 }
41566 getDefinitionsForSymbols(...symbols) {
41567 return flatMap(symbols, ({ shimLocation }) => {
41568 var _a;
41569 const { shimPath, positionInShimFile } = shimLocation;
41570 return (_a = this.tsLS.getDefinitionAtPosition(shimPath, positionInShimFile)) !== null && _a !== void 0 ? _a : [];
41571 });
41572 }
41573 getTypeDefinitionsAtPosition(fileName, position) {
41574 const templateInfo = getTemplateInfoAtPosition(fileName, position, this.compiler);
41575 if (templateInfo === undefined) {
41576 return;
41577 }
41578 const definitionMetas = this.getDefinitionMetaAtPosition(templateInfo, position);
41579 if (definitionMetas === undefined) {
41580 return undefined;
41581 }
41582 const definitions = [];
41583 for (const { symbol, node, parent } of definitionMetas) {
41584 switch (symbol.kind) {
41585 case SymbolKind.Directive:
41586 case SymbolKind.DomBinding:
41587 case SymbolKind.Element:
41588 case SymbolKind.Template:
41589 definitions.push(...this.getTypeDefinitionsForTemplateInstance(symbol, node));
41590 break;
41591 case SymbolKind.Output:
41592 case SymbolKind.Input: {
41593 const bindingDefs = this.getTypeDefinitionsForSymbols(...symbol.bindings);
41594 definitions.push(...bindingDefs);
41595 // Also attempt to get directive matches for the input name. If there is a directive that
41596 // has the input name as part of the selector, we want to return that as well.
41597 const directiveDefs = this.getDirectiveTypeDefsForBindingNode(node, parent, templateInfo.component);
41598 definitions.push(...directiveDefs);
41599 break;
41600 }
41601 case SymbolKind.Pipe: {
41602 if (symbol.tsSymbol !== null) {
41603 definitions.push(...this.getTypeDefinitionsForSymbols(symbol));
41604 }
41605 else {
41606 // If there is no `ts.Symbol` for the pipe transform, we want to return the
41607 // type definition (the pipe class).
41608 definitions.push(...this.getTypeDefinitionsForSymbols(symbol.classSymbol));
41609 }
41610 break;
41611 }
41612 case SymbolKind.Reference:
41613 definitions.push(...this.getTypeDefinitionsForSymbols({ shimLocation: symbol.targetLocation }));
41614 break;
41615 case SymbolKind.Expression:
41616 definitions.push(...this.getTypeDefinitionsForSymbols(symbol));
41617 break;
41618 case SymbolKind.Variable: {
41619 definitions.push(...this.getTypeDefinitionsForSymbols({ shimLocation: symbol.initializerLocation }));
41620 break;
41621 }
41622 }
41623 return definitions;
41624 }
41625 }
41626 getTypeDefinitionsForTemplateInstance(symbol, node) {
41627 switch (symbol.kind) {
41628 case SymbolKind.Template: {
41629 const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives);
41630 return this.getTypeDefinitionsForSymbols(...matches);
41631 }
41632 case SymbolKind.Element: {
41633 const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives);
41634 // If one of the directive matches is a component, we should not include the native element
41635 // in the results because it is replaced by the component.
41636 return Array.from(matches).some(dir => dir.isComponent) ?
41637 this.getTypeDefinitionsForSymbols(...matches) :
41638 this.getTypeDefinitionsForSymbols(...matches, symbol);
41639 }
41640 case SymbolKind.DomBinding: {
41641 if (!(node instanceof TextAttribute)) {
41642 return [];
41643 }
41644 const dirs = getDirectiveMatchesForAttribute(node.name, symbol.host.templateNode, symbol.host.directives);
41645 return this.getTypeDefinitionsForSymbols(...dirs);
41646 }
41647 case SymbolKind.Directive:
41648 return this.getTypeDefinitionsForSymbols(symbol);
41649 }
41650 }
41651 getDirectiveTypeDefsForBindingNode(node, parent, component) {
41652 if (!(node instanceof BoundAttribute) && !(node instanceof TextAttribute) &&
41653 !(node instanceof BoundEvent)) {
41654 return [];
41655 }
41656 if (parent === null ||
41657 !(parent instanceof Template || parent instanceof Element)) {
41658 return [];
41659 }
41660 const templateOrElementSymbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(parent, component);
41661 if (templateOrElementSymbol === null ||
41662 (templateOrElementSymbol.kind !== SymbolKind.Template &&
41663 templateOrElementSymbol.kind !== SymbolKind.Element)) {
41664 return [];
41665 }
41666 const dirs = getDirectiveMatchesForAttribute(node.name, parent, templateOrElementSymbol.directives);
41667 return this.getTypeDefinitionsForSymbols(...dirs);
41668 }
41669 getTypeDefinitionsForSymbols(...symbols) {
41670 return flatMap(symbols, ({ shimLocation }) => {
41671 var _a;
41672 const { shimPath, positionInShimFile } = shimLocation;
41673 return (_a = this.tsLS.getTypeDefinitionAtPosition(shimPath, positionInShimFile)) !== null && _a !== void 0 ? _a : [];
41674 });
41675 }
41676 getDefinitionMetaAtPosition({ template, component }, position) {
41677 const target = getTargetAtPosition(template, position);
41678 if (target === null) {
41679 return undefined;
41680 }
41681 const { context, parent } = target;
41682 const nodes = context.kind === TargetNodeKind.TwoWayBindingContext ? context.nodes : [context.node];
41683 const definitionMetas = [];
41684 for (const node of nodes) {
41685 const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(node, component);
41686 if (symbol === null) {
41687 continue;
41688 }
41689 definitionMetas.push({ node, parent, symbol });
41690 }
41691 return definitionMetas.length > 0 ? definitionMetas : undefined;
41692 }
41693 }
41694 /**
41695 * Gets an Angular-specific definition in a TypeScript source file.
41696 */
41697 function getDefinitionForExpressionAtPosition(fileName, position, compiler) {
41698 const sf = compiler.getNextProgram().getSourceFile(fileName);
41699 if (sf === undefined) {
41700 return;
41701 }
41702 const expression = findTightestNode(sf, position);
41703 if (expression === undefined) {
41704 return;
41705 }
41706 const classDeclaration = getParentClassDeclaration(expression);
41707 if (classDeclaration === undefined) {
41708 return;
41709 }
41710 const componentResources = compiler.getComponentResources(classDeclaration);
41711 if (componentResources === null) {
41712 return;
41713 }
41714 const allResources = [...componentResources.styles, componentResources.template];
41715 const resourceForExpression = allResources.find(resource => resource.expression === expression);
41716 if (resourceForExpression === undefined || !isExternalResource(resourceForExpression)) {
41717 return;
41718 }
41719 const templateDefinitions = [{
41720 kind: ts$1.ScriptElementKind.externalModuleName,
41721 name: resourceForExpression.path,
41722 containerKind: ts$1.ScriptElementKind.unknown,
41723 containerName: '',
41724 // Reading the template is expensive, so don't provide a preview.
41725 // TODO(ayazhafiz): Consider providing an actual span:
41726 // 1. We're likely to read the template anyway
41727 // 2. We could show just the first 100 chars or so
41728 textSpan: { start: 0, length: 0 },
41729 fileName: resourceForExpression.path,
41730 }];
41731 return {
41732 definitions: templateDefinitions,
41733 textSpan: {
41734 // Exclude opening and closing quotes in the url span.
41735 start: expression.getStart() + 1,
41736 length: expression.getWidth() - 2,
41737 },
41738 };
41739 }
41740
41741 /**
41742 * @license
41743 * Copyright Google LLC All Rights Reserved.
41744 *
41745 * Use of this source code is governed by an MIT-style license that can be
41746 * found in the LICENSE file at https://angular.io/license
41747 */
41748 class QuickInfoBuilder {
41749 constructor(tsLS, compiler, component, node) {
41750 this.tsLS = tsLS;
41751 this.compiler = compiler;
41752 this.component = component;
41753 this.node = node;
41754 this.typeChecker = this.compiler.getNextProgram().getTypeChecker();
41755 }
41756 get() {
41757 const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(this.node, this.component);
41758 if (symbol === null) {
41759 return isDollarAny(this.node) ? createDollarAnyQuickInfo(this.node) : undefined;
41760 }
41761 return this.getQuickInfoForSymbol(symbol);
41762 }
41763 getQuickInfoForSymbol(symbol) {
41764 switch (symbol.kind) {
41765 case SymbolKind.Input:
41766 case SymbolKind.Output:
41767 return this.getQuickInfoForBindingSymbol(symbol);
41768 case SymbolKind.Template:
41769 return createNgTemplateQuickInfo(this.node);
41770 case SymbolKind.Element:
41771 return this.getQuickInfoForElementSymbol(symbol);
41772 case SymbolKind.Variable:
41773 return this.getQuickInfoForVariableSymbol(symbol);
41774 case SymbolKind.Reference:
41775 return this.getQuickInfoForReferenceSymbol(symbol);
41776 case SymbolKind.DomBinding:
41777 return this.getQuickInfoForDomBinding(symbol);
41778 case SymbolKind.Directive:
41779 return this.getQuickInfoAtShimLocation(symbol.shimLocation);
41780 case SymbolKind.Pipe:
41781 return this.getQuickInfoForPipeSymbol(symbol);
41782 case SymbolKind.Expression:
41783 return this.getQuickInfoAtShimLocation(symbol.shimLocation);
41784 }
41785 }
41786 getQuickInfoForBindingSymbol(symbol) {
41787 if (symbol.bindings.length === 0) {
41788 return undefined;
41789 }
41790 const kind = symbol.kind === SymbolKind.Input ? DisplayInfoKind.PROPERTY : DisplayInfoKind.EVENT;
41791 const quickInfo = this.getQuickInfoAtShimLocation(symbol.bindings[0].shimLocation);
41792 return quickInfo === undefined ? undefined : updateQuickInfoKind(quickInfo, kind);
41793 }
41794 getQuickInfoForElementSymbol(symbol) {
41795 const { templateNode } = symbol;
41796 const matches = getDirectiveMatchesForElementTag(templateNode, symbol.directives);
41797 if (matches.size > 0) {
41798 return this.getQuickInfoForDirectiveSymbol(matches.values().next().value, templateNode);
41799 }
41800 return createQuickInfo(templateNode.name, DisplayInfoKind.ELEMENT, getTextSpanOfNode(templateNode), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType));
41801 }
41802 getQuickInfoForVariableSymbol(symbol) {
41803 const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.initializerLocation);
41804 return createQuickInfo(symbol.declaration.name, DisplayInfoKind.VARIABLE, getTextSpanOfNode(this.node), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType), documentation);
41805 }
41806 getQuickInfoForReferenceSymbol(symbol) {
41807 const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.targetLocation);
41808 return createQuickInfo(symbol.declaration.name, DisplayInfoKind.REFERENCE, getTextSpanOfNode(this.node), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType), documentation);
41809 }
41810 getQuickInfoForPipeSymbol(symbol) {
41811 if (symbol.tsSymbol !== null) {
41812 const quickInfo = this.getQuickInfoAtShimLocation(symbol.shimLocation);
41813 return quickInfo === undefined ? undefined :
41814 updateQuickInfoKind(quickInfo, DisplayInfoKind.PIPE);
41815 }
41816 else {
41817 return createQuickInfo(this.typeChecker.typeToString(symbol.classSymbol.tsType), DisplayInfoKind.PIPE, getTextSpanOfNode(this.node));
41818 }
41819 }
41820 getQuickInfoForDomBinding(symbol) {
41821 if (!(this.node instanceof TextAttribute) &&
41822 !(this.node instanceof BoundAttribute)) {
41823 return undefined;
41824 }
41825 const directives = getDirectiveMatchesForAttribute(this.node.name, symbol.host.templateNode, symbol.host.directives);
41826 if (directives.size === 0) {
41827 return undefined;
41828 }
41829 return this.getQuickInfoForDirectiveSymbol(directives.values().next().value);
41830 }
41831 getQuickInfoForDirectiveSymbol(dir, node = this.node) {
41832 const kind = dir.isComponent ? DisplayInfoKind.COMPONENT : DisplayInfoKind.DIRECTIVE;
41833 const documentation = this.getDocumentationFromTypeDefAtLocation(dir.shimLocation);
41834 let containerName;
41835 if (ts$1.isClassDeclaration(dir.tsSymbol.valueDeclaration) && dir.ngModule !== null) {
41836 containerName = dir.ngModule.name.getText();
41837 }
41838 return createQuickInfo(this.typeChecker.typeToString(dir.tsType), kind, getTextSpanOfNode(this.node), containerName, undefined, documentation);
41839 }
41840 getDocumentationFromTypeDefAtLocation(shimLocation) {
41841 var _a;
41842 const typeDefs = this.tsLS.getTypeDefinitionAtPosition(shimLocation.shimPath, shimLocation.positionInShimFile);
41843 if (typeDefs === undefined || typeDefs.length === 0) {
41844 return undefined;
41845 }
41846 return (_a = this.tsLS.getQuickInfoAtPosition(typeDefs[0].fileName, typeDefs[0].textSpan.start)) === null || _a === void 0 ? void 0 : _a.documentation;
41847 }
41848 getQuickInfoAtShimLocation(location) {
41849 const quickInfo = this.tsLS.getQuickInfoAtPosition(location.shimPath, location.positionInShimFile);
41850 if (quickInfo === undefined || quickInfo.displayParts === undefined) {
41851 return quickInfo;
41852 }
41853 quickInfo.displayParts = filterAliasImports(quickInfo.displayParts);
41854 const textSpan = getTextSpanOfNode(this.node);
41855 return Object.assign(Object.assign({}, quickInfo), { textSpan });
41856 }
41857 }
41858 function updateQuickInfoKind(quickInfo, kind) {
41859 if (quickInfo.displayParts === undefined) {
41860 return quickInfo;
41861 }
41862 const startsWithKind = quickInfo.displayParts.length >= 3 &&
41863 displayPartsEqual(quickInfo.displayParts[0], { text: '(', kind: SYMBOL_PUNC }) &&
41864 quickInfo.displayParts[1].kind === SYMBOL_TEXT &&
41865 displayPartsEqual(quickInfo.displayParts[2], { text: ')', kind: SYMBOL_PUNC });
41866 if (startsWithKind) {
41867 quickInfo.displayParts[1].text = kind;
41868 }
41869 else {
41870 quickInfo.displayParts = [
41871 { text: '(', kind: SYMBOL_PUNC },
41872 { text: kind, kind: SYMBOL_TEXT },
41873 { text: ')', kind: SYMBOL_PUNC },
41874 { text: ' ', kind: SYMBOL_SPACE },
41875 ...quickInfo.displayParts,
41876 ];
41877 }
41878 return quickInfo;
41879 }
41880 function displayPartsEqual(a, b) {
41881 return a.text === b.text && a.kind === b.kind;
41882 }
41883 function isDollarAny(node) {
41884 return node instanceof MethodCall && node.receiver instanceof ImplicitReceiver &&
41885 !(node.receiver instanceof ThisReceiver) && node.name === '$any' && node.args.length === 1;
41886 }
41887 function createDollarAnyQuickInfo(node) {
41888 return createQuickInfo('$any', DisplayInfoKind.METHOD, getTextSpanOfNode(node),
41889 /** containerName */ undefined, 'any', [{
41890 kind: SYMBOL_TEXT,
41891 text: 'function to cast an expression to the `any` type',
41892 }]);
41893 }
41894 // TODO(atscott): Create special `ts.QuickInfo` for `ng-template` and `ng-container` as well.
41895 function createNgTemplateQuickInfo(node) {
41896 return createQuickInfo('ng-template', DisplayInfoKind.TEMPLATE, getTextSpanOfNode(node),
41897 /** containerName */ undefined,
41898 /** type */ undefined, [{
41899 kind: SYMBOL_TEXT,
41900 text: 'The `<ng-template>` is an Angular element for rendering HTML. It is never displayed directly.',
41901 }]);
41902 }
41903 /**
41904 * Construct a QuickInfo object taking into account its container and type.
41905 * @param name Name of the QuickInfo target
41906 * @param kind component, directive, pipe, etc.
41907 * @param textSpan span of the target
41908 * @param containerName either the Symbol's container or the NgModule that contains the directive
41909 * @param type user-friendly name of the type
41910 * @param documentation docstring or comment
41911 */
41912 function createQuickInfo(name, kind, textSpan, containerName, type, documentation) {
41913 const displayParts = createDisplayParts(name, kind, containerName, type);
41914 return {
41915 kind: unsafeCastDisplayInfoKindToScriptElementKind(kind),
41916 kindModifiers: ts$1.ScriptElementKindModifier.none,
41917 textSpan: textSpan,
41918 displayParts,
41919 documentation,
41920 };
41921 }
41922
41923 /**
41924 * @license
41925 * Copyright Google LLC All Rights Reserved.
41926 *
41927 * Use of this source code is governed by an MIT-style license that can be
41928 * found in the LICENSE file at https://angular.io/license
41929 */
41930 function toFilePosition(shimLocation) {
41931 return { fileName: shimLocation.shimPath, position: shimLocation.positionInShimFile };
41932 }
41933 var RequestKind;
41934 (function (RequestKind) {
41935 RequestKind[RequestKind["Template"] = 0] = "Template";
41936 RequestKind[RequestKind["TypeScript"] = 1] = "TypeScript";
41937 })(RequestKind || (RequestKind = {}));
41938 class ReferencesAndRenameBuilder {
41939 constructor(strategy, tsLS, compiler) {
41940 this.strategy = strategy;
41941 this.tsLS = tsLS;
41942 this.compiler = compiler;
41943 this.ttc = this.compiler.getTemplateTypeChecker();
41944 }
41945 getRenameInfo(filePath, position) {
41946 const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
41947 // We could not get a template at position so we assume the request came from outside the
41948 // template.
41949 if (templateInfo === undefined) {
41950 return this.tsLS.getRenameInfo(filePath, position);
41951 }
41952 const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
41953 if (allTargetDetails === null) {
41954 return { canRename: false, localizedErrorMessage: 'Could not find template node at position.' };
41955 }
41956 const { templateTarget } = allTargetDetails[0];
41957 const templateTextAndSpan = getRenameTextAndSpanAtPosition(templateTarget, position);
41958 if (templateTextAndSpan === null) {
41959 return { canRename: false, localizedErrorMessage: 'Could not determine template node text.' };
41960 }
41961 const { text, span } = templateTextAndSpan;
41962 return {
41963 canRename: true,
41964 displayName: text,
41965 fullDisplayName: text,
41966 triggerSpan: span,
41967 };
41968 }
41969 findRenameLocations(filePath, position) {
41970 this.ttc.generateAllTypeCheckBlocks();
41971 const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
41972 // We could not get a template at position so we assume the request came from outside the
41973 // template.
41974 if (templateInfo === undefined) {
41975 const requestNode = this.getTsNodeAtPosition(filePath, position);
41976 if (requestNode === null) {
41977 return undefined;
41978 }
41979 const requestOrigin = { kind: RequestKind.TypeScript, requestNode };
41980 return this.findRenameLocationsAtTypescriptPosition(filePath, position, requestOrigin);
41981 }
41982 return this.findRenameLocationsAtTemplatePosition(templateInfo, position);
41983 }
41984 findRenameLocationsAtTemplatePosition(templateInfo, position) {
41985 const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
41986 if (allTargetDetails === null) {
41987 return undefined;
41988 }
41989 const allRenameLocations = [];
41990 for (const targetDetails of allTargetDetails) {
41991 const requestOrigin = {
41992 kind: RequestKind.Template,
41993 requestNode: targetDetails.templateTarget,
41994 position,
41995 };
41996 for (const location of targetDetails.typescriptLocations) {
41997 const locations = this.findRenameLocationsAtTypescriptPosition(location.fileName, location.position, requestOrigin);
41998 // If we couldn't find rename locations for _any_ result, we should not allow renaming to
41999 // proceed instead of having a partially complete rename.
42000 if (locations === undefined) {
42001 return undefined;
42002 }
42003 allRenameLocations.push(...locations);
42004 }
42005 }
42006 return allRenameLocations.length > 0 ? allRenameLocations : undefined;
42007 }
42008 getTsNodeAtPosition(filePath, position) {
42009 var _a;
42010 const sf = this.strategy.getProgram().getSourceFile(filePath);
42011 if (!sf) {
42012 return null;
42013 }
42014 return (_a = findTightestNode(sf, position)) !== null && _a !== void 0 ? _a : null;
42015 }
42016 findRenameLocationsAtTypescriptPosition(filePath, position, requestOrigin) {
42017 let originalNodeText;
42018 if (requestOrigin.kind === RequestKind.TypeScript) {
42019 originalNodeText = requestOrigin.requestNode.getText();
42020 }
42021 else {
42022 const templateNodeText = getRenameTextAndSpanAtPosition(requestOrigin.requestNode, requestOrigin.position);
42023 if (templateNodeText === null) {
42024 return undefined;
42025 }
42026 originalNodeText = templateNodeText.text;
42027 }
42028 const locations = this.tsLS.findRenameLocations(filePath, position, /*findInStrings*/ false, /*findInComments*/ false);
42029 if (locations === undefined) {
42030 return undefined;
42031 }
42032 const entries = new Map();
42033 for (const location of locations) {
42034 // TODO(atscott): Determine if a file is a shim file in a more robust way and make the API
42035 // available in an appropriate location.
42036 if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(location.fileName))) {
42037 const entry = this.convertToTemplateDocumentSpan(location, this.ttc, originalNodeText);
42038 // There is no template node whose text matches the original rename request. Bail on
42039 // renaming completely rather than providing incomplete results.
42040 if (entry === null) {
42041 return undefined;
42042 }
42043 entries.set(createLocationKey(entry), entry);
42044 }
42045 else {
42046 // Ensure we only allow renaming a TS result with matching text
42047 const refNode = this.getTsNodeAtPosition(location.fileName, location.textSpan.start);
42048 if (refNode === null || refNode.getText() !== originalNodeText) {
42049 return undefined;
42050 }
42051 entries.set(createLocationKey(location), location);
42052 }
42053 }
42054 return Array.from(entries.values());
42055 }
42056 getReferencesAtPosition(filePath, position) {
42057 this.ttc.generateAllTypeCheckBlocks();
42058 const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
42059 if (templateInfo === undefined) {
42060 return this.getReferencesAtTypescriptPosition(filePath, position);
42061 }
42062 return this.getReferencesAtTemplatePosition(templateInfo, position);
42063 }
42064 getReferencesAtTemplatePosition(templateInfo, position) {
42065 const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
42066 if (allTargetDetails === null) {
42067 return undefined;
42068 }
42069 const allReferences = [];
42070 for (const targetDetails of allTargetDetails) {
42071 for (const location of targetDetails.typescriptLocations) {
42072 const refs = this.getReferencesAtTypescriptPosition(location.fileName, location.position);
42073 if (refs !== undefined) {
42074 allReferences.push(...refs);
42075 }
42076 }
42077 }
42078 return allReferences.length > 0 ? allReferences : undefined;
42079 }
42080 getTargetDetailsAtTemplatePosition({ template, component }, position) {
42081 // Find the AST node in the template at the position.
42082 const positionDetails = getTargetAtPosition(template, position);
42083 if (positionDetails === null) {
42084 return null;
42085 }
42086 const nodes = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
42087 positionDetails.context.nodes :
42088 [positionDetails.context.node];
42089 const details = [];
42090 for (const node of nodes) {
42091 // Get the information about the TCB at the template position.
42092 const symbol = this.ttc.getSymbolOfNode(node, component);
42093 if (symbol === null) {
42094 continue;
42095 }
42096 const templateTarget = node;
42097 switch (symbol.kind) {
42098 case SymbolKind.Directive:
42099 case SymbolKind.Template:
42100 // References to elements, templates, and directives will be through template references
42101 // (#ref). They shouldn't be used directly for a Language Service reference request.
42102 break;
42103 case SymbolKind.Element: {
42104 const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives);
42105 details.push({ typescriptLocations: this.getPositionsForDirectives(matches), templateTarget });
42106 break;
42107 }
42108 case SymbolKind.DomBinding: {
42109 // Dom bindings aren't currently type-checked (see `checkTypeOfDomBindings`) so they don't
42110 // have a shim location. This means we can't match dom bindings to their lib.dom
42111 // reference, but we can still see if they match to a directive.
42112 if (!(node instanceof TextAttribute) && !(node instanceof BoundAttribute)) {
42113 return null;
42114 }
42115 const directives = getDirectiveMatchesForAttribute(node.name, symbol.host.templateNode, symbol.host.directives);
42116 details.push({
42117 typescriptLocations: this.getPositionsForDirectives(directives),
42118 templateTarget,
42119 });
42120 break;
42121 }
42122 case SymbolKind.Reference: {
42123 details.push({
42124 typescriptLocations: [toFilePosition(symbol.referenceVarLocation)],
42125 templateTarget,
42126 });
42127 break;
42128 }
42129 case SymbolKind.Variable: {
42130 if ((templateTarget instanceof Variable)) {
42131 if (templateTarget.valueSpan !== undefined &&
42132 isWithin(position, templateTarget.valueSpan)) {
42133 // In the valueSpan of the variable, we want to get the reference of the initializer.
42134 details.push({
42135 typescriptLocations: [toFilePosition(symbol.initializerLocation)],
42136 templateTarget,
42137 });
42138 }
42139 else if (isWithin(position, templateTarget.keySpan)) {
42140 // In the keySpan of the variable, we want to get the reference of the local variable.
42141 details.push({
42142 typescriptLocations: [toFilePosition(symbol.localVarLocation)],
42143 templateTarget,
42144 });
42145 }
42146 }
42147 else {
42148 // If the templateNode is not the `TmplAstVariable`, it must be a usage of the
42149 // variable somewhere in the template.
42150 details.push({
42151 typescriptLocations: [toFilePosition(symbol.localVarLocation)],
42152 templateTarget,
42153 });
42154 }
42155 break;
42156 }
42157 case SymbolKind.Input:
42158 case SymbolKind.Output: {
42159 details.push({
42160 typescriptLocations: symbol.bindings.map(binding => toFilePosition(binding.shimLocation)),
42161 templateTarget,
42162 });
42163 break;
42164 }
42165 case SymbolKind.Pipe:
42166 case SymbolKind.Expression: {
42167 details.push({ typescriptLocations: [toFilePosition(symbol.shimLocation)], templateTarget });
42168 break;
42169 }
42170 }
42171 }
42172 return details.length > 0 ? details : null;
42173 }
42174 getPositionsForDirectives(directives) {
42175 const allDirectives = [];
42176 for (const dir of directives.values()) {
42177 const dirClass = dir.tsSymbol.valueDeclaration;
42178 if (dirClass === undefined || !ts$1.isClassDeclaration(dirClass) ||
42179 dirClass.name === undefined) {
42180 continue;
42181 }
42182 const { fileName } = dirClass.getSourceFile();
42183 const position = dirClass.name.getStart();
42184 allDirectives.push({ fileName, position });
42185 }
42186 return allDirectives;
42187 }
42188 getReferencesAtTypescriptPosition(fileName, position) {
42189 const refs = this.tsLS.getReferencesAtPosition(fileName, position);
42190 if (refs === undefined) {
42191 return undefined;
42192 }
42193 const entries = new Map();
42194 for (const ref of refs) {
42195 if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(ref.fileName))) {
42196 const entry = this.convertToTemplateDocumentSpan(ref, this.ttc);
42197 if (entry !== null) {
42198 entries.set(createLocationKey(entry), entry);
42199 }
42200 }
42201 }
42202 return Array.from(entries.values());
42203 }
42204 convertToTemplateDocumentSpan(shimDocumentSpan, templateTypeChecker, requiredNodeText) {
42205 const sf = this.strategy.getProgram().getSourceFile(shimDocumentSpan.fileName);
42206 if (sf === undefined) {
42207 return null;
42208 }
42209 const tcbNode = findTightestNode(sf, shimDocumentSpan.textSpan.start);
42210 if (tcbNode === undefined ||
42211 hasExpressionIdentifier(sf, tcbNode, ExpressionIdentifier.EVENT_PARAMETER)) {
42212 // If the reference result is the $event parameter in the subscribe/addEventListener
42213 // function in the TCB, we want to filter this result out of the references. We really only
42214 // want to return references to the parameter in the template itself.
42215 return null;
42216 }
42217 // TODO(atscott): Determine how to consistently resolve paths. i.e. with the project
42218 // serverHost or LSParseConfigHost in the adapter. We should have a better defined way to
42219 // normalize paths.
42220 const mapping = getTemplateLocationFromShimLocation(templateTypeChecker, absoluteFrom(shimDocumentSpan.fileName), shimDocumentSpan.textSpan.start);
42221 if (mapping === null) {
42222 return null;
42223 }
42224 const { span, templateUrl } = mapping;
42225 if (requiredNodeText !== undefined && span.toString() !== requiredNodeText) {
42226 return null;
42227 }
42228 return Object.assign(Object.assign({}, shimDocumentSpan), { fileName: templateUrl, textSpan: toTextSpan(span),
42229 // Specifically clear other text span values because we do not have enough knowledge to
42230 // convert these to spans in the template.
42231 contextSpan: undefined, originalContextSpan: undefined, originalTextSpan: undefined });
42232 }
42233 }
42234 function getRenameTextAndSpanAtPosition(node, position) {
42235 if (node instanceof BoundAttribute || node instanceof TextAttribute ||
42236 node instanceof BoundEvent) {
42237 if (node.keySpan === undefined) {
42238 return null;
42239 }
42240 return { text: node.name, span: toTextSpan(node.keySpan) };
42241 }
42242 else if (node instanceof Variable || node instanceof Reference) {
42243 if (isWithin(position, node.keySpan)) {
42244 return { text: node.keySpan.toString(), span: toTextSpan(node.keySpan) };
42245 }
42246 else if (node.valueSpan && isWithin(position, node.valueSpan)) {
42247 return { text: node.valueSpan.toString(), span: toTextSpan(node.valueSpan) };
42248 }
42249 }
42250 if (node instanceof BindingPipe) {
42251 // TODO(atscott): Add support for renaming pipes
42252 return null;
42253 }
42254 if (node instanceof PropertyRead || node instanceof MethodCall || node instanceof PropertyWrite ||
42255 node instanceof SafePropertyRead || node instanceof SafeMethodCall) {
42256 return { text: node.name, span: toTextSpan(node.nameSpan) };
42257 }
42258 else if (node instanceof LiteralPrimitive) {
42259 const span = toTextSpan(node.sourceSpan);
42260 const text = node.value;
42261 if (typeof text === 'string') {
42262 // The span of a string literal includes the quotes but they should be removed for renaming.
42263 span.start += 1;
42264 span.length -= 2;
42265 }
42266 return { text, span };
42267 }
42268 return null;
42269 }
42270 /**
42271 * Creates a "key" for a rename/reference location by concatenating file name, span start, and span
42272 * length. This allows us to de-duplicate template results when an item may appear several times
42273 * in the TCB but map back to the same template location.
42274 */
42275 function createLocationKey(ds) {
42276 return ds.fileName + ds.textSpan.start + ds.textSpan.length;
42277 }
42278
42279 /**
42280 * @license
42281 * Copyright Google LLC All Rights Reserved.
42282 *
42283 * Use of this source code is governed by an MIT-style license that can be
42284 * found in the LICENSE file at https://angular.io/license
42285 */
42286 class LanguageService {
42287 constructor(project, tsLS, config) {
42288 this.project = project;
42289 this.tsLS = tsLS;
42290 this.config = config;
42291 this.parseConfigHost = new LSParseConfigHost(project.projectService.host);
42292 this.options = parseNgCompilerOptions(project, this.parseConfigHost, config);
42293 logCompilerOptions(project, this.options);
42294 this.strategy = createTypeCheckingProgramStrategy(project);
42295 this.adapter = new LanguageServiceAdapter(project);
42296 this.compilerFactory = new CompilerFactory(this.adapter, this.strategy, this.options);
42297 this.watchConfigFile(project);
42298 }
42299 getCompilerOptions() {
42300 return this.options;
42301 }
42302 getSemanticDiagnostics(fileName) {
42303 const compiler = this.compilerFactory.getOrCreate();
42304 const ttc = compiler.getTemplateTypeChecker();
42305 const diagnostics = [];
42306 if (isTypeScriptFile(fileName)) {
42307 const program = compiler.getNextProgram();
42308 const sourceFile = program.getSourceFile(fileName);
42309 if (sourceFile) {
42310 const ngDiagnostics = compiler.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile);
42311 // There are several kinds of diagnostics returned by `NgCompiler` for a source file:
42312 //
42313 // 1. Angular-related non-template diagnostics from decorated classes within that file.
42314 // 2. Template diagnostics for components with direct inline templates (a string literal).
42315 // 3. Template diagnostics for components with indirect inline templates (templates computed
42316 // by expression).
42317 // 4. Template diagnostics for components with external templates.
42318 //
42319 // When showing diagnostics for a TS source file, we want to only include kinds 1 and 2 -
42320 // those diagnostics which are reported at a location within the TS file itself. Diagnostics
42321 // for external templates will be shown when editing that template file (the `else` block)
42322 // below.
42323 //
42324 // Currently, indirect inline template diagnostics (kind 3) are not shown at all by the
42325 // Language Service, because there is no sensible location in the user's code for them. Such
42326 // templates are an edge case, though, and should not be common.
42327 //
42328 // TODO(alxhub): figure out a good user experience for indirect template diagnostics and
42329 // show them from within the Language Service.
42330 diagnostics.push(...ngDiagnostics.filter(diag => diag.file !== undefined && diag.file.fileName === sourceFile.fileName));
42331 }
42332 }
42333 else {
42334 const components = compiler.getComponentsWithTemplateFile(fileName);
42335 for (const component of components) {
42336 if (ts.isClassDeclaration(component)) {
42337 diagnostics.push(...ttc.getDiagnosticsForComponent(component));
42338 }
42339 }
42340 }
42341 this.compilerFactory.registerLastKnownProgram();
42342 return diagnostics;
42343 }
42344 getDefinitionAndBoundSpan(fileName, position) {
42345 return this.withCompiler((compiler) => {
42346 if (!isInAngularContext(compiler.getNextProgram(), fileName, position)) {
42347 return undefined;
42348 }
42349 return new DefinitionBuilder(this.tsLS, compiler)
42350 .getDefinitionAndBoundSpan(fileName, position);
42351 });
42352 }
42353 getTypeDefinitionAtPosition(fileName, position) {
42354 return this.withCompiler((compiler) => {
42355 if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
42356 return undefined;
42357 }
42358 return new DefinitionBuilder(this.tsLS, compiler)
42359 .getTypeDefinitionsAtPosition(fileName, position);
42360 });
42361 }
42362 getQuickInfoAtPosition(fileName, position) {
42363 return this.withCompiler((compiler) => {
42364 if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
42365 return undefined;
42366 }
42367 const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
42368 if (templateInfo === undefined) {
42369 return undefined;
42370 }
42371 const positionDetails = getTargetAtPosition(templateInfo.template, position);
42372 if (positionDetails === null) {
42373 return undefined;
42374 }
42375 // Because we can only show 1 quick info, just use the bound attribute if the target is a two
42376 // way binding. We may consider concatenating additional display parts from the other target
42377 // nodes or representing the two way binding in some other manner in the future.
42378 const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
42379 positionDetails.context.nodes[0] :
42380 positionDetails.context.node;
42381 return new QuickInfoBuilder(this.tsLS, compiler, templateInfo.component, node).get();
42382 });
42383 }
42384 getReferencesAtPosition(fileName, position) {
42385 const compiler = this.compilerFactory.getOrCreate();
42386 const results = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
42387 .getReferencesAtPosition(fileName, position);
42388 this.compilerFactory.registerLastKnownProgram();
42389 return results;
42390 }
42391 getRenameInfo(fileName, position) {
42392 var _a, _b, _c;
42393 const compiler = this.compilerFactory.getOrCreate();
42394 const renameInfo = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
42395 .getRenameInfo(absoluteFrom(fileName), position);
42396 if (!renameInfo.canRename) {
42397 return renameInfo;
42398 }
42399 const quickInfo = (_a = this.getQuickInfoAtPosition(fileName, position)) !== null && _a !== void 0 ? _a : this.tsLS.getQuickInfoAtPosition(fileName, position);
42400 const kind = (_b = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kind) !== null && _b !== void 0 ? _b : ts.ScriptElementKind.unknown;
42401 const kindModifiers = (_c = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kindModifiers) !== null && _c !== void 0 ? _c : ts.ScriptElementKind.unknown;
42402 return Object.assign(Object.assign({}, renameInfo), { kind, kindModifiers });
42403 }
42404 findRenameLocations(fileName, position) {
42405 const compiler = this.compilerFactory.getOrCreate();
42406 const results = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
42407 .findRenameLocations(fileName, position);
42408 this.compilerFactory.registerLastKnownProgram();
42409 return results;
42410 }
42411 getCompletionBuilder(fileName, position) {
42412 const compiler = this.compilerFactory.getOrCreate();
42413 const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
42414 if (templateInfo === undefined) {
42415 return null;
42416 }
42417 const positionDetails = getTargetAtPosition(templateInfo.template, position);
42418 if (positionDetails === null) {
42419 return null;
42420 }
42421 // For two-way bindings, we actually only need to be concerned with the bound attribute because
42422 // the bindings in the template are written with the attribute name, not the event name.
42423 const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
42424 positionDetails.context.nodes[0] :
42425 positionDetails.context.node;
42426 return new CompletionBuilder(this.tsLS, compiler, templateInfo.component, node, positionDetails);
42427 }
42428 getCompletionsAtPosition(fileName, position, options) {
42429 return this.withCompiler((compiler) => {
42430 if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
42431 return undefined;
42432 }
42433 const builder = this.getCompletionBuilder(fileName, position);
42434 if (builder === null) {
42435 return undefined;
42436 }
42437 return builder.getCompletionsAtPosition(options);
42438 });
42439 }
42440 getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences) {
42441 return this.withCompiler((compiler) => {
42442 if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
42443 return undefined;
42444 }
42445 const builder = this.getCompletionBuilder(fileName, position);
42446 if (builder === null) {
42447 return undefined;
42448 }
42449 return builder.getCompletionEntryDetails(entryName, formatOptions, preferences);
42450 });
42451 }
42452 getCompletionEntrySymbol(fileName, position, entryName) {
42453 return this.withCompiler((compiler) => {
42454 if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
42455 return undefined;
42456 }
42457 const builder = this.getCompletionBuilder(fileName, position);
42458 if (builder === null) {
42459 return undefined;
42460 }
42461 const result = builder.getCompletionEntrySymbol(entryName);
42462 this.compilerFactory.registerLastKnownProgram();
42463 return result;
42464 });
42465 }
42466 getTcb(fileName, position) {
42467 return this.withCompiler(compiler => {
42468 const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
42469 if (templateInfo === undefined) {
42470 return undefined;
42471 }
42472 const tcb = compiler.getTemplateTypeChecker().getTypeCheckBlock(templateInfo.component);
42473 if (tcb === null) {
42474 return undefined;
42475 }
42476 const sf = tcb.getSourceFile();
42477 let selections = [];
42478 const target = getTargetAtPosition(templateInfo.template, position);
42479 if (target !== null) {
42480 let selectionSpans;
42481 if ('nodes' in target.context) {
42482 selectionSpans = target.context.nodes.map(n => n.sourceSpan);
42483 }
42484 else {
42485 selectionSpans = [target.context.node.sourceSpan];
42486 }
42487 const selectionNodes = selectionSpans
42488 .map(s => findFirstMatchingNode(tcb, {
42489 withSpan: s,
42490 filter: (node) => true,
42491 }))
42492 .filter((n) => n !== null);
42493 selections = selectionNodes.map(n => {
42494 return {
42495 start: n.getStart(sf),
42496 length: n.getEnd() - n.getStart(sf),
42497 };
42498 });
42499 }
42500 return {
42501 fileName: sf.fileName,
42502 content: sf.getFullText(),
42503 selections,
42504 };
42505 });
42506 }
42507 withCompiler(p) {
42508 const compiler = this.compilerFactory.getOrCreate();
42509 const result = p(compiler);
42510 this.compilerFactory.registerLastKnownProgram();
42511 return result;
42512 }
42513 getCompilerOptionsDiagnostics() {
42514 const project = this.project;
42515 if (!(project instanceof ts.server.ConfiguredProject)) {
42516 return [];
42517 }
42518 const diagnostics = [];
42519 const configSourceFile = ts.readJsonConfigFile(project.getConfigFilePath(), (path) => project.readFile(path));
42520 if (!this.options.strictTemplates && !this.options.fullTemplateTypeCheck) {
42521 diagnostics.push({
42522 messageText: 'Some language features are not available. ' +
42523 'To access all features, enable `strictTemplates` in `angularCompilerOptions`.',
42524 category: ts.DiagnosticCategory.Suggestion,
42525 code: ngErrorCode(ErrorCode.SUGGEST_STRICT_TEMPLATES),
42526 file: configSourceFile,
42527 start: undefined,
42528 length: undefined,
42529 });
42530 }
42531 const compiler = this.compilerFactory.getOrCreate();
42532 diagnostics.push(...compiler.getOptionDiagnostics());
42533 return diagnostics;
42534 }
42535 watchConfigFile(project) {
42536 // TODO: Check the case when the project is disposed. An InferredProject
42537 // could be disposed when a tsconfig.json is added to the workspace,
42538 // in which case it becomes a ConfiguredProject (or vice-versa).
42539 // We need to make sure that the FileWatcher is closed.
42540 if (!(project instanceof ts.server.ConfiguredProject)) {
42541 return;
42542 }
42543 const { host } = project.projectService;
42544 host.watchFile(project.getConfigFilePath(), (fileName, eventKind) => {
42545 project.log(`Config file changed: ${fileName}`);
42546 if (eventKind === ts.FileWatcherEventKind.Changed) {
42547 this.options = parseNgCompilerOptions(project, this.parseConfigHost, this.config);
42548 logCompilerOptions(project, this.options);
42549 }
42550 });
42551 }
42552 }
42553 function logCompilerOptions(project, options) {
42554 const { logger } = project.projectService;
42555 const projectName = project.getProjectName();
42556 logger.info(`Angular compiler options for ${projectName}: ` + JSON.stringify(options, null, 2));
42557 }
42558 function parseNgCompilerOptions(project, host, config) {
42559 if (!(project instanceof ts.server.ConfiguredProject)) {
42560 return {};
42561 }
42562 const { options, errors } = readConfiguration(project.getConfigFilePath(), /* existingOptions */ undefined, host);
42563 if (errors.length > 0) {
42564 project.setProjectErrors(errors);
42565 }
42566 // Projects loaded into the Language Service often include test files which are not part of the
42567 // app's main compilation unit, and these test files often include inline NgModules that declare
42568 // components from the app. These declarations conflict with the main declarations of such
42569 // components in the app's NgModules. This conflict is not normally present during regular
42570 // compilation because the app and the tests are part of separate compilation units.
42571 //
42572 // As a temporary mitigation of this problem, we instruct the compiler to ignore classes which
42573 // are not exported. In many cases, this ensures the test NgModules are ignored by the compiler
42574 // and only the real component declaration is used.
42575 options.compileNonExportedClasses = false;
42576 // If `forceStrictTemplates` is true, always enable `strictTemplates`
42577 // regardless of its value in tsconfig.json.
42578 if (config.forceStrictTemplates === true) {
42579 options.strictTemplates = true;
42580 }
42581 return options;
42582 }
42583 function createTypeCheckingProgramStrategy(project) {
42584 return {
42585 supportsInlineOperations: false,
42586 shimPathForComponent(component) {
42587 return TypeCheckShimGenerator.shimFor(absoluteFromSourceFile(component.getSourceFile()));
42588 },
42589 getProgram() {
42590 const program = project.getLanguageService().getProgram();
42591 if (!program) {
42592 throw new Error('Language service does not have a program!');
42593 }
42594 return program;
42595 },
42596 updateFiles(contents) {
42597 for (const [fileName, newText] of contents) {
42598 const scriptInfo = getOrCreateTypeCheckScriptInfo(project, fileName);
42599 const snapshot = scriptInfo.getSnapshot();
42600 const length = snapshot.getLength();
42601 scriptInfo.editContent(0, length, newText);
42602 }
42603 },
42604 };
42605 }
42606 function getOrCreateTypeCheckScriptInfo(project, tcf) {
42607 // First check if there is already a ScriptInfo for the tcf
42608 const { projectService } = project;
42609 let scriptInfo = projectService.getScriptInfo(tcf);
42610 if (!scriptInfo) {
42611 // ScriptInfo needs to be opened by client to be able to set its user-defined
42612 // content. We must also provide file content, otherwise the service will
42613 // attempt to fetch the content from disk and fail.
42614 scriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath(ts.server.toNormalizedPath(tcf), true, // openedByClient
42615 '', // fileContent
42616 // script info added by plugins should be marked as external, see
42617 // https://github.com/microsoft/TypeScript/blob/b217f22e798c781f55d17da72ed099a9dee5c650/src/compiler/program.ts#L1897-L1899
42618 ts.ScriptKind.External);
42619 if (!scriptInfo) {
42620 throw new Error(`Failed to create script info for ${tcf}`);
42621 }
42622 }
42623 // Add ScriptInfo to project if it's missing. A ScriptInfo needs to be part of
42624 // the project so that it becomes part of the program.
42625 if (!project.containsScriptInfo(scriptInfo)) {
42626 project.addRoot(scriptInfo);
42627 }
42628 return scriptInfo;
42629 }
42630 function isTemplateContext(program, fileName, position) {
42631 if (!isTypeScriptFile(fileName)) {
42632 // If we aren't in a TS file, we must be in an HTML file, which we treat as template context
42633 return true;
42634 }
42635 const node = findTightestNodeAtPosition(program, fileName, position);
42636 if (node === undefined) {
42637 return false;
42638 }
42639 let asgn = getPropertyAssignmentFromValue(node, 'template');
42640 if (asgn === null) {
42641 return false;
42642 }
42643 return getClassDeclFromDecoratorProp(asgn) !== null;
42644 }
42645 function isInAngularContext(program, fileName, position) {
42646 var _a, _b;
42647 if (!isTypeScriptFile(fileName)) {
42648 return true;
42649 }
42650 const node = findTightestNodeAtPosition(program, fileName, position);
42651 if (node === undefined) {
42652 return false;
42653 }
42654 const asgn = (_b = (_a = getPropertyAssignmentFromValue(node, 'template')) !== null && _a !== void 0 ? _a : getPropertyAssignmentFromValue(node, 'templateUrl')) !== null && _b !== void 0 ? _b : getPropertyAssignmentFromValue(node.parent, 'styleUrls');
42655 return asgn !== null && getClassDeclFromDecoratorProp(asgn) !== null;
42656 }
42657 function findTightestNodeAtPosition(program, fileName, position) {
42658 const sourceFile = program.getSourceFile(fileName);
42659 if (sourceFile === undefined) {
42660 return undefined;
42661 }
42662 return findTightestNode(sourceFile, position);
42663 }
42664
42665 /**
42666 * @license
42667 * Copyright Google LLC All Rights Reserved.
42668 *
42669 * Use of this source code is governed by an MIT-style license that can be
42670 * found in the LICENSE file at https://angular.io/license
42671 */
42672 function create(info) {
42673 const { project, languageService: tsLS, config } = info;
42674 const angularOnly = (config === null || config === void 0 ? void 0 : config.angularOnly) === true;
42675 const ngLS = new LanguageService(project, tsLS, config);
42676 function getSemanticDiagnostics(fileName) {
42677 const diagnostics = [];
42678 if (!angularOnly) {
42679 diagnostics.push(...tsLS.getSemanticDiagnostics(fileName));
42680 }
42681 diagnostics.push(...ngLS.getSemanticDiagnostics(fileName));
42682 return diagnostics;
42683 }
42684 function getQuickInfoAtPosition(fileName, position) {
42685 var _a;
42686 if (angularOnly) {
42687 return ngLS.getQuickInfoAtPosition(fileName, position);
42688 }
42689 else {
42690 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42691 return (_a = tsLS.getQuickInfoAtPosition(fileName, position)) !== null && _a !== void 0 ? _a : ngLS.getQuickInfoAtPosition(fileName, position);
42692 }
42693 }
42694 function getTypeDefinitionAtPosition(fileName, position) {
42695 var _a;
42696 if (angularOnly) {
42697 return ngLS.getTypeDefinitionAtPosition(fileName, position);
42698 }
42699 else {
42700 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42701 return (_a = tsLS.getTypeDefinitionAtPosition(fileName, position)) !== null && _a !== void 0 ? _a : ngLS.getTypeDefinitionAtPosition(fileName, position);
42702 }
42703 }
42704 function getDefinitionAndBoundSpan(fileName, position) {
42705 var _a;
42706 if (angularOnly) {
42707 return ngLS.getDefinitionAndBoundSpan(fileName, position);
42708 }
42709 else {
42710 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42711 return (_a = tsLS.getDefinitionAndBoundSpan(fileName, position)) !== null && _a !== void 0 ? _a : ngLS.getDefinitionAndBoundSpan(fileName, position);
42712 }
42713 }
42714 function getReferencesAtPosition(fileName, position) {
42715 return ngLS.getReferencesAtPosition(fileName, position);
42716 }
42717 function findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename) {
42718 // Most operations combine results from all extensions. However, rename locations are exclusive
42719 // (results from only one extension are used) so our rename locations are a superset of the TS
42720 // rename locations. As a result, we do not check the `angularOnly` flag here because we always
42721 // want to include results at TS locations as well as locations in templates.
42722 return ngLS.findRenameLocations(fileName, position);
42723 }
42724 function getRenameInfo(fileName, position) {
42725 // See the comment in `findRenameLocations` explaining why we don't check the `angularOnly`
42726 // flag.
42727 return ngLS.getRenameInfo(fileName, position);
42728 }
42729 function getCompletionsAtPosition(fileName, position, options) {
42730 var _a;
42731 if (angularOnly) {
42732 return ngLS.getCompletionsAtPosition(fileName, position, options);
42733 }
42734 else {
42735 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42736 return (_a = tsLS.getCompletionsAtPosition(fileName, position, options)) !== null && _a !== void 0 ? _a : ngLS.getCompletionsAtPosition(fileName, position, options);
42737 }
42738 }
42739 function getCompletionEntryDetails(fileName, position, entryName, formatOptions, source, preferences) {
42740 var _a;
42741 if (angularOnly) {
42742 return ngLS.getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences);
42743 }
42744 else {
42745 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42746 return (_a = tsLS.getCompletionEntryDetails(fileName, position, entryName, formatOptions, source, preferences)) !== null && _a !== void 0 ? _a : ngLS.getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences);
42747 }
42748 }
42749 function getCompletionEntrySymbol(fileName, position, name, source) {
42750 var _a;
42751 if (angularOnly) {
42752 return ngLS.getCompletionEntrySymbol(fileName, position, name);
42753 }
42754 else {
42755 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42756 return (_a = tsLS.getCompletionEntrySymbol(fileName, position, name, source)) !== null && _a !== void 0 ? _a : ngLS.getCompletionEntrySymbol(fileName, position, name);
42757 }
42758 }
42759 /**
42760 * Gets global diagnostics related to the program configuration and compiler options.
42761 */
42762 function getCompilerOptionsDiagnostics() {
42763 const diagnostics = [];
42764 if (!angularOnly) {
42765 diagnostics.push(...tsLS.getCompilerOptionsDiagnostics());
42766 }
42767 diagnostics.push(...ngLS.getCompilerOptionsDiagnostics());
42768 return diagnostics;
42769 }
42770 function getTcb(fileName, position) {
42771 return ngLS.getTcb(fileName, position);
42772 }
42773 return Object.assign(Object.assign({}, tsLS), { getSemanticDiagnostics,
42774 getTypeDefinitionAtPosition,
42775 getQuickInfoAtPosition,
42776 getDefinitionAndBoundSpan,
42777 getReferencesAtPosition,
42778 findRenameLocations,
42779 getRenameInfo,
42780 getCompletionsAtPosition,
42781 getCompletionEntryDetails,
42782 getCompletionEntrySymbol,
42783 getTcb,
42784 getCompilerOptionsDiagnostics });
42785 }
42786 function getExternalFiles(project) {
42787 if (!project.hasRoots()) {
42788 return []; // project has not been initialized
42789 }
42790 const typecheckFiles = [];
42791 for (const scriptInfo of project.getScriptInfos()) {
42792 if (scriptInfo.scriptKind === ts.ScriptKind.External) {
42793 // script info for typecheck file is marked as external, see
42794 // getOrCreateTypeCheckScriptInfo() in
42795 // packages/language-service/ivy/language_service.ts
42796 typecheckFiles.push(scriptInfo.fileName);
42797 }
42798 }
42799 return typecheckFiles;
42800 }
42801
42802 exports.create = create;
42803 exports.getExternalFiles = getExternalFiles;
42804
42805 Object.defineProperty(exports, '__esModule', { value: true });
42806
42807});
42808//# sourceMappingURL=ivy.js.map