UNPKG

2.1 MBJavaScriptView Raw
1/**
2 * @license Angular v13.0.2
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', 'module', 'path', 'url'], (function (exports, ts$1, os, ts, fs$1, module, path, url) { 'use strict';
31
32 function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
33
34 function _interopNamespace(e) {
35 if (e && e.__esModule) return e;
36 var n = Object.create(null);
37 if (e) {
38 Object.keys(e).forEach(function (k) {
39 if (k !== 'default') {
40 var d = Object.getOwnPropertyDescriptor(e, k);
41 Object.defineProperty(n, k, d.get ? d : {
42 enumerable: true,
43 get: function () { return e[k]; }
44 });
45 }
46 });
47 }
48 n["default"] = e;
49 return Object.freeze(n);
50 }
51
52 var ts__namespace = /*#__PURE__*/_interopNamespace(ts$1);
53 var ts__default = /*#__PURE__*/_interopDefaultLegacy(ts);
54 var fs__namespace = /*#__PURE__*/_interopNamespace(fs$1);
55 var module__default = /*#__PURE__*/_interopDefaultLegacy(module);
56 var path__namespace = /*#__PURE__*/_interopNamespace(path);
57
58 /**
59 * The default `FileSystem` that will always fail.
60 *
61 * This is a way of ensuring that the developer consciously chooses and
62 * configures the `FileSystem` before using it; particularly important when
63 * considering static functions like `absoluteFrom()` which rely on
64 * the `FileSystem` under the hood.
65 */
66 class InvalidFileSystem {
67 exists(path) {
68 throw makeError();
69 }
70 readFile(path) {
71 throw makeError();
72 }
73 readFileBuffer(path) {
74 throw makeError();
75 }
76 writeFile(path, data, exclusive) {
77 throw makeError();
78 }
79 removeFile(path) {
80 throw makeError();
81 }
82 symlink(target, path) {
83 throw makeError();
84 }
85 readdir(path) {
86 throw makeError();
87 }
88 lstat(path) {
89 throw makeError();
90 }
91 stat(path) {
92 throw makeError();
93 }
94 pwd() {
95 throw makeError();
96 }
97 chdir(path) {
98 throw makeError();
99 }
100 extname(path) {
101 throw makeError();
102 }
103 copyFile(from, to) {
104 throw makeError();
105 }
106 moveFile(from, to) {
107 throw makeError();
108 }
109 ensureDir(path) {
110 throw makeError();
111 }
112 removeDeep(path) {
113 throw makeError();
114 }
115 isCaseSensitive() {
116 throw makeError();
117 }
118 resolve(...paths) {
119 throw makeError();
120 }
121 dirname(file) {
122 throw makeError();
123 }
124 join(basePath, ...paths) {
125 throw makeError();
126 }
127 isRoot(path) {
128 throw makeError();
129 }
130 isRooted(path) {
131 throw makeError();
132 }
133 relative(from, to) {
134 throw makeError();
135 }
136 basename(filePath, extension) {
137 throw makeError();
138 }
139 realpath(filePath) {
140 throw makeError();
141 }
142 getDefaultLibLocation() {
143 throw makeError();
144 }
145 normalize(path) {
146 throw makeError();
147 }
148 }
149 function makeError() {
150 return new Error('FileSystem has not been configured. Please call `setFileSystem()` before calling this method.');
151 }
152
153 const TS_DTS_JS_EXTENSION = /(?:\.d)?\.ts$|\.js$/;
154 /**
155 * Remove a .ts, .d.ts, or .js extension from a file name.
156 */
157 function stripExtension(path) {
158 return path.replace(TS_DTS_JS_EXTENSION, '');
159 }
160 function getSourceFileOrError(program, fileName) {
161 const sf = program.getSourceFile(fileName);
162 if (sf === undefined) {
163 throw new Error(`Program does not contain "${fileName}" - available files are ${program.getSourceFiles().map(sf => sf.fileName).join(', ')}`);
164 }
165 return sf;
166 }
167
168 let fs = new InvalidFileSystem();
169 function getFileSystem() {
170 return fs;
171 }
172 function setFileSystem(fileSystem) {
173 fs = fileSystem;
174 }
175 /**
176 * Convert the path `path` to an `AbsoluteFsPath`, throwing an error if it's not an absolute path.
177 */
178 function absoluteFrom(path) {
179 if (!fs.isRooted(path)) {
180 throw new Error(`Internal Error: absoluteFrom(${path}): path is not absolute`);
181 }
182 return fs.resolve(path);
183 }
184 const ABSOLUTE_PATH = Symbol('AbsolutePath');
185 /**
186 * Extract an `AbsoluteFsPath` from a `ts.SourceFile`-like object.
187 */
188 function absoluteFromSourceFile(sf) {
189 const sfWithPatch = sf;
190 if (sfWithPatch[ABSOLUTE_PATH] === undefined) {
191 sfWithPatch[ABSOLUTE_PATH] = fs.resolve(sfWithPatch.fileName);
192 }
193 // Non-null assertion needed since TS doesn't narrow the type of fields that use a symbol as a key
194 // apparently.
195 return sfWithPatch[ABSOLUTE_PATH];
196 }
197 /**
198 * Static access to `dirname`.
199 */
200 function dirname(file) {
201 return fs.dirname(file);
202 }
203 /**
204 * Static access to `join`.
205 */
206 function join(basePath, ...paths) {
207 return fs.join(basePath, ...paths);
208 }
209 /**
210 * Static access to `resolve`s.
211 */
212 function resolve(basePath, ...paths) {
213 return fs.resolve(basePath, ...paths);
214 }
215 /**
216 * Static access to `isRooted`.
217 */
218 function isRooted(path) {
219 return fs.isRooted(path);
220 }
221 /**
222 * Static access to `relative`.
223 */
224 function relative(from, to) {
225 return fs.relative(from, to);
226 }
227 /**
228 * Returns true if the given path is locally relative.
229 *
230 * This is used to work out if the given path is relative (i.e. not absolute) but also is not
231 * escaping the current directory.
232 */
233 function isLocalRelativePath(relativePath) {
234 return !isRooted(relativePath) && !relativePath.startsWith('..');
235 }
236 /**
237 * Converts a path to a form suitable for use as a relative module import specifier.
238 *
239 * In other words it adds the `./` to the path if it is locally relative.
240 */
241 function toRelativeImport(relativePath) {
242 return isLocalRelativePath(relativePath) ? `./${relativePath}` : relativePath;
243 }
244
245 const LogicalProjectPath = {
246 /**
247 * Get the relative path between two `LogicalProjectPath`s.
248 *
249 * This will return a `PathSegment` which would be a valid module specifier to use in `from` when
250 * importing from `to`.
251 */
252 relativePathBetween: function (from, to) {
253 const relativePath = relative(dirname(resolve(from)), resolve(to));
254 return toRelativeImport(relativePath);
255 },
256 };
257 /**
258 * A utility class which can translate absolute paths to source files into logical paths in
259 * TypeScript's logical file system, based on the root directories of the project.
260 */
261 class LogicalFileSystem {
262 constructor(rootDirs, compilerHost) {
263 this.compilerHost = compilerHost;
264 /**
265 * A cache of file paths to project paths, because computation of these paths is slightly
266 * expensive.
267 */
268 this.cache = new Map();
269 // Make a copy and sort it by length in reverse order (longest first). This speeds up lookups,
270 // since there's no need to keep going through the array once a match is found.
271 this.rootDirs = rootDirs.concat([]).sort((a, b) => b.length - a.length);
272 this.canonicalRootDirs =
273 this.rootDirs.map(dir => this.compilerHost.getCanonicalFileName(dir));
274 }
275 /**
276 * Get the logical path in the project of a `ts.SourceFile`.
277 *
278 * This method is provided as a convenient alternative to calling
279 * `logicalPathOfFile(absoluteFromSourceFile(sf))`.
280 */
281 logicalPathOfSf(sf) {
282 return this.logicalPathOfFile(absoluteFrom(sf.fileName));
283 }
284 /**
285 * Get the logical path in the project of a source file.
286 *
287 * @returns A `LogicalProjectPath` to the source file, or `null` if the source file is not in any
288 * of the TS project's root directories.
289 */
290 logicalPathOfFile(physicalFile) {
291 const canonicalFilePath = this.compilerHost.getCanonicalFileName(physicalFile);
292 if (!this.cache.has(canonicalFilePath)) {
293 let logicalFile = null;
294 for (let i = 0; i < this.rootDirs.length; i++) {
295 const rootDir = this.rootDirs[i];
296 const canonicalRootDir = this.canonicalRootDirs[i];
297 if (isWithinBasePath(canonicalRootDir, canonicalFilePath)) {
298 // Note that we match against canonical paths but then create the logical path from
299 // original paths.
300 logicalFile = this.createLogicalProjectPath(physicalFile, rootDir);
301 // The logical project does not include any special "node_modules" nested directories.
302 if (logicalFile.indexOf('/node_modules/') !== -1) {
303 logicalFile = null;
304 }
305 else {
306 break;
307 }
308 }
309 }
310 this.cache.set(canonicalFilePath, logicalFile);
311 }
312 return this.cache.get(canonicalFilePath);
313 }
314 createLogicalProjectPath(file, rootDir) {
315 const logicalPath = stripExtension(file.substr(rootDir.length));
316 return (logicalPath.startsWith('/') ? logicalPath : '/' + logicalPath);
317 }
318 }
319 /**
320 * Is the `path` a descendant of the `base`?
321 * E.g. `foo/bar/zee` is within `foo/bar` but not within `foo/car`.
322 */
323 function isWithinBasePath(base, path) {
324 return isLocalRelativePath(relative(base, path));
325 }
326
327 /**
328 * @license
329 * Copyright Google LLC All Rights Reserved.
330 *
331 * Use of this source code is governed by an MIT-style license that can be
332 * found in the LICENSE file at https://angular.io/license
333 */
334 /**
335 * A wrapper around the Node.js file-system that supports path manipulation.
336 */
337 class NodeJSPathManipulation {
338 pwd() {
339 return this.normalize(process.cwd());
340 }
341 chdir(dir) {
342 process.chdir(dir);
343 }
344 resolve(...paths) {
345 return this.normalize(path__namespace.resolve(...paths));
346 }
347 dirname(file) {
348 return this.normalize(path__namespace.dirname(file));
349 }
350 join(basePath, ...paths) {
351 return this.normalize(path__namespace.join(basePath, ...paths));
352 }
353 isRoot(path) {
354 return this.dirname(path) === this.normalize(path);
355 }
356 isRooted(path) {
357 return path__namespace.isAbsolute(path);
358 }
359 relative(from, to) {
360 return this.normalize(path__namespace.relative(from, to));
361 }
362 basename(filePath, extension) {
363 return path__namespace.basename(filePath, extension);
364 }
365 extname(path) {
366 return path__namespace.extname(path);
367 }
368 normalize(path) {
369 // Convert backslashes to forward slashes
370 return path.replace(/\\/g, '/');
371 }
372 }
373 // CommonJS/ESM interop for determining the current file name and containing
374 // directory. These paths are needed for detecting whether the file system
375 // is case sensitive, or for finding the TypeScript default libraries.
376 // TODO(devversion): Simplify this in the future if devmode uses ESM as well.
377 const isCommonJS = typeof __filename !== 'undefined';
378 const currentFileUrl = isCommonJS ? null : __ESM_IMPORT_META_URL__;
379 const currentFileName = isCommonJS ? __filename : url.fileURLToPath(currentFileUrl);
380 /**
381 * A wrapper around the Node.js file-system that supports readonly operations and path manipulation.
382 */
383 class NodeJSReadonlyFileSystem extends NodeJSPathManipulation {
384 constructor() {
385 super(...arguments);
386 this._caseSensitive = undefined;
387 }
388 isCaseSensitive() {
389 if (this._caseSensitive === undefined) {
390 // Note the use of the real file-system is intentional:
391 // `this.exists()` relies upon `isCaseSensitive()` so that would cause an infinite recursion.
392 this._caseSensitive = !fs__namespace.existsSync(this.normalize(toggleCase(currentFileName)));
393 }
394 return this._caseSensitive;
395 }
396 exists(path) {
397 return fs__namespace.existsSync(path);
398 }
399 readFile(path) {
400 return fs__namespace.readFileSync(path, 'utf8');
401 }
402 readFileBuffer(path) {
403 return fs__namespace.readFileSync(path);
404 }
405 readdir(path) {
406 return fs__namespace.readdirSync(path);
407 }
408 lstat(path) {
409 return fs__namespace.lstatSync(path);
410 }
411 stat(path) {
412 return fs__namespace.statSync(path);
413 }
414 realpath(path) {
415 return this.resolve(fs__namespace.realpathSync(path));
416 }
417 getDefaultLibLocation() {
418 // TODO(devversion): Once devmode output uses ESM, we can simplify this.
419 const requireFn = isCommonJS ? require : module__default["default"].createRequire(currentFileUrl);
420 return this.resolve(requireFn.resolve('typescript'), '..');
421 }
422 }
423 /**
424 * A wrapper around the Node.js file-system (i.e. the `fs` package).
425 */
426 class NodeJSFileSystem extends NodeJSReadonlyFileSystem {
427 writeFile(path, data, exclusive = false) {
428 fs__namespace.writeFileSync(path, data, exclusive ? { flag: 'wx' } : undefined);
429 }
430 removeFile(path) {
431 fs__namespace.unlinkSync(path);
432 }
433 symlink(target, path) {
434 fs__namespace.symlinkSync(target, path);
435 }
436 copyFile(from, to) {
437 fs__namespace.copyFileSync(from, to);
438 }
439 moveFile(from, to) {
440 fs__namespace.renameSync(from, to);
441 }
442 ensureDir(path) {
443 const parents = [];
444 while (!this.isRoot(path) && !this.exists(path)) {
445 parents.push(path);
446 path = this.dirname(path);
447 }
448 while (parents.length) {
449 this.safeMkdir(parents.pop());
450 }
451 }
452 removeDeep(path) {
453 fs__namespace.rmdirSync(path, { recursive: true });
454 }
455 safeMkdir(path) {
456 try {
457 fs__namespace.mkdirSync(path);
458 }
459 catch (err) {
460 // Ignore the error, if the path already exists and points to a directory.
461 // Re-throw otherwise.
462 if (!this.exists(path) || !this.stat(path).isDirectory()) {
463 throw err;
464 }
465 }
466 }
467 }
468 /**
469 * Toggle the case of each character in a string.
470 */
471 function toggleCase(str) {
472 return str.replace(/\w/g, ch => ch.toUpperCase() === ch ? ch.toLowerCase() : ch.toUpperCase());
473 }
474
475 /**
476 * @license
477 * Copyright Google LLC All Rights Reserved.
478 *
479 * Use of this source code is governed by an MIT-style license that can be
480 * found in the LICENSE file at https://angular.io/license
481 */
482 var TagContentType;
483 (function (TagContentType) {
484 TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
485 TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
486 TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
487 })(TagContentType || (TagContentType = {}));
488 function splitNsName(elementName) {
489 if (elementName[0] != ':') {
490 return [null, elementName];
491 }
492 const colonIndex = elementName.indexOf(':', 1);
493 if (colonIndex === -1) {
494 throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
495 }
496 return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
497 }
498 // `<ng-container>` tags work the same regardless the namespace
499 function isNgContainer(tagName) {
500 return splitNsName(tagName)[1] === 'ng-container';
501 }
502 // `<ng-content>` tags work the same regardless the namespace
503 function isNgContent(tagName) {
504 return splitNsName(tagName)[1] === 'ng-content';
505 }
506 // `<ng-template>` tags work the same regardless the namespace
507 function isNgTemplate(tagName) {
508 return splitNsName(tagName)[1] === 'ng-template';
509 }
510 function getNsPrefix(fullName) {
511 return fullName === null ? null : splitNsName(fullName)[0];
512 }
513 function mergeNsAndName(prefix, localName) {
514 return prefix ? `:${prefix}:${localName}` : localName;
515 }
516
517 /**
518 * @license
519 * Copyright Google LLC All Rights Reserved.
520 *
521 * Use of this source code is governed by an MIT-style license that can be
522 * found in the LICENSE file at https://angular.io/license
523 */
524 class HtmlTagDefinition {
525 constructor({ closedByChildren, implicitNamespacePrefix, contentType = TagContentType.PARSABLE_DATA, closedByParent = false, isVoid = false, ignoreFirstLf = false, preventNamespaceInheritance = false } = {}) {
526 this.closedByChildren = {};
527 this.closedByParent = false;
528 this.canSelfClose = false;
529 if (closedByChildren && closedByChildren.length > 0) {
530 closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true);
531 }
532 this.isVoid = isVoid;
533 this.closedByParent = closedByParent || isVoid;
534 this.implicitNamespacePrefix = implicitNamespacePrefix || null;
535 this.contentType = contentType;
536 this.ignoreFirstLf = ignoreFirstLf;
537 this.preventNamespaceInheritance = preventNamespaceInheritance;
538 }
539 isClosedByChild(name) {
540 return this.isVoid || name.toLowerCase() in this.closedByChildren;
541 }
542 getContentType(prefix) {
543 if (typeof this.contentType === 'object') {
544 const overrideType = prefix === undefined ? undefined : this.contentType[prefix];
545 return overrideType ?? this.contentType.default;
546 }
547 return this.contentType;
548 }
549 }
550 let _DEFAULT_TAG_DEFINITION;
551 // see https://www.w3.org/TR/html51/syntax.html#optional-tags
552 // This implementation does not fully conform to the HTML5 spec.
553 let TAG_DEFINITIONS;
554 function getHtmlTagDefinition(tagName) {
555 if (!TAG_DEFINITIONS) {
556 _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition();
557 TAG_DEFINITIONS = {
558 'base': new HtmlTagDefinition({ isVoid: true }),
559 'meta': new HtmlTagDefinition({ isVoid: true }),
560 'area': new HtmlTagDefinition({ isVoid: true }),
561 'embed': new HtmlTagDefinition({ isVoid: true }),
562 'link': new HtmlTagDefinition({ isVoid: true }),
563 'img': new HtmlTagDefinition({ isVoid: true }),
564 'input': new HtmlTagDefinition({ isVoid: true }),
565 'param': new HtmlTagDefinition({ isVoid: true }),
566 'hr': new HtmlTagDefinition({ isVoid: true }),
567 'br': new HtmlTagDefinition({ isVoid: true }),
568 'source': new HtmlTagDefinition({ isVoid: true }),
569 'track': new HtmlTagDefinition({ isVoid: true }),
570 'wbr': new HtmlTagDefinition({ isVoid: true }),
571 'p': new HtmlTagDefinition({
572 closedByChildren: [
573 'address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset',
574 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',
575 'h6', 'header', 'hgroup', 'hr', 'main', 'nav', 'ol',
576 'p', 'pre', 'section', 'table', 'ul'
577 ],
578 closedByParent: true
579 }),
580 'thead': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'] }),
581 'tbody': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'], closedByParent: true }),
582 'tfoot': new HtmlTagDefinition({ closedByChildren: ['tbody'], closedByParent: true }),
583 'tr': new HtmlTagDefinition({ closedByChildren: ['tr'], closedByParent: true }),
584 'td': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }),
585 'th': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }),
586 'col': new HtmlTagDefinition({ isVoid: true }),
587 'svg': new HtmlTagDefinition({ implicitNamespacePrefix: 'svg' }),
588 'foreignObject': new HtmlTagDefinition({
589 // Usually the implicit namespace here would be redundant since it will be inherited from
590 // the parent `svg`, but we have to do it for `foreignObject`, because the way the parser
591 // works is that the parent node of an end tag is its own start tag which means that
592 // the `preventNamespaceInheritance` on `foreignObject` would have it default to the
593 // implicit namespace which is `html`, unless specified otherwise.
594 implicitNamespacePrefix: 'svg',
595 // We want to prevent children of foreignObject from inheriting its namespace, because
596 // the point of the element is to allow nodes from other namespaces to be inserted.
597 preventNamespaceInheritance: true,
598 }),
599 'math': new HtmlTagDefinition({ implicitNamespacePrefix: 'math' }),
600 'li': new HtmlTagDefinition({ closedByChildren: ['li'], closedByParent: true }),
601 'dt': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'] }),
602 'dd': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'], closedByParent: true }),
603 'rb': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
604 'rt': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
605 'rtc': new HtmlTagDefinition({ closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true }),
606 'rp': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
607 'optgroup': new HtmlTagDefinition({ closedByChildren: ['optgroup'], closedByParent: true }),
608 'option': new HtmlTagDefinition({ closedByChildren: ['option', 'optgroup'], closedByParent: true }),
609 'pre': new HtmlTagDefinition({ ignoreFirstLf: true }),
610 'listing': new HtmlTagDefinition({ ignoreFirstLf: true }),
611 'style': new HtmlTagDefinition({ contentType: TagContentType.RAW_TEXT }),
612 'script': new HtmlTagDefinition({ contentType: TagContentType.RAW_TEXT }),
613 'title': new HtmlTagDefinition({
614 // The browser supports two separate `title` tags which have to use
615 // a different content type: `HTMLTitleElement` and `SVGTitleElement`
616 contentType: { default: TagContentType.ESCAPABLE_RAW_TEXT, svg: TagContentType.PARSABLE_DATA }
617 }),
618 'textarea': new HtmlTagDefinition({ contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true }),
619 };
620 }
621 // We have to make both a case-sensitive and a case-insesitive lookup, because
622 // HTML tag names are case insensitive, whereas some SVG tags are case sensitive.
623 return TAG_DEFINITIONS[tagName] ?? TAG_DEFINITIONS[tagName.toLowerCase()] ??
624 _DEFAULT_TAG_DEFINITION;
625 }
626
627 /**
628 * @license
629 * Copyright Google LLC All Rights Reserved.
630 *
631 * Use of this source code is governed by an MIT-style license that can be
632 * found in the LICENSE file at https://angular.io/license
633 */
634 const _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' + // 1: ":not("
635 '(([\\.\\#]?)[-\\w]+)|' + // 2: "tag"; 3: "."/"#";
636 // "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range
637 // 4: attribute; 5: attribute_string; 6: attribute_value
638 '(?:\\[([-.\\w*\\\\$]+)(?:=([\"\']?)([^\\]\"\']*)\\5)?\\])|' + // "[name]", "[name=value]",
639 // "[name="value"]",
640 // "[name='value']"
641 '(\\))|' + // 7: ")"
642 '(\\s*,\\s*)', // 8: ","
643 'g');
644 /**
645 * A css selector contains an element name,
646 * css classes and attribute/value pairs with the purpose
647 * of selecting subsets out of them.
648 */
649 class CssSelector {
650 constructor() {
651 this.element = null;
652 this.classNames = [];
653 /**
654 * The selectors are encoded in pairs where:
655 * - even locations are attribute names
656 * - odd locations are attribute values.
657 *
658 * Example:
659 * Selector: `[key1=value1][key2]` would parse to:
660 * ```
661 * ['key1', 'value1', 'key2', '']
662 * ```
663 */
664 this.attrs = [];
665 this.notSelectors = [];
666 }
667 static parse(selector) {
668 const results = [];
669 const _addResult = (res, cssSel) => {
670 if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 &&
671 cssSel.attrs.length == 0) {
672 cssSel.element = '*';
673 }
674 res.push(cssSel);
675 };
676 let cssSelector = new CssSelector();
677 let match;
678 let current = cssSelector;
679 let inNot = false;
680 _SELECTOR_REGEXP.lastIndex = 0;
681 while (match = _SELECTOR_REGEXP.exec(selector)) {
682 if (match[1 /* NOT */]) {
683 if (inNot) {
684 throw new Error('Nesting :not in a selector is not allowed');
685 }
686 inNot = true;
687 current = new CssSelector();
688 cssSelector.notSelectors.push(current);
689 }
690 const tag = match[2 /* TAG */];
691 if (tag) {
692 const prefix = match[3 /* PREFIX */];
693 if (prefix === '#') {
694 // #hash
695 current.addAttribute('id', tag.substr(1));
696 }
697 else if (prefix === '.') {
698 // Class
699 current.addClassName(tag.substr(1));
700 }
701 else {
702 // Element
703 current.setElement(tag);
704 }
705 }
706 const attribute = match[4 /* ATTRIBUTE */];
707 if (attribute) {
708 current.addAttribute(current.unescapeAttribute(attribute), match[6 /* ATTRIBUTE_VALUE */]);
709 }
710 if (match[7 /* NOT_END */]) {
711 inNot = false;
712 current = cssSelector;
713 }
714 if (match[8 /* SEPARATOR */]) {
715 if (inNot) {
716 throw new Error('Multiple selectors in :not are not supported');
717 }
718 _addResult(results, cssSelector);
719 cssSelector = current = new CssSelector();
720 }
721 }
722 _addResult(results, cssSelector);
723 return results;
724 }
725 /**
726 * Unescape `\$` sequences from the CSS attribute selector.
727 *
728 * This is needed because `$` can have a special meaning in CSS selectors,
729 * but we might want to match an attribute that contains `$`.
730 * [MDN web link for more
731 * info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).
732 * @param attr the attribute to unescape.
733 * @returns the unescaped string.
734 */
735 unescapeAttribute(attr) {
736 let result = '';
737 let escaping = false;
738 for (let i = 0; i < attr.length; i++) {
739 const char = attr.charAt(i);
740 if (char === '\\') {
741 escaping = true;
742 continue;
743 }
744 if (char === '$' && !escaping) {
745 throw new Error(`Error in attribute selector "${attr}". ` +
746 `Unescaped "$" is not supported. Please escape with "\\$".`);
747 }
748 escaping = false;
749 result += char;
750 }
751 return result;
752 }
753 /**
754 * Escape `$` sequences from the CSS attribute selector.
755 *
756 * This is needed because `$` can have a special meaning in CSS selectors,
757 * with this method we are escaping `$` with `\$'.
758 * [MDN web link for more
759 * info](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors).
760 * @param attr the attribute to escape.
761 * @returns the escaped string.
762 */
763 escapeAttribute(attr) {
764 return attr.replace(/\\/g, '\\\\').replace(/\$/g, '\\$');
765 }
766 isElementSelector() {
767 return this.hasElementSelector() && this.classNames.length == 0 && this.attrs.length == 0 &&
768 this.notSelectors.length === 0;
769 }
770 hasElementSelector() {
771 return !!this.element;
772 }
773 setElement(element = null) {
774 this.element = element;
775 }
776 /** Gets a template string for an element that matches the selector. */
777 getMatchingElementTemplate() {
778 const tagName = this.element || 'div';
779 const classAttr = this.classNames.length > 0 ? ` class="${this.classNames.join(' ')}"` : '';
780 let attrs = '';
781 for (let i = 0; i < this.attrs.length; i += 2) {
782 const attrName = this.attrs[i];
783 const attrValue = this.attrs[i + 1] !== '' ? `="${this.attrs[i + 1]}"` : '';
784 attrs += ` ${attrName}${attrValue}`;
785 }
786 return getHtmlTagDefinition(tagName).isVoid ? `<${tagName}${classAttr}${attrs}/>` :
787 `<${tagName}${classAttr}${attrs}></${tagName}>`;
788 }
789 getAttrs() {
790 const result = [];
791 if (this.classNames.length > 0) {
792 result.push('class', this.classNames.join(' '));
793 }
794 return result.concat(this.attrs);
795 }
796 addAttribute(name, value = '') {
797 this.attrs.push(name, value && value.toLowerCase() || '');
798 }
799 addClassName(name) {
800 this.classNames.push(name.toLowerCase());
801 }
802 toString() {
803 let res = this.element || '';
804 if (this.classNames) {
805 this.classNames.forEach(klass => res += `.${klass}`);
806 }
807 if (this.attrs) {
808 for (let i = 0; i < this.attrs.length; i += 2) {
809 const name = this.escapeAttribute(this.attrs[i]);
810 const value = this.attrs[i + 1];
811 res += `[${name}${value ? '=' + value : ''}]`;
812 }
813 }
814 this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`);
815 return res;
816 }
817 }
818 /**
819 * Reads a list of CssSelectors and allows to calculate which ones
820 * are contained in a given CssSelector.
821 */
822 class SelectorMatcher {
823 constructor() {
824 this._elementMap = new Map();
825 this._elementPartialMap = new Map();
826 this._classMap = new Map();
827 this._classPartialMap = new Map();
828 this._attrValueMap = new Map();
829 this._attrValuePartialMap = new Map();
830 this._listContexts = [];
831 }
832 static createNotMatcher(notSelectors) {
833 const notMatcher = new SelectorMatcher();
834 notMatcher.addSelectables(notSelectors, null);
835 return notMatcher;
836 }
837 addSelectables(cssSelectors, callbackCtxt) {
838 let listContext = null;
839 if (cssSelectors.length > 1) {
840 listContext = new SelectorListContext(cssSelectors);
841 this._listContexts.push(listContext);
842 }
843 for (let i = 0; i < cssSelectors.length; i++) {
844 this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
845 }
846 }
847 /**
848 * Add an object that can be found later on by calling `match`.
849 * @param cssSelector A css selector
850 * @param callbackCtxt An opaque object that will be given to the callback of the `match` function
851 */
852 _addSelectable(cssSelector, callbackCtxt, listContext) {
853 let matcher = this;
854 const element = cssSelector.element;
855 const classNames = cssSelector.classNames;
856 const attrs = cssSelector.attrs;
857 const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
858 if (element) {
859 const isTerminal = attrs.length === 0 && classNames.length === 0;
860 if (isTerminal) {
861 this._addTerminal(matcher._elementMap, element, selectable);
862 }
863 else {
864 matcher = this._addPartial(matcher._elementPartialMap, element);
865 }
866 }
867 if (classNames) {
868 for (let i = 0; i < classNames.length; i++) {
869 const isTerminal = attrs.length === 0 && i === classNames.length - 1;
870 const className = classNames[i];
871 if (isTerminal) {
872 this._addTerminal(matcher._classMap, className, selectable);
873 }
874 else {
875 matcher = this._addPartial(matcher._classPartialMap, className);
876 }
877 }
878 }
879 if (attrs) {
880 for (let i = 0; i < attrs.length; i += 2) {
881 const isTerminal = i === attrs.length - 2;
882 const name = attrs[i];
883 const value = attrs[i + 1];
884 if (isTerminal) {
885 const terminalMap = matcher._attrValueMap;
886 let terminalValuesMap = terminalMap.get(name);
887 if (!terminalValuesMap) {
888 terminalValuesMap = new Map();
889 terminalMap.set(name, terminalValuesMap);
890 }
891 this._addTerminal(terminalValuesMap, value, selectable);
892 }
893 else {
894 const partialMap = matcher._attrValuePartialMap;
895 let partialValuesMap = partialMap.get(name);
896 if (!partialValuesMap) {
897 partialValuesMap = new Map();
898 partialMap.set(name, partialValuesMap);
899 }
900 matcher = this._addPartial(partialValuesMap, value);
901 }
902 }
903 }
904 }
905 _addTerminal(map, name, selectable) {
906 let terminalList = map.get(name);
907 if (!terminalList) {
908 terminalList = [];
909 map.set(name, terminalList);
910 }
911 terminalList.push(selectable);
912 }
913 _addPartial(map, name) {
914 let matcher = map.get(name);
915 if (!matcher) {
916 matcher = new SelectorMatcher();
917 map.set(name, matcher);
918 }
919 return matcher;
920 }
921 /**
922 * Find the objects that have been added via `addSelectable`
923 * whose css selector is contained in the given css selector.
924 * @param cssSelector A css selector
925 * @param matchedCallback This callback will be called with the object handed into `addSelectable`
926 * @return boolean true if a match was found
927 */
928 match(cssSelector, matchedCallback) {
929 let result = false;
930 const element = cssSelector.element;
931 const classNames = cssSelector.classNames;
932 const attrs = cssSelector.attrs;
933 for (let i = 0; i < this._listContexts.length; i++) {
934 this._listContexts[i].alreadyMatched = false;
935 }
936 result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
937 result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) ||
938 result;
939 if (classNames) {
940 for (let i = 0; i < classNames.length; i++) {
941 const className = classNames[i];
942 result =
943 this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
944 result =
945 this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) ||
946 result;
947 }
948 }
949 if (attrs) {
950 for (let i = 0; i < attrs.length; i += 2) {
951 const name = attrs[i];
952 const value = attrs[i + 1];
953 const terminalValuesMap = this._attrValueMap.get(name);
954 if (value) {
955 result =
956 this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
957 }
958 result =
959 this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
960 const partialValuesMap = this._attrValuePartialMap.get(name);
961 if (value) {
962 result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
963 }
964 result =
965 this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result;
966 }
967 }
968 return result;
969 }
970 /** @internal */
971 _matchTerminal(map, name, cssSelector, matchedCallback) {
972 if (!map || typeof name !== 'string') {
973 return false;
974 }
975 let selectables = map.get(name) || [];
976 const starSelectables = map.get('*');
977 if (starSelectables) {
978 selectables = selectables.concat(starSelectables);
979 }
980 if (selectables.length === 0) {
981 return false;
982 }
983 let selectable;
984 let result = false;
985 for (let i = 0; i < selectables.length; i++) {
986 selectable = selectables[i];
987 result = selectable.finalize(cssSelector, matchedCallback) || result;
988 }
989 return result;
990 }
991 /** @internal */
992 _matchPartial(map, name, cssSelector, matchedCallback) {
993 if (!map || typeof name !== 'string') {
994 return false;
995 }
996 const nestedSelector = map.get(name);
997 if (!nestedSelector) {
998 return false;
999 }
1000 // TODO(perf): get rid of recursion and measure again
1001 // TODO(perf): don't pass the whole selector into the recursion,
1002 // but only the not processed parts
1003 return nestedSelector.match(cssSelector, matchedCallback);
1004 }
1005 }
1006 class SelectorListContext {
1007 constructor(selectors) {
1008 this.selectors = selectors;
1009 this.alreadyMatched = false;
1010 }
1011 }
1012 // Store context to pass back selector and context when a selector is matched
1013 class SelectorContext {
1014 constructor(selector, cbContext, listContext) {
1015 this.selector = selector;
1016 this.cbContext = cbContext;
1017 this.listContext = listContext;
1018 this.notSelectors = selector.notSelectors;
1019 }
1020 finalize(cssSelector, callback) {
1021 let result = true;
1022 if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
1023 const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
1024 result = !notMatcher.match(cssSelector, null);
1025 }
1026 if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) {
1027 if (this.listContext) {
1028 this.listContext.alreadyMatched = true;
1029 }
1030 callback(this.selector, this.cbContext);
1031 }
1032 return result;
1033 }
1034 }
1035
1036 /**
1037 * @license
1038 * Copyright Google LLC All Rights Reserved.
1039 *
1040 * Use of this source code is governed by an MIT-style license that can be
1041 * found in the LICENSE file at https://angular.io/license
1042 */
1043 // Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not
1044 // explicitly set.
1045 const emitDistinctChangesOnlyDefaultValue = true;
1046 var ViewEncapsulation;
1047 (function (ViewEncapsulation) {
1048 ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
1049 // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
1050 ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
1051 ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
1052 })(ViewEncapsulation || (ViewEncapsulation = {}));
1053 var ChangeDetectionStrategy;
1054 (function (ChangeDetectionStrategy) {
1055 ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
1056 ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
1057 })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
1058 const CUSTOM_ELEMENTS_SCHEMA = {
1059 name: 'custom-elements'
1060 };
1061 const NO_ERRORS_SCHEMA = {
1062 name: 'no-errors-schema'
1063 };
1064 var SecurityContext;
1065 (function (SecurityContext) {
1066 SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
1067 SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
1068 SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
1069 SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
1070 SecurityContext[SecurityContext["URL"] = 4] = "URL";
1071 SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
1072 })(SecurityContext || (SecurityContext = {}));
1073 var MissingTranslationStrategy;
1074 (function (MissingTranslationStrategy) {
1075 MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error";
1076 MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning";
1077 MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore";
1078 })(MissingTranslationStrategy || (MissingTranslationStrategy = {}));
1079 function parserSelectorToSimpleSelector(selector) {
1080 const classes = selector.classNames && selector.classNames.length ?
1081 [8 /* CLASS */, ...selector.classNames] :
1082 [];
1083 const elementName = selector.element && selector.element !== '*' ? selector.element : '';
1084 return [elementName, ...selector.attrs, ...classes];
1085 }
1086 function parserSelectorToNegativeSelector(selector) {
1087 const classes = selector.classNames && selector.classNames.length ?
1088 [8 /* CLASS */, ...selector.classNames] :
1089 [];
1090 if (selector.element) {
1091 return [
1092 1 /* NOT */ | 4 /* ELEMENT */, selector.element, ...selector.attrs, ...classes
1093 ];
1094 }
1095 else if (selector.attrs.length) {
1096 return [1 /* NOT */ | 2 /* ATTRIBUTE */, ...selector.attrs, ...classes];
1097 }
1098 else {
1099 return selector.classNames && selector.classNames.length ?
1100 [1 /* NOT */ | 8 /* CLASS */, ...selector.classNames] :
1101 [];
1102 }
1103 }
1104 function parserSelectorToR3Selector(selector) {
1105 const positive = parserSelectorToSimpleSelector(selector);
1106 const negative = selector.notSelectors && selector.notSelectors.length ?
1107 selector.notSelectors.map(notSelector => parserSelectorToNegativeSelector(notSelector)) :
1108 [];
1109 return positive.concat(...negative);
1110 }
1111 function parseSelectorToR3Selector(selector) {
1112 return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : [];
1113 }
1114
1115 /**
1116 * @license
1117 * Copyright Google LLC All Rights Reserved.
1118 *
1119 * Use of this source code is governed by an MIT-style license that can be
1120 * found in the LICENSE file at https://angular.io/license
1121 */
1122 //// Types
1123 var TypeModifier;
1124 (function (TypeModifier) {
1125 TypeModifier[TypeModifier["Const"] = 0] = "Const";
1126 })(TypeModifier || (TypeModifier = {}));
1127 class Type {
1128 constructor(modifiers = []) {
1129 this.modifiers = modifiers;
1130 }
1131 hasModifier(modifier) {
1132 return this.modifiers.indexOf(modifier) !== -1;
1133 }
1134 }
1135 var BuiltinTypeName;
1136 (function (BuiltinTypeName) {
1137 BuiltinTypeName[BuiltinTypeName["Dynamic"] = 0] = "Dynamic";
1138 BuiltinTypeName[BuiltinTypeName["Bool"] = 1] = "Bool";
1139 BuiltinTypeName[BuiltinTypeName["String"] = 2] = "String";
1140 BuiltinTypeName[BuiltinTypeName["Int"] = 3] = "Int";
1141 BuiltinTypeName[BuiltinTypeName["Number"] = 4] = "Number";
1142 BuiltinTypeName[BuiltinTypeName["Function"] = 5] = "Function";
1143 BuiltinTypeName[BuiltinTypeName["Inferred"] = 6] = "Inferred";
1144 BuiltinTypeName[BuiltinTypeName["None"] = 7] = "None";
1145 })(BuiltinTypeName || (BuiltinTypeName = {}));
1146 class BuiltinType extends Type {
1147 constructor(name, modifiers) {
1148 super(modifiers);
1149 this.name = name;
1150 }
1151 visitType(visitor, context) {
1152 return visitor.visitBuiltinType(this, context);
1153 }
1154 }
1155 class ExpressionType extends Type {
1156 constructor(value, modifiers, typeParams = null) {
1157 super(modifiers);
1158 this.value = value;
1159 this.typeParams = typeParams;
1160 }
1161 visitType(visitor, context) {
1162 return visitor.visitExpressionType(this, context);
1163 }
1164 }
1165 const DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
1166 const INFERRED_TYPE = new BuiltinType(BuiltinTypeName.Inferred);
1167 const BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
1168 new BuiltinType(BuiltinTypeName.Int);
1169 const NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
1170 const STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
1171 new BuiltinType(BuiltinTypeName.Function);
1172 const NONE_TYPE = new BuiltinType(BuiltinTypeName.None);
1173 ///// Expressions
1174 var UnaryOperator;
1175 (function (UnaryOperator) {
1176 UnaryOperator[UnaryOperator["Minus"] = 0] = "Minus";
1177 UnaryOperator[UnaryOperator["Plus"] = 1] = "Plus";
1178 })(UnaryOperator || (UnaryOperator = {}));
1179 var BinaryOperator;
1180 (function (BinaryOperator) {
1181 BinaryOperator[BinaryOperator["Equals"] = 0] = "Equals";
1182 BinaryOperator[BinaryOperator["NotEquals"] = 1] = "NotEquals";
1183 BinaryOperator[BinaryOperator["Identical"] = 2] = "Identical";
1184 BinaryOperator[BinaryOperator["NotIdentical"] = 3] = "NotIdentical";
1185 BinaryOperator[BinaryOperator["Minus"] = 4] = "Minus";
1186 BinaryOperator[BinaryOperator["Plus"] = 5] = "Plus";
1187 BinaryOperator[BinaryOperator["Divide"] = 6] = "Divide";
1188 BinaryOperator[BinaryOperator["Multiply"] = 7] = "Multiply";
1189 BinaryOperator[BinaryOperator["Modulo"] = 8] = "Modulo";
1190 BinaryOperator[BinaryOperator["And"] = 9] = "And";
1191 BinaryOperator[BinaryOperator["Or"] = 10] = "Or";
1192 BinaryOperator[BinaryOperator["BitwiseAnd"] = 11] = "BitwiseAnd";
1193 BinaryOperator[BinaryOperator["Lower"] = 12] = "Lower";
1194 BinaryOperator[BinaryOperator["LowerEquals"] = 13] = "LowerEquals";
1195 BinaryOperator[BinaryOperator["Bigger"] = 14] = "Bigger";
1196 BinaryOperator[BinaryOperator["BiggerEquals"] = 15] = "BiggerEquals";
1197 BinaryOperator[BinaryOperator["NullishCoalesce"] = 16] = "NullishCoalesce";
1198 })(BinaryOperator || (BinaryOperator = {}));
1199 function nullSafeIsEquivalent(base, other) {
1200 if (base == null || other == null) {
1201 return base == other;
1202 }
1203 return base.isEquivalent(other);
1204 }
1205 function areAllEquivalentPredicate(base, other, equivalentPredicate) {
1206 const len = base.length;
1207 if (len !== other.length) {
1208 return false;
1209 }
1210 for (let i = 0; i < len; i++) {
1211 if (!equivalentPredicate(base[i], other[i])) {
1212 return false;
1213 }
1214 }
1215 return true;
1216 }
1217 function areAllEquivalent(base, other) {
1218 return areAllEquivalentPredicate(base, other, (baseElement, otherElement) => baseElement.isEquivalent(otherElement));
1219 }
1220 class Expression {
1221 constructor(type, sourceSpan) {
1222 this.type = type || null;
1223 this.sourceSpan = sourceSpan || null;
1224 }
1225 prop(name, sourceSpan) {
1226 return new ReadPropExpr(this, name, null, sourceSpan);
1227 }
1228 key(index, type, sourceSpan) {
1229 return new ReadKeyExpr(this, index, type, sourceSpan);
1230 }
1231 callFn(params, sourceSpan, pure) {
1232 return new InvokeFunctionExpr(this, params, null, sourceSpan, pure);
1233 }
1234 instantiate(params, type, sourceSpan) {
1235 return new InstantiateExpr(this, params, type, sourceSpan);
1236 }
1237 conditional(trueCase, falseCase = null, sourceSpan) {
1238 return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan);
1239 }
1240 equals(rhs, sourceSpan) {
1241 return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs, null, sourceSpan);
1242 }
1243 notEquals(rhs, sourceSpan) {
1244 return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs, null, sourceSpan);
1245 }
1246 identical(rhs, sourceSpan) {
1247 return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs, null, sourceSpan);
1248 }
1249 notIdentical(rhs, sourceSpan) {
1250 return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs, null, sourceSpan);
1251 }
1252 minus(rhs, sourceSpan) {
1253 return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs, null, sourceSpan);
1254 }
1255 plus(rhs, sourceSpan) {
1256 return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs, null, sourceSpan);
1257 }
1258 divide(rhs, sourceSpan) {
1259 return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs, null, sourceSpan);
1260 }
1261 multiply(rhs, sourceSpan) {
1262 return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs, null, sourceSpan);
1263 }
1264 modulo(rhs, sourceSpan) {
1265 return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs, null, sourceSpan);
1266 }
1267 and(rhs, sourceSpan) {
1268 return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan);
1269 }
1270 bitwiseAnd(rhs, sourceSpan, parens = true) {
1271 return new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, this, rhs, null, sourceSpan, parens);
1272 }
1273 or(rhs, sourceSpan) {
1274 return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs, null, sourceSpan);
1275 }
1276 lower(rhs, sourceSpan) {
1277 return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs, null, sourceSpan);
1278 }
1279 lowerEquals(rhs, sourceSpan) {
1280 return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs, null, sourceSpan);
1281 }
1282 bigger(rhs, sourceSpan) {
1283 return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs, null, sourceSpan);
1284 }
1285 biggerEquals(rhs, sourceSpan) {
1286 return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs, null, sourceSpan);
1287 }
1288 isBlank(sourceSpan) {
1289 // Note: We use equals by purpose here to compare to null and undefined in JS.
1290 // We use the typed null to allow strictNullChecks to narrow types.
1291 return this.equals(TYPED_NULL_EXPR, sourceSpan);
1292 }
1293 cast(type, sourceSpan) {
1294 return new CastExpr(this, type, sourceSpan);
1295 }
1296 nullishCoalesce(rhs, sourceSpan) {
1297 return new BinaryOperatorExpr(BinaryOperator.NullishCoalesce, this, rhs, null, sourceSpan);
1298 }
1299 toStmt() {
1300 return new ExpressionStatement(this, null);
1301 }
1302 }
1303 var BuiltinVar;
1304 (function (BuiltinVar) {
1305 BuiltinVar[BuiltinVar["This"] = 0] = "This";
1306 BuiltinVar[BuiltinVar["Super"] = 1] = "Super";
1307 BuiltinVar[BuiltinVar["CatchError"] = 2] = "CatchError";
1308 BuiltinVar[BuiltinVar["CatchStack"] = 3] = "CatchStack";
1309 })(BuiltinVar || (BuiltinVar = {}));
1310 class ReadVarExpr extends Expression {
1311 constructor(name, type, sourceSpan) {
1312 super(type, sourceSpan);
1313 if (typeof name === 'string') {
1314 this.name = name;
1315 this.builtin = null;
1316 }
1317 else {
1318 this.name = null;
1319 this.builtin = name;
1320 }
1321 }
1322 isEquivalent(e) {
1323 return e instanceof ReadVarExpr && this.name === e.name && this.builtin === e.builtin;
1324 }
1325 isConstant() {
1326 return false;
1327 }
1328 visitExpression(visitor, context) {
1329 return visitor.visitReadVarExpr(this, context);
1330 }
1331 set(value) {
1332 if (!this.name) {
1333 throw new Error(`Built in variable ${this.builtin} can not be assigned to.`);
1334 }
1335 return new WriteVarExpr(this.name, value, null, this.sourceSpan);
1336 }
1337 }
1338 class TypeofExpr extends Expression {
1339 constructor(expr, type, sourceSpan) {
1340 super(type, sourceSpan);
1341 this.expr = expr;
1342 }
1343 visitExpression(visitor, context) {
1344 return visitor.visitTypeofExpr(this, context);
1345 }
1346 isEquivalent(e) {
1347 return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr);
1348 }
1349 isConstant() {
1350 return this.expr.isConstant();
1351 }
1352 }
1353 class WrappedNodeExpr extends Expression {
1354 constructor(node, type, sourceSpan) {
1355 super(type, sourceSpan);
1356 this.node = node;
1357 }
1358 isEquivalent(e) {
1359 return e instanceof WrappedNodeExpr && this.node === e.node;
1360 }
1361 isConstant() {
1362 return false;
1363 }
1364 visitExpression(visitor, context) {
1365 return visitor.visitWrappedNodeExpr(this, context);
1366 }
1367 }
1368 class WriteVarExpr extends Expression {
1369 constructor(name, value, type, sourceSpan) {
1370 super(type || value.type, sourceSpan);
1371 this.name = name;
1372 this.value = value;
1373 }
1374 isEquivalent(e) {
1375 return e instanceof WriteVarExpr && this.name === e.name && this.value.isEquivalent(e.value);
1376 }
1377 isConstant() {
1378 return false;
1379 }
1380 visitExpression(visitor, context) {
1381 return visitor.visitWriteVarExpr(this, context);
1382 }
1383 toDeclStmt(type, modifiers) {
1384 return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
1385 }
1386 toConstDecl() {
1387 return this.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]);
1388 }
1389 }
1390 class WriteKeyExpr extends Expression {
1391 constructor(receiver, index, value, type, sourceSpan) {
1392 super(type || value.type, sourceSpan);
1393 this.receiver = receiver;
1394 this.index = index;
1395 this.value = value;
1396 }
1397 isEquivalent(e) {
1398 return e instanceof WriteKeyExpr && this.receiver.isEquivalent(e.receiver) &&
1399 this.index.isEquivalent(e.index) && this.value.isEquivalent(e.value);
1400 }
1401 isConstant() {
1402 return false;
1403 }
1404 visitExpression(visitor, context) {
1405 return visitor.visitWriteKeyExpr(this, context);
1406 }
1407 }
1408 class WritePropExpr extends Expression {
1409 constructor(receiver, name, value, type, sourceSpan) {
1410 super(type || value.type, sourceSpan);
1411 this.receiver = receiver;
1412 this.name = name;
1413 this.value = value;
1414 }
1415 isEquivalent(e) {
1416 return e instanceof WritePropExpr && this.receiver.isEquivalent(e.receiver) &&
1417 this.name === e.name && this.value.isEquivalent(e.value);
1418 }
1419 isConstant() {
1420 return false;
1421 }
1422 visitExpression(visitor, context) {
1423 return visitor.visitWritePropExpr(this, context);
1424 }
1425 }
1426 var BuiltinMethod;
1427 (function (BuiltinMethod) {
1428 BuiltinMethod[BuiltinMethod["ConcatArray"] = 0] = "ConcatArray";
1429 BuiltinMethod[BuiltinMethod["SubscribeObservable"] = 1] = "SubscribeObservable";
1430 BuiltinMethod[BuiltinMethod["Bind"] = 2] = "Bind";
1431 })(BuiltinMethod || (BuiltinMethod = {}));
1432 class InvokeFunctionExpr extends Expression {
1433 constructor(fn, args, type, sourceSpan, pure = false) {
1434 super(type, sourceSpan);
1435 this.fn = fn;
1436 this.args = args;
1437 this.pure = pure;
1438 }
1439 isEquivalent(e) {
1440 return e instanceof InvokeFunctionExpr && this.fn.isEquivalent(e.fn) &&
1441 areAllEquivalent(this.args, e.args) && this.pure === e.pure;
1442 }
1443 isConstant() {
1444 return false;
1445 }
1446 visitExpression(visitor, context) {
1447 return visitor.visitInvokeFunctionExpr(this, context);
1448 }
1449 }
1450 class TaggedTemplateExpr extends Expression {
1451 constructor(tag, template, type, sourceSpan) {
1452 super(type, sourceSpan);
1453 this.tag = tag;
1454 this.template = template;
1455 }
1456 isEquivalent(e) {
1457 return e instanceof TaggedTemplateExpr && this.tag.isEquivalent(e.tag) &&
1458 areAllEquivalentPredicate(this.template.elements, e.template.elements, (a, b) => a.text === b.text) &&
1459 areAllEquivalent(this.template.expressions, e.template.expressions);
1460 }
1461 isConstant() {
1462 return false;
1463 }
1464 visitExpression(visitor, context) {
1465 return visitor.visitTaggedTemplateExpr(this, context);
1466 }
1467 }
1468 class InstantiateExpr extends Expression {
1469 constructor(classExpr, args, type, sourceSpan) {
1470 super(type, sourceSpan);
1471 this.classExpr = classExpr;
1472 this.args = args;
1473 }
1474 isEquivalent(e) {
1475 return e instanceof InstantiateExpr && this.classExpr.isEquivalent(e.classExpr) &&
1476 areAllEquivalent(this.args, e.args);
1477 }
1478 isConstant() {
1479 return false;
1480 }
1481 visitExpression(visitor, context) {
1482 return visitor.visitInstantiateExpr(this, context);
1483 }
1484 }
1485 class LiteralExpr extends Expression {
1486 constructor(value, type, sourceSpan) {
1487 super(type, sourceSpan);
1488 this.value = value;
1489 }
1490 isEquivalent(e) {
1491 return e instanceof LiteralExpr && this.value === e.value;
1492 }
1493 isConstant() {
1494 return true;
1495 }
1496 visitExpression(visitor, context) {
1497 return visitor.visitLiteralExpr(this, context);
1498 }
1499 }
1500 class TemplateLiteral {
1501 constructor(elements, expressions) {
1502 this.elements = elements;
1503 this.expressions = expressions;
1504 }
1505 }
1506 class TemplateLiteralElement {
1507 constructor(text, sourceSpan, rawText) {
1508 this.text = text;
1509 this.sourceSpan = sourceSpan;
1510 // If `rawText` is not provided, try to extract the raw string from its
1511 // associated `sourceSpan`. If that is also not available, "fake" the raw
1512 // string instead by escaping the following control sequences:
1513 // - "\" would otherwise indicate that the next character is a control character.
1514 // - "`" and "${" are template string control sequences that would otherwise prematurely
1515 // indicate the end of the template literal element.
1516 this.rawText =
1517 rawText ?? sourceSpan?.toString() ?? escapeForTemplateLiteral(escapeSlashes(text));
1518 }
1519 }
1520 class MessagePiece {
1521 constructor(text, sourceSpan) {
1522 this.text = text;
1523 this.sourceSpan = sourceSpan;
1524 }
1525 }
1526 class LiteralPiece extends MessagePiece {
1527 }
1528 class PlaceholderPiece extends MessagePiece {
1529 }
1530 class LocalizedString extends Expression {
1531 constructor(metaBlock, messageParts, placeHolderNames, expressions, sourceSpan) {
1532 super(STRING_TYPE, sourceSpan);
1533 this.metaBlock = metaBlock;
1534 this.messageParts = messageParts;
1535 this.placeHolderNames = placeHolderNames;
1536 this.expressions = expressions;
1537 }
1538 isEquivalent(e) {
1539 // return e instanceof LocalizedString && this.message === e.message;
1540 return false;
1541 }
1542 isConstant() {
1543 return false;
1544 }
1545 visitExpression(visitor, context) {
1546 return visitor.visitLocalizedString(this, context);
1547 }
1548 /**
1549 * Serialize the given `meta` and `messagePart` into "cooked" and "raw" strings that can be used
1550 * in a `$localize` tagged string. The format of the metadata is the same as that parsed by
1551 * `parseI18nMeta()`.
1552 *
1553 * @param meta The metadata to serialize
1554 * @param messagePart The first part of the tagged string
1555 */
1556 serializeI18nHead() {
1557 const MEANING_SEPARATOR = '|';
1558 const ID_SEPARATOR = '@@';
1559 const LEGACY_ID_INDICATOR = '␟';
1560 let metaBlock = this.metaBlock.description || '';
1561 if (this.metaBlock.meaning) {
1562 metaBlock = `${this.metaBlock.meaning}${MEANING_SEPARATOR}${metaBlock}`;
1563 }
1564 if (this.metaBlock.customId) {
1565 metaBlock = `${metaBlock}${ID_SEPARATOR}${this.metaBlock.customId}`;
1566 }
1567 if (this.metaBlock.legacyIds) {
1568 this.metaBlock.legacyIds.forEach(legacyId => {
1569 metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`;
1570 });
1571 }
1572 return createCookedRawString(metaBlock, this.messageParts[0].text, this.getMessagePartSourceSpan(0));
1573 }
1574 getMessagePartSourceSpan(i) {
1575 return this.messageParts[i]?.sourceSpan ?? this.sourceSpan;
1576 }
1577 getPlaceholderSourceSpan(i) {
1578 return this.placeHolderNames[i]?.sourceSpan ?? this.expressions[i]?.sourceSpan ??
1579 this.sourceSpan;
1580 }
1581 /**
1582 * Serialize the given `placeholderName` and `messagePart` into "cooked" and "raw" strings that
1583 * can be used in a `$localize` tagged string.
1584 *
1585 * @param placeholderName The placeholder name to serialize
1586 * @param messagePart The following message string after this placeholder
1587 */
1588 serializeI18nTemplatePart(partIndex) {
1589 const placeholderName = this.placeHolderNames[partIndex - 1].text;
1590 const messagePart = this.messageParts[partIndex];
1591 return createCookedRawString(placeholderName, messagePart.text, this.getMessagePartSourceSpan(partIndex));
1592 }
1593 }
1594 const escapeSlashes = (str) => str.replace(/\\/g, '\\\\');
1595 const escapeStartingColon = (str) => str.replace(/^:/, '\\:');
1596 const escapeColons = (str) => str.replace(/:/g, '\\:');
1597 const escapeForTemplateLiteral = (str) => str.replace(/`/g, '\\`').replace(/\${/g, '$\\{');
1598 /**
1599 * Creates a `{cooked, raw}` object from the `metaBlock` and `messagePart`.
1600 *
1601 * The `raw` text must have various character sequences escaped:
1602 * * "\" would otherwise indicate that the next character is a control character.
1603 * * "`" and "${" are template string control sequences that would otherwise prematurely indicate
1604 * the end of a message part.
1605 * * ":" inside a metablock would prematurely indicate the end of the metablock.
1606 * * ":" at the start of a messagePart with no metablock would erroneously indicate the start of a
1607 * metablock.
1608 *
1609 * @param metaBlock Any metadata that should be prepended to the string
1610 * @param messagePart The message part of the string
1611 */
1612 function createCookedRawString(metaBlock, messagePart, range) {
1613 if (metaBlock === '') {
1614 return {
1615 cooked: messagePart,
1616 raw: escapeForTemplateLiteral(escapeStartingColon(escapeSlashes(messagePart))),
1617 range,
1618 };
1619 }
1620 else {
1621 return {
1622 cooked: `:${metaBlock}:${messagePart}`,
1623 raw: escapeForTemplateLiteral(`:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`),
1624 range,
1625 };
1626 }
1627 }
1628 class ExternalExpr extends Expression {
1629 constructor(value, type, typeParams = null, sourceSpan) {
1630 super(type, sourceSpan);
1631 this.value = value;
1632 this.typeParams = typeParams;
1633 }
1634 isEquivalent(e) {
1635 return e instanceof ExternalExpr && this.value.name === e.value.name &&
1636 this.value.moduleName === e.value.moduleName && this.value.runtime === e.value.runtime;
1637 }
1638 isConstant() {
1639 return false;
1640 }
1641 visitExpression(visitor, context) {
1642 return visitor.visitExternalExpr(this, context);
1643 }
1644 }
1645 class ExternalReference {
1646 constructor(moduleName, name, runtime) {
1647 this.moduleName = moduleName;
1648 this.name = name;
1649 this.runtime = runtime;
1650 }
1651 }
1652 class ConditionalExpr extends Expression {
1653 constructor(condition, trueCase, falseCase = null, type, sourceSpan) {
1654 super(type || trueCase.type, sourceSpan);
1655 this.condition = condition;
1656 this.falseCase = falseCase;
1657 this.trueCase = trueCase;
1658 }
1659 isEquivalent(e) {
1660 return e instanceof ConditionalExpr && this.condition.isEquivalent(e.condition) &&
1661 this.trueCase.isEquivalent(e.trueCase) && nullSafeIsEquivalent(this.falseCase, e.falseCase);
1662 }
1663 isConstant() {
1664 return false;
1665 }
1666 visitExpression(visitor, context) {
1667 return visitor.visitConditionalExpr(this, context);
1668 }
1669 }
1670 class NotExpr extends Expression {
1671 constructor(condition, sourceSpan) {
1672 super(BOOL_TYPE, sourceSpan);
1673 this.condition = condition;
1674 }
1675 isEquivalent(e) {
1676 return e instanceof NotExpr && this.condition.isEquivalent(e.condition);
1677 }
1678 isConstant() {
1679 return false;
1680 }
1681 visitExpression(visitor, context) {
1682 return visitor.visitNotExpr(this, context);
1683 }
1684 }
1685 class AssertNotNull extends Expression {
1686 constructor(condition, sourceSpan) {
1687 super(condition.type, sourceSpan);
1688 this.condition = condition;
1689 }
1690 isEquivalent(e) {
1691 return e instanceof AssertNotNull && this.condition.isEquivalent(e.condition);
1692 }
1693 isConstant() {
1694 return false;
1695 }
1696 visitExpression(visitor, context) {
1697 return visitor.visitAssertNotNullExpr(this, context);
1698 }
1699 }
1700 class CastExpr extends Expression {
1701 constructor(value, type, sourceSpan) {
1702 super(type, sourceSpan);
1703 this.value = value;
1704 }
1705 isEquivalent(e) {
1706 return e instanceof CastExpr && this.value.isEquivalent(e.value);
1707 }
1708 isConstant() {
1709 return false;
1710 }
1711 visitExpression(visitor, context) {
1712 return visitor.visitCastExpr(this, context);
1713 }
1714 }
1715 class FnParam {
1716 constructor(name, type = null) {
1717 this.name = name;
1718 this.type = type;
1719 }
1720 isEquivalent(param) {
1721 return this.name === param.name;
1722 }
1723 }
1724 class FunctionExpr extends Expression {
1725 constructor(params, statements, type, sourceSpan, name) {
1726 super(type, sourceSpan);
1727 this.params = params;
1728 this.statements = statements;
1729 this.name = name;
1730 }
1731 isEquivalent(e) {
1732 return e instanceof FunctionExpr && areAllEquivalent(this.params, e.params) &&
1733 areAllEquivalent(this.statements, e.statements);
1734 }
1735 isConstant() {
1736 return false;
1737 }
1738 visitExpression(visitor, context) {
1739 return visitor.visitFunctionExpr(this, context);
1740 }
1741 toDeclStmt(name, modifiers) {
1742 return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
1743 }
1744 }
1745 class UnaryOperatorExpr extends Expression {
1746 constructor(operator, expr, type, sourceSpan, parens = true) {
1747 super(type || NUMBER_TYPE, sourceSpan);
1748 this.operator = operator;
1749 this.expr = expr;
1750 this.parens = parens;
1751 }
1752 isEquivalent(e) {
1753 return e instanceof UnaryOperatorExpr && this.operator === e.operator &&
1754 this.expr.isEquivalent(e.expr);
1755 }
1756 isConstant() {
1757 return false;
1758 }
1759 visitExpression(visitor, context) {
1760 return visitor.visitUnaryOperatorExpr(this, context);
1761 }
1762 }
1763 class BinaryOperatorExpr extends Expression {
1764 constructor(operator, lhs, rhs, type, sourceSpan, parens = true) {
1765 super(type || lhs.type, sourceSpan);
1766 this.operator = operator;
1767 this.rhs = rhs;
1768 this.parens = parens;
1769 this.lhs = lhs;
1770 }
1771 isEquivalent(e) {
1772 return e instanceof BinaryOperatorExpr && this.operator === e.operator &&
1773 this.lhs.isEquivalent(e.lhs) && this.rhs.isEquivalent(e.rhs);
1774 }
1775 isConstant() {
1776 return false;
1777 }
1778 visitExpression(visitor, context) {
1779 return visitor.visitBinaryOperatorExpr(this, context);
1780 }
1781 }
1782 class ReadPropExpr extends Expression {
1783 constructor(receiver, name, type, sourceSpan) {
1784 super(type, sourceSpan);
1785 this.receiver = receiver;
1786 this.name = name;
1787 }
1788 isEquivalent(e) {
1789 return e instanceof ReadPropExpr && this.receiver.isEquivalent(e.receiver) &&
1790 this.name === e.name;
1791 }
1792 isConstant() {
1793 return false;
1794 }
1795 visitExpression(visitor, context) {
1796 return visitor.visitReadPropExpr(this, context);
1797 }
1798 set(value) {
1799 return new WritePropExpr(this.receiver, this.name, value, null, this.sourceSpan);
1800 }
1801 }
1802 class ReadKeyExpr extends Expression {
1803 constructor(receiver, index, type, sourceSpan) {
1804 super(type, sourceSpan);
1805 this.receiver = receiver;
1806 this.index = index;
1807 }
1808 isEquivalent(e) {
1809 return e instanceof ReadKeyExpr && this.receiver.isEquivalent(e.receiver) &&
1810 this.index.isEquivalent(e.index);
1811 }
1812 isConstant() {
1813 return false;
1814 }
1815 visitExpression(visitor, context) {
1816 return visitor.visitReadKeyExpr(this, context);
1817 }
1818 set(value) {
1819 return new WriteKeyExpr(this.receiver, this.index, value, null, this.sourceSpan);
1820 }
1821 }
1822 class LiteralArrayExpr extends Expression {
1823 constructor(entries, type, sourceSpan) {
1824 super(type, sourceSpan);
1825 this.entries = entries;
1826 }
1827 isConstant() {
1828 return this.entries.every(e => e.isConstant());
1829 }
1830 isEquivalent(e) {
1831 return e instanceof LiteralArrayExpr && areAllEquivalent(this.entries, e.entries);
1832 }
1833 visitExpression(visitor, context) {
1834 return visitor.visitLiteralArrayExpr(this, context);
1835 }
1836 }
1837 class LiteralMapEntry {
1838 constructor(key, value, quoted) {
1839 this.key = key;
1840 this.value = value;
1841 this.quoted = quoted;
1842 }
1843 isEquivalent(e) {
1844 return this.key === e.key && this.value.isEquivalent(e.value);
1845 }
1846 }
1847 class LiteralMapExpr extends Expression {
1848 constructor(entries, type, sourceSpan) {
1849 super(type, sourceSpan);
1850 this.entries = entries;
1851 this.valueType = null;
1852 if (type) {
1853 this.valueType = type.valueType;
1854 }
1855 }
1856 isEquivalent(e) {
1857 return e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries);
1858 }
1859 isConstant() {
1860 return this.entries.every(e => e.value.isConstant());
1861 }
1862 visitExpression(visitor, context) {
1863 return visitor.visitLiteralMapExpr(this, context);
1864 }
1865 }
1866 new ReadVarExpr(BuiltinVar.This, null, null);
1867 new ReadVarExpr(BuiltinVar.Super, null, null);
1868 new ReadVarExpr(BuiltinVar.CatchError, null, null);
1869 new ReadVarExpr(BuiltinVar.CatchStack, null, null);
1870 const NULL_EXPR = new LiteralExpr(null, null, null);
1871 const TYPED_NULL_EXPR = new LiteralExpr(null, INFERRED_TYPE, null);
1872 //// Statements
1873 var StmtModifier;
1874 (function (StmtModifier) {
1875 StmtModifier[StmtModifier["Final"] = 0] = "Final";
1876 StmtModifier[StmtModifier["Private"] = 1] = "Private";
1877 StmtModifier[StmtModifier["Exported"] = 2] = "Exported";
1878 StmtModifier[StmtModifier["Static"] = 3] = "Static";
1879 })(StmtModifier || (StmtModifier = {}));
1880 class LeadingComment {
1881 constructor(text, multiline, trailingNewline) {
1882 this.text = text;
1883 this.multiline = multiline;
1884 this.trailingNewline = trailingNewline;
1885 }
1886 toString() {
1887 return this.multiline ? ` ${this.text} ` : this.text;
1888 }
1889 }
1890 class JSDocComment extends LeadingComment {
1891 constructor(tags) {
1892 super('', /* multiline */ true, /* trailingNewline */ true);
1893 this.tags = tags;
1894 }
1895 toString() {
1896 return serializeTags(this.tags);
1897 }
1898 }
1899 class Statement {
1900 constructor(modifiers = [], sourceSpan = null, leadingComments) {
1901 this.modifiers = modifiers;
1902 this.sourceSpan = sourceSpan;
1903 this.leadingComments = leadingComments;
1904 }
1905 hasModifier(modifier) {
1906 return this.modifiers.indexOf(modifier) !== -1;
1907 }
1908 addLeadingComment(leadingComment) {
1909 this.leadingComments = this.leadingComments ?? [];
1910 this.leadingComments.push(leadingComment);
1911 }
1912 }
1913 class DeclareVarStmt extends Statement {
1914 constructor(name, value, type, modifiers, sourceSpan, leadingComments) {
1915 super(modifiers, sourceSpan, leadingComments);
1916 this.name = name;
1917 this.value = value;
1918 this.type = type || (value && value.type) || null;
1919 }
1920 isEquivalent(stmt) {
1921 return stmt instanceof DeclareVarStmt && this.name === stmt.name &&
1922 (this.value ? !!stmt.value && this.value.isEquivalent(stmt.value) : !stmt.value);
1923 }
1924 visitStatement(visitor, context) {
1925 return visitor.visitDeclareVarStmt(this, context);
1926 }
1927 }
1928 class DeclareFunctionStmt extends Statement {
1929 constructor(name, params, statements, type, modifiers, sourceSpan, leadingComments) {
1930 super(modifiers, sourceSpan, leadingComments);
1931 this.name = name;
1932 this.params = params;
1933 this.statements = statements;
1934 this.type = type || null;
1935 }
1936 isEquivalent(stmt) {
1937 return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) &&
1938 areAllEquivalent(this.statements, stmt.statements);
1939 }
1940 visitStatement(visitor, context) {
1941 return visitor.visitDeclareFunctionStmt(this, context);
1942 }
1943 }
1944 class ExpressionStatement extends Statement {
1945 constructor(expr, sourceSpan, leadingComments) {
1946 super([], sourceSpan, leadingComments);
1947 this.expr = expr;
1948 }
1949 isEquivalent(stmt) {
1950 return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr);
1951 }
1952 visitStatement(visitor, context) {
1953 return visitor.visitExpressionStmt(this, context);
1954 }
1955 }
1956 class ReturnStatement extends Statement {
1957 constructor(value, sourceSpan = null, leadingComments) {
1958 super([], sourceSpan, leadingComments);
1959 this.value = value;
1960 }
1961 isEquivalent(stmt) {
1962 return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value);
1963 }
1964 visitStatement(visitor, context) {
1965 return visitor.visitReturnStmt(this, context);
1966 }
1967 }
1968 class IfStmt extends Statement {
1969 constructor(condition, trueCase, falseCase = [], sourceSpan, leadingComments) {
1970 super([], sourceSpan, leadingComments);
1971 this.condition = condition;
1972 this.trueCase = trueCase;
1973 this.falseCase = falseCase;
1974 }
1975 isEquivalent(stmt) {
1976 return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) &&
1977 areAllEquivalent(this.trueCase, stmt.trueCase) &&
1978 areAllEquivalent(this.falseCase, stmt.falseCase);
1979 }
1980 visitStatement(visitor, context) {
1981 return visitor.visitIfStmt(this, context);
1982 }
1983 }
1984 function jsDocComment(tags = []) {
1985 return new JSDocComment(tags);
1986 }
1987 function variable(name, type, sourceSpan) {
1988 return new ReadVarExpr(name, type, sourceSpan);
1989 }
1990 function importExpr(id, typeParams = null, sourceSpan) {
1991 return new ExternalExpr(id, null, typeParams, sourceSpan);
1992 }
1993 function expressionType(expr, typeModifiers, typeParams) {
1994 return new ExpressionType(expr, typeModifiers, typeParams);
1995 }
1996 function typeofExpr(expr) {
1997 return new TypeofExpr(expr);
1998 }
1999 function literalArr(values, type, sourceSpan) {
2000 return new LiteralArrayExpr(values, type, sourceSpan);
2001 }
2002 function literalMap(values, type = null) {
2003 return new LiteralMapExpr(values.map(e => new LiteralMapEntry(e.key, e.value, e.quoted)), type, null);
2004 }
2005 function not(expr, sourceSpan) {
2006 return new NotExpr(expr, sourceSpan);
2007 }
2008 function assertNotNull(expr, sourceSpan) {
2009 return new AssertNotNull(expr, sourceSpan);
2010 }
2011 function fn(params, body, type, sourceSpan, name) {
2012 return new FunctionExpr(params, body, type, sourceSpan, name);
2013 }
2014 function ifStmt(condition, thenClause, elseClause, sourceSpan, leadingComments) {
2015 return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments);
2016 }
2017 function taggedTemplate(tag, template, type, sourceSpan) {
2018 return new TaggedTemplateExpr(tag, template, type, sourceSpan);
2019 }
2020 function literal$1(value, type, sourceSpan) {
2021 return new LiteralExpr(value, type, sourceSpan);
2022 }
2023 function localizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan) {
2024 return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan);
2025 }
2026 function isNull(exp) {
2027 return exp instanceof LiteralExpr && exp.value === null;
2028 }
2029 /*
2030 * Serializes a `Tag` into a string.
2031 * Returns a string like " @foo {bar} baz" (note the leading whitespace before `@foo`).
2032 */
2033 function tagToString(tag) {
2034 let out = '';
2035 if (tag.tagName) {
2036 out += ` @${tag.tagName}`;
2037 }
2038 if (tag.text) {
2039 if (tag.text.match(/\/\*|\*\//)) {
2040 throw new Error('JSDoc text cannot contain "/*" and "*/"');
2041 }
2042 out += ' ' + tag.text.replace(/@/g, '\\@');
2043 }
2044 return out;
2045 }
2046 function serializeTags(tags) {
2047 if (tags.length === 0)
2048 return '';
2049 if (tags.length === 1 && tags[0].tagName && !tags[0].text) {
2050 // The JSDOC comment is a single simple tag: e.g `/** @tagname */`.
2051 return `*${tagToString(tags[0])} `;
2052 }
2053 let out = '*\n';
2054 for (const tag of tags) {
2055 out += ' *';
2056 // If the tagToString is multi-line, insert " * " prefixes on lines.
2057 out += tagToString(tag).replace(/\n/g, '\n * ');
2058 out += '\n';
2059 }
2060 out += ' ';
2061 return out;
2062 }
2063
2064 /**
2065 * @license
2066 * Copyright Google LLC All Rights Reserved.
2067 *
2068 * Use of this source code is governed by an MIT-style license that can be
2069 * found in the LICENSE file at https://angular.io/license
2070 */
2071 const CONSTANT_PREFIX = '_c';
2072 /**
2073 * `ConstantPool` tries to reuse literal factories when two or more literals are identical.
2074 * We determine whether literals are identical by creating a key out of their AST using the
2075 * `KeyVisitor`. This constant is used to replace dynamic expressions which can't be safely
2076 * converted into a key. E.g. given an expression `{foo: bar()}`, since we don't know what
2077 * the result of `bar` will be, we create a key that looks like `{foo: <unknown>}`. Note
2078 * that we use a variable, rather than something like `null` in order to avoid collisions.
2079 */
2080 const UNKNOWN_VALUE_KEY = variable('<unknown>');
2081 /**
2082 * Context to use when producing a key.
2083 *
2084 * This ensures we see the constant not the reference variable when producing
2085 * a key.
2086 */
2087 const KEY_CONTEXT = {};
2088 /**
2089 * Generally all primitive values are excluded from the `ConstantPool`, but there is an exclusion
2090 * for strings that reach a certain length threshold. This constant defines the length threshold for
2091 * strings.
2092 */
2093 const POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS = 50;
2094 /**
2095 * A node that is a place-holder that allows the node to be replaced when the actual
2096 * node is known.
2097 *
2098 * This allows the constant pool to change an expression from a direct reference to
2099 * a constant to a shared constant. It returns a fix-up node that is later allowed to
2100 * change the referenced expression.
2101 */
2102 class FixupExpression extends Expression {
2103 constructor(resolved) {
2104 super(resolved.type);
2105 this.resolved = resolved;
2106 this.original = resolved;
2107 }
2108 visitExpression(visitor, context) {
2109 if (context === KEY_CONTEXT) {
2110 // When producing a key we want to traverse the constant not the
2111 // variable used to refer to it.
2112 return this.original.visitExpression(visitor, context);
2113 }
2114 else {
2115 return this.resolved.visitExpression(visitor, context);
2116 }
2117 }
2118 isEquivalent(e) {
2119 return e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved);
2120 }
2121 isConstant() {
2122 return true;
2123 }
2124 fixup(expression) {
2125 this.resolved = expression;
2126 this.shared = true;
2127 }
2128 }
2129 /**
2130 * A constant pool allows a code emitter to share constant in an output context.
2131 *
2132 * The constant pool also supports sharing access to ivy definitions references.
2133 */
2134 class ConstantPool {
2135 constructor(isClosureCompilerEnabled = false) {
2136 this.isClosureCompilerEnabled = isClosureCompilerEnabled;
2137 this.statements = [];
2138 this.literals = new Map();
2139 this.literalFactories = new Map();
2140 this.injectorDefinitions = new Map();
2141 this.directiveDefinitions = new Map();
2142 this.componentDefinitions = new Map();
2143 this.pipeDefinitions = new Map();
2144 this.nextNameIndex = 0;
2145 }
2146 getConstLiteral(literal, forceShared) {
2147 if ((literal instanceof LiteralExpr && !isLongStringLiteral(literal)) ||
2148 literal instanceof FixupExpression) {
2149 // Do no put simple literals into the constant pool or try to produce a constant for a
2150 // reference to a constant.
2151 return literal;
2152 }
2153 const key = this.keyOf(literal);
2154 let fixup = this.literals.get(key);
2155 let newValue = false;
2156 if (!fixup) {
2157 fixup = new FixupExpression(literal);
2158 this.literals.set(key, fixup);
2159 newValue = true;
2160 }
2161 if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
2162 // Replace the expression with a variable
2163 const name = this.freshName();
2164 let definition;
2165 let usage;
2166 if (this.isClosureCompilerEnabled && isLongStringLiteral(literal)) {
2167 // For string literals, Closure will **always** inline the string at
2168 // **all** usages, duplicating it each time. For large strings, this
2169 // unnecessarily bloats bundle size. To work around this restriction, we
2170 // wrap the string in a function, and call that function for each usage.
2171 // This tricks Closure into using inline logic for functions instead of
2172 // string literals. Function calls are only inlined if the body is small
2173 // enough to be worth it. By doing this, very large strings will be
2174 // shared across multiple usages, rather than duplicating the string at
2175 // each usage site.
2176 //
2177 // const myStr = function() { return "very very very long string"; };
2178 // const usage1 = myStr();
2179 // const usage2 = myStr();
2180 definition = variable(name).set(new FunctionExpr([], // Params.
2181 [
2182 // Statements.
2183 new ReturnStatement(literal),
2184 ]));
2185 usage = variable(name).callFn([]);
2186 }
2187 else {
2188 // Just declare and use the variable directly, without a function call
2189 // indirection. This saves a few bytes and avoids an unncessary call.
2190 definition = variable(name).set(literal);
2191 usage = variable(name);
2192 }
2193 this.statements.push(definition.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]));
2194 fixup.fixup(usage);
2195 }
2196 return fixup;
2197 }
2198 getDefinition(type, kind, ctx, forceShared = false) {
2199 const definitions = this.definitionsOf(kind);
2200 let fixup = definitions.get(type);
2201 let newValue = false;
2202 if (!fixup) {
2203 const property = this.propertyNameOf(kind);
2204 fixup = new FixupExpression(ctx.importExpr(type).prop(property));
2205 definitions.set(type, fixup);
2206 newValue = true;
2207 }
2208 if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
2209 const name = this.freshName();
2210 this.statements.push(variable(name).set(fixup.resolved).toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]));
2211 fixup.fixup(variable(name));
2212 }
2213 return fixup;
2214 }
2215 getLiteralFactory(literal) {
2216 // Create a pure function that builds an array of a mix of constant and variable expressions
2217 if (literal instanceof LiteralArrayExpr) {
2218 const argumentsForKey = literal.entries.map(e => e.isConstant() ? e : UNKNOWN_VALUE_KEY);
2219 const key = this.keyOf(literalArr(argumentsForKey));
2220 return this._getLiteralFactory(key, literal.entries, entries => literalArr(entries));
2221 }
2222 else {
2223 const expressionForKey = literalMap(literal.entries.map(e => ({
2224 key: e.key,
2225 value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY,
2226 quoted: e.quoted
2227 })));
2228 const key = this.keyOf(expressionForKey);
2229 return this._getLiteralFactory(key, literal.entries.map(e => e.value), entries => literalMap(entries.map((value, index) => ({
2230 key: literal.entries[index].key,
2231 value,
2232 quoted: literal.entries[index].quoted
2233 }))));
2234 }
2235 }
2236 _getLiteralFactory(key, values, resultMap) {
2237 let literalFactory = this.literalFactories.get(key);
2238 const literalFactoryArguments = values.filter((e => !e.isConstant()));
2239 if (!literalFactory) {
2240 const resultExpressions = values.map((e, index) => e.isConstant() ? this.getConstLiteral(e, true) : variable(`a${index}`));
2241 const parameters = resultExpressions.filter(isVariable).map(e => new FnParam(e.name, DYNAMIC_TYPE));
2242 const pureFunctionDeclaration = fn(parameters, [new ReturnStatement(resultMap(resultExpressions))], INFERRED_TYPE);
2243 const name = this.freshName();
2244 this.statements.push(variable(name).set(pureFunctionDeclaration).toDeclStmt(INFERRED_TYPE, [
2245 StmtModifier.Final
2246 ]));
2247 literalFactory = variable(name);
2248 this.literalFactories.set(key, literalFactory);
2249 }
2250 return { literalFactory, literalFactoryArguments };
2251 }
2252 /**
2253 * Produce a unique name.
2254 *
2255 * The name might be unique among different prefixes if any of the prefixes end in
2256 * a digit so the prefix should be a constant string (not based on user input) and
2257 * must not end in a digit.
2258 */
2259 uniqueName(prefix) {
2260 return `${prefix}${this.nextNameIndex++}`;
2261 }
2262 definitionsOf(kind) {
2263 switch (kind) {
2264 case 2 /* Component */:
2265 return this.componentDefinitions;
2266 case 1 /* Directive */:
2267 return this.directiveDefinitions;
2268 case 0 /* Injector */:
2269 return this.injectorDefinitions;
2270 case 3 /* Pipe */:
2271 return this.pipeDefinitions;
2272 }
2273 }
2274 propertyNameOf(kind) {
2275 switch (kind) {
2276 case 2 /* Component */:
2277 return 'ɵcmp';
2278 case 1 /* Directive */:
2279 return 'ɵdir';
2280 case 0 /* Injector */:
2281 return 'ɵinj';
2282 case 3 /* Pipe */:
2283 return 'ɵpipe';
2284 }
2285 }
2286 freshName() {
2287 return this.uniqueName(CONSTANT_PREFIX);
2288 }
2289 keyOf(expression) {
2290 return expression.visitExpression(new KeyVisitor(), KEY_CONTEXT);
2291 }
2292 }
2293 /**
2294 * Visitor used to determine if 2 expressions are equivalent and can be shared in the
2295 * `ConstantPool`.
2296 *
2297 * When the id (string) generated by the visitor is equal, expressions are considered equivalent.
2298 */
2299 class KeyVisitor {
2300 constructor() {
2301 this.visitWrappedNodeExpr = invalid$1;
2302 this.visitWriteVarExpr = invalid$1;
2303 this.visitWriteKeyExpr = invalid$1;
2304 this.visitWritePropExpr = invalid$1;
2305 this.visitInvokeFunctionExpr = invalid$1;
2306 this.visitTaggedTemplateExpr = invalid$1;
2307 this.visitInstantiateExpr = invalid$1;
2308 this.visitConditionalExpr = invalid$1;
2309 this.visitNotExpr = invalid$1;
2310 this.visitAssertNotNullExpr = invalid$1;
2311 this.visitCastExpr = invalid$1;
2312 this.visitFunctionExpr = invalid$1;
2313 this.visitUnaryOperatorExpr = invalid$1;
2314 this.visitBinaryOperatorExpr = invalid$1;
2315 this.visitReadPropExpr = invalid$1;
2316 this.visitReadKeyExpr = invalid$1;
2317 this.visitCommaExpr = invalid$1;
2318 this.visitLocalizedString = invalid$1;
2319 }
2320 visitLiteralExpr(ast) {
2321 return `${typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value}`;
2322 }
2323 visitLiteralArrayExpr(ast, context) {
2324 return `[${ast.entries.map(entry => entry.visitExpression(this, context)).join(',')}]`;
2325 }
2326 visitLiteralMapExpr(ast, context) {
2327 const mapKey = (entry) => {
2328 const quote = entry.quoted ? '"' : '';
2329 return `${quote}${entry.key}${quote}`;
2330 };
2331 const mapEntry = (entry) => `${mapKey(entry)}:${entry.value.visitExpression(this, context)}`;
2332 return `{${ast.entries.map(mapEntry).join(',')}`;
2333 }
2334 visitExternalExpr(ast) {
2335 return ast.value.moduleName ? `EX:${ast.value.moduleName}:${ast.value.name}` :
2336 `EX:${ast.value.runtime.name}`;
2337 }
2338 visitReadVarExpr(node) {
2339 return `VAR:${node.name}`;
2340 }
2341 visitTypeofExpr(node, context) {
2342 return `TYPEOF:${node.expr.visitExpression(this, context)}`;
2343 }
2344 }
2345 function invalid$1(arg) {
2346 throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
2347 }
2348 function isVariable(e) {
2349 return e instanceof ReadVarExpr;
2350 }
2351 function isLongStringLiteral(expr) {
2352 return expr instanceof LiteralExpr && typeof expr.value === 'string' &&
2353 expr.value.length >= POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS;
2354 }
2355
2356 /**
2357 * @license
2358 * Copyright Google LLC All Rights Reserved.
2359 *
2360 * Use of this source code is governed by an MIT-style license that can be
2361 * found in the LICENSE file at https://angular.io/license
2362 */
2363 const CORE$1 = '@angular/core';
2364 class Identifiers$1 {
2365 }
2366 /* Methods */
2367 Identifiers$1.NEW_METHOD = 'factory';
2368 Identifiers$1.TRANSFORM_METHOD = 'transform';
2369 Identifiers$1.PATCH_DEPS = 'patchedDeps';
2370 Identifiers$1.core = { name: null, moduleName: CORE$1 };
2371 /* Instructions */
2372 Identifiers$1.namespaceHTML = { name: 'ɵɵnamespaceHTML', moduleName: CORE$1 };
2373 Identifiers$1.namespaceMathML = { name: 'ɵɵnamespaceMathML', moduleName: CORE$1 };
2374 Identifiers$1.namespaceSVG = { name: 'ɵɵnamespaceSVG', moduleName: CORE$1 };
2375 Identifiers$1.element = { name: 'ɵɵelement', moduleName: CORE$1 };
2376 Identifiers$1.elementStart = { name: 'ɵɵelementStart', moduleName: CORE$1 };
2377 Identifiers$1.elementEnd = { name: 'ɵɵelementEnd', moduleName: CORE$1 };
2378 Identifiers$1.advance = { name: 'ɵɵadvance', moduleName: CORE$1 };
2379 Identifiers$1.syntheticHostProperty = { name: 'ɵɵsyntheticHostProperty', moduleName: CORE$1 };
2380 Identifiers$1.syntheticHostListener = { name: 'ɵɵsyntheticHostListener', moduleName: CORE$1 };
2381 Identifiers$1.attribute = { name: 'ɵɵattribute', moduleName: CORE$1 };
2382 Identifiers$1.attributeInterpolate1 = { name: 'ɵɵattributeInterpolate1', moduleName: CORE$1 };
2383 Identifiers$1.attributeInterpolate2 = { name: 'ɵɵattributeInterpolate2', moduleName: CORE$1 };
2384 Identifiers$1.attributeInterpolate3 = { name: 'ɵɵattributeInterpolate3', moduleName: CORE$1 };
2385 Identifiers$1.attributeInterpolate4 = { name: 'ɵɵattributeInterpolate4', moduleName: CORE$1 };
2386 Identifiers$1.attributeInterpolate5 = { name: 'ɵɵattributeInterpolate5', moduleName: CORE$1 };
2387 Identifiers$1.attributeInterpolate6 = { name: 'ɵɵattributeInterpolate6', moduleName: CORE$1 };
2388 Identifiers$1.attributeInterpolate7 = { name: 'ɵɵattributeInterpolate7', moduleName: CORE$1 };
2389 Identifiers$1.attributeInterpolate8 = { name: 'ɵɵattributeInterpolate8', moduleName: CORE$1 };
2390 Identifiers$1.attributeInterpolateV = { name: 'ɵɵattributeInterpolateV', moduleName: CORE$1 };
2391 Identifiers$1.classProp = { name: 'ɵɵclassProp', moduleName: CORE$1 };
2392 Identifiers$1.elementContainerStart = { name: 'ɵɵelementContainerStart', moduleName: CORE$1 };
2393 Identifiers$1.elementContainerEnd = { name: 'ɵɵelementContainerEnd', moduleName: CORE$1 };
2394 Identifiers$1.elementContainer = { name: 'ɵɵelementContainer', moduleName: CORE$1 };
2395 Identifiers$1.styleMap = { name: 'ɵɵstyleMap', moduleName: CORE$1 };
2396 Identifiers$1.styleMapInterpolate1 = { name: 'ɵɵstyleMapInterpolate1', moduleName: CORE$1 };
2397 Identifiers$1.styleMapInterpolate2 = { name: 'ɵɵstyleMapInterpolate2', moduleName: CORE$1 };
2398 Identifiers$1.styleMapInterpolate3 = { name: 'ɵɵstyleMapInterpolate3', moduleName: CORE$1 };
2399 Identifiers$1.styleMapInterpolate4 = { name: 'ɵɵstyleMapInterpolate4', moduleName: CORE$1 };
2400 Identifiers$1.styleMapInterpolate5 = { name: 'ɵɵstyleMapInterpolate5', moduleName: CORE$1 };
2401 Identifiers$1.styleMapInterpolate6 = { name: 'ɵɵstyleMapInterpolate6', moduleName: CORE$1 };
2402 Identifiers$1.styleMapInterpolate7 = { name: 'ɵɵstyleMapInterpolate7', moduleName: CORE$1 };
2403 Identifiers$1.styleMapInterpolate8 = { name: 'ɵɵstyleMapInterpolate8', moduleName: CORE$1 };
2404 Identifiers$1.styleMapInterpolateV = { name: 'ɵɵstyleMapInterpolateV', moduleName: CORE$1 };
2405 Identifiers$1.classMap = { name: 'ɵɵclassMap', moduleName: CORE$1 };
2406 Identifiers$1.classMapInterpolate1 = { name: 'ɵɵclassMapInterpolate1', moduleName: CORE$1 };
2407 Identifiers$1.classMapInterpolate2 = { name: 'ɵɵclassMapInterpolate2', moduleName: CORE$1 };
2408 Identifiers$1.classMapInterpolate3 = { name: 'ɵɵclassMapInterpolate3', moduleName: CORE$1 };
2409 Identifiers$1.classMapInterpolate4 = { name: 'ɵɵclassMapInterpolate4', moduleName: CORE$1 };
2410 Identifiers$1.classMapInterpolate5 = { name: 'ɵɵclassMapInterpolate5', moduleName: CORE$1 };
2411 Identifiers$1.classMapInterpolate6 = { name: 'ɵɵclassMapInterpolate6', moduleName: CORE$1 };
2412 Identifiers$1.classMapInterpolate7 = { name: 'ɵɵclassMapInterpolate7', moduleName: CORE$1 };
2413 Identifiers$1.classMapInterpolate8 = { name: 'ɵɵclassMapInterpolate8', moduleName: CORE$1 };
2414 Identifiers$1.classMapInterpolateV = { name: 'ɵɵclassMapInterpolateV', moduleName: CORE$1 };
2415 Identifiers$1.styleProp = { name: 'ɵɵstyleProp', moduleName: CORE$1 };
2416 Identifiers$1.stylePropInterpolate1 = { name: 'ɵɵstylePropInterpolate1', moduleName: CORE$1 };
2417 Identifiers$1.stylePropInterpolate2 = { name: 'ɵɵstylePropInterpolate2', moduleName: CORE$1 };
2418 Identifiers$1.stylePropInterpolate3 = { name: 'ɵɵstylePropInterpolate3', moduleName: CORE$1 };
2419 Identifiers$1.stylePropInterpolate4 = { name: 'ɵɵstylePropInterpolate4', moduleName: CORE$1 };
2420 Identifiers$1.stylePropInterpolate5 = { name: 'ɵɵstylePropInterpolate5', moduleName: CORE$1 };
2421 Identifiers$1.stylePropInterpolate6 = { name: 'ɵɵstylePropInterpolate6', moduleName: CORE$1 };
2422 Identifiers$1.stylePropInterpolate7 = { name: 'ɵɵstylePropInterpolate7', moduleName: CORE$1 };
2423 Identifiers$1.stylePropInterpolate8 = { name: 'ɵɵstylePropInterpolate8', moduleName: CORE$1 };
2424 Identifiers$1.stylePropInterpolateV = { name: 'ɵɵstylePropInterpolateV', moduleName: CORE$1 };
2425 Identifiers$1.nextContext = { name: 'ɵɵnextContext', moduleName: CORE$1 };
2426 Identifiers$1.templateCreate = { name: 'ɵɵtemplate', moduleName: CORE$1 };
2427 Identifiers$1.text = { name: 'ɵɵtext', moduleName: CORE$1 };
2428 Identifiers$1.enableBindings = { name: 'ɵɵenableBindings', moduleName: CORE$1 };
2429 Identifiers$1.disableBindings = { name: 'ɵɵdisableBindings', moduleName: CORE$1 };
2430 Identifiers$1.getCurrentView = { name: 'ɵɵgetCurrentView', moduleName: CORE$1 };
2431 Identifiers$1.textInterpolate = { name: 'ɵɵtextInterpolate', moduleName: CORE$1 };
2432 Identifiers$1.textInterpolate1 = { name: 'ɵɵtextInterpolate1', moduleName: CORE$1 };
2433 Identifiers$1.textInterpolate2 = { name: 'ɵɵtextInterpolate2', moduleName: CORE$1 };
2434 Identifiers$1.textInterpolate3 = { name: 'ɵɵtextInterpolate3', moduleName: CORE$1 };
2435 Identifiers$1.textInterpolate4 = { name: 'ɵɵtextInterpolate4', moduleName: CORE$1 };
2436 Identifiers$1.textInterpolate5 = { name: 'ɵɵtextInterpolate5', moduleName: CORE$1 };
2437 Identifiers$1.textInterpolate6 = { name: 'ɵɵtextInterpolate6', moduleName: CORE$1 };
2438 Identifiers$1.textInterpolate7 = { name: 'ɵɵtextInterpolate7', moduleName: CORE$1 };
2439 Identifiers$1.textInterpolate8 = { name: 'ɵɵtextInterpolate8', moduleName: CORE$1 };
2440 Identifiers$1.textInterpolateV = { name: 'ɵɵtextInterpolateV', moduleName: CORE$1 };
2441 Identifiers$1.restoreView = { name: 'ɵɵrestoreView', moduleName: CORE$1 };
2442 Identifiers$1.pureFunction0 = { name: 'ɵɵpureFunction0', moduleName: CORE$1 };
2443 Identifiers$1.pureFunction1 = { name: 'ɵɵpureFunction1', moduleName: CORE$1 };
2444 Identifiers$1.pureFunction2 = { name: 'ɵɵpureFunction2', moduleName: CORE$1 };
2445 Identifiers$1.pureFunction3 = { name: 'ɵɵpureFunction3', moduleName: CORE$1 };
2446 Identifiers$1.pureFunction4 = { name: 'ɵɵpureFunction4', moduleName: CORE$1 };
2447 Identifiers$1.pureFunction5 = { name: 'ɵɵpureFunction5', moduleName: CORE$1 };
2448 Identifiers$1.pureFunction6 = { name: 'ɵɵpureFunction6', moduleName: CORE$1 };
2449 Identifiers$1.pureFunction7 = { name: 'ɵɵpureFunction7', moduleName: CORE$1 };
2450 Identifiers$1.pureFunction8 = { name: 'ɵɵpureFunction8', moduleName: CORE$1 };
2451 Identifiers$1.pureFunctionV = { name: 'ɵɵpureFunctionV', moduleName: CORE$1 };
2452 Identifiers$1.pipeBind1 = { name: 'ɵɵpipeBind1', moduleName: CORE$1 };
2453 Identifiers$1.pipeBind2 = { name: 'ɵɵpipeBind2', moduleName: CORE$1 };
2454 Identifiers$1.pipeBind3 = { name: 'ɵɵpipeBind3', moduleName: CORE$1 };
2455 Identifiers$1.pipeBind4 = { name: 'ɵɵpipeBind4', moduleName: CORE$1 };
2456 Identifiers$1.pipeBindV = { name: 'ɵɵpipeBindV', moduleName: CORE$1 };
2457 Identifiers$1.hostProperty = { name: 'ɵɵhostProperty', moduleName: CORE$1 };
2458 Identifiers$1.property = { name: 'ɵɵproperty', moduleName: CORE$1 };
2459 Identifiers$1.propertyInterpolate = { name: 'ɵɵpropertyInterpolate', moduleName: CORE$1 };
2460 Identifiers$1.propertyInterpolate1 = { name: 'ɵɵpropertyInterpolate1', moduleName: CORE$1 };
2461 Identifiers$1.propertyInterpolate2 = { name: 'ɵɵpropertyInterpolate2', moduleName: CORE$1 };
2462 Identifiers$1.propertyInterpolate3 = { name: 'ɵɵpropertyInterpolate3', moduleName: CORE$1 };
2463 Identifiers$1.propertyInterpolate4 = { name: 'ɵɵpropertyInterpolate4', moduleName: CORE$1 };
2464 Identifiers$1.propertyInterpolate5 = { name: 'ɵɵpropertyInterpolate5', moduleName: CORE$1 };
2465 Identifiers$1.propertyInterpolate6 = { name: 'ɵɵpropertyInterpolate6', moduleName: CORE$1 };
2466 Identifiers$1.propertyInterpolate7 = { name: 'ɵɵpropertyInterpolate7', moduleName: CORE$1 };
2467 Identifiers$1.propertyInterpolate8 = { name: 'ɵɵpropertyInterpolate8', moduleName: CORE$1 };
2468 Identifiers$1.propertyInterpolateV = { name: 'ɵɵpropertyInterpolateV', moduleName: CORE$1 };
2469 Identifiers$1.i18n = { name: 'ɵɵi18n', moduleName: CORE$1 };
2470 Identifiers$1.i18nAttributes = { name: 'ɵɵi18nAttributes', moduleName: CORE$1 };
2471 Identifiers$1.i18nExp = { name: 'ɵɵi18nExp', moduleName: CORE$1 };
2472 Identifiers$1.i18nStart = { name: 'ɵɵi18nStart', moduleName: CORE$1 };
2473 Identifiers$1.i18nEnd = { name: 'ɵɵi18nEnd', moduleName: CORE$1 };
2474 Identifiers$1.i18nApply = { name: 'ɵɵi18nApply', moduleName: CORE$1 };
2475 Identifiers$1.i18nPostprocess = { name: 'ɵɵi18nPostprocess', moduleName: CORE$1 };
2476 Identifiers$1.pipe = { name: 'ɵɵpipe', moduleName: CORE$1 };
2477 Identifiers$1.projection = { name: 'ɵɵprojection', moduleName: CORE$1 };
2478 Identifiers$1.projectionDef = { name: 'ɵɵprojectionDef', moduleName: CORE$1 };
2479 Identifiers$1.reference = { name: 'ɵɵreference', moduleName: CORE$1 };
2480 Identifiers$1.inject = { name: 'ɵɵinject', moduleName: CORE$1 };
2481 Identifiers$1.injectAttribute = { name: 'ɵɵinjectAttribute', moduleName: CORE$1 };
2482 Identifiers$1.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE$1 };
2483 Identifiers$1.invalidFactory = { name: 'ɵɵinvalidFactory', moduleName: CORE$1 };
2484 Identifiers$1.invalidFactoryDep = { name: 'ɵɵinvalidFactoryDep', moduleName: CORE$1 };
2485 Identifiers$1.templateRefExtractor = { name: 'ɵɵtemplateRefExtractor', moduleName: CORE$1 };
2486 Identifiers$1.forwardRef = { name: 'forwardRef', moduleName: CORE$1 };
2487 Identifiers$1.resolveForwardRef = { name: 'resolveForwardRef', moduleName: CORE$1 };
2488 Identifiers$1.ɵɵdefineInjectable = { name: 'ɵɵdefineInjectable', moduleName: CORE$1 };
2489 Identifiers$1.declareInjectable = { name: 'ɵɵngDeclareInjectable', moduleName: CORE$1 };
2490 Identifiers$1.InjectableDeclaration = { name: 'ɵɵInjectableDeclaration', moduleName: CORE$1 };
2491 Identifiers$1.resolveWindow = { name: 'ɵɵresolveWindow', moduleName: CORE$1 };
2492 Identifiers$1.resolveDocument = { name: 'ɵɵresolveDocument', moduleName: CORE$1 };
2493 Identifiers$1.resolveBody = { name: 'ɵɵresolveBody', moduleName: CORE$1 };
2494 Identifiers$1.defineComponent = { name: 'ɵɵdefineComponent', moduleName: CORE$1 };
2495 Identifiers$1.declareComponent = { name: 'ɵɵngDeclareComponent', moduleName: CORE$1 };
2496 Identifiers$1.setComponentScope = { name: 'ɵɵsetComponentScope', moduleName: CORE$1 };
2497 Identifiers$1.ChangeDetectionStrategy = {
2498 name: 'ChangeDetectionStrategy',
2499 moduleName: CORE$1,
2500 };
2501 Identifiers$1.ViewEncapsulation = {
2502 name: 'ViewEncapsulation',
2503 moduleName: CORE$1,
2504 };
2505 Identifiers$1.ComponentDeclaration = {
2506 name: 'ɵɵComponentDeclaration',
2507 moduleName: CORE$1,
2508 };
2509 Identifiers$1.FactoryDeclaration = {
2510 name: 'ɵɵFactoryDeclaration',
2511 moduleName: CORE$1,
2512 };
2513 Identifiers$1.declareFactory = { name: 'ɵɵngDeclareFactory', moduleName: CORE$1 };
2514 Identifiers$1.FactoryTarget = { name: 'ɵɵFactoryTarget', moduleName: CORE$1 };
2515 Identifiers$1.defineDirective = { name: 'ɵɵdefineDirective', moduleName: CORE$1 };
2516 Identifiers$1.declareDirective = { name: 'ɵɵngDeclareDirective', moduleName: CORE$1 };
2517 Identifiers$1.DirectiveDeclaration = {
2518 name: 'ɵɵDirectiveDeclaration',
2519 moduleName: CORE$1,
2520 };
2521 Identifiers$1.InjectorDef = { name: 'ɵɵInjectorDef', moduleName: CORE$1 };
2522 Identifiers$1.InjectorDeclaration = { name: 'ɵɵInjectorDeclaration', moduleName: CORE$1 };
2523 Identifiers$1.defineInjector = { name: 'ɵɵdefineInjector', moduleName: CORE$1 };
2524 Identifiers$1.declareInjector = { name: 'ɵɵngDeclareInjector', moduleName: CORE$1 };
2525 Identifiers$1.NgModuleDeclaration = {
2526 name: 'ɵɵNgModuleDeclaration',
2527 moduleName: CORE$1,
2528 };
2529 Identifiers$1.ModuleWithProviders = {
2530 name: 'ModuleWithProviders',
2531 moduleName: CORE$1,
2532 };
2533 Identifiers$1.defineNgModule = { name: 'ɵɵdefineNgModule', moduleName: CORE$1 };
2534 Identifiers$1.declareNgModule = { name: 'ɵɵngDeclareNgModule', moduleName: CORE$1 };
2535 Identifiers$1.setNgModuleScope = { name: 'ɵɵsetNgModuleScope', moduleName: CORE$1 };
2536 Identifiers$1.PipeDeclaration = { name: 'ɵɵPipeDeclaration', moduleName: CORE$1 };
2537 Identifiers$1.definePipe = { name: 'ɵɵdefinePipe', moduleName: CORE$1 };
2538 Identifiers$1.declarePipe = { name: 'ɵɵngDeclarePipe', moduleName: CORE$1 };
2539 Identifiers$1.declareClassMetadata = { name: 'ɵɵngDeclareClassMetadata', moduleName: CORE$1 };
2540 Identifiers$1.setClassMetadata = { name: 'ɵsetClassMetadata', moduleName: CORE$1 };
2541 Identifiers$1.queryRefresh = { name: 'ɵɵqueryRefresh', moduleName: CORE$1 };
2542 Identifiers$1.viewQuery = { name: 'ɵɵviewQuery', moduleName: CORE$1 };
2543 Identifiers$1.loadQuery = { name: 'ɵɵloadQuery', moduleName: CORE$1 };
2544 Identifiers$1.contentQuery = { name: 'ɵɵcontentQuery', moduleName: CORE$1 };
2545 Identifiers$1.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE$1 };
2546 Identifiers$1.InheritDefinitionFeature = { name: 'ɵɵInheritDefinitionFeature', moduleName: CORE$1 };
2547 Identifiers$1.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE$1 };
2548 Identifiers$1.ProvidersFeature = { name: 'ɵɵProvidersFeature', moduleName: CORE$1 };
2549 Identifiers$1.listener = { name: 'ɵɵlistener', moduleName: CORE$1 };
2550 Identifiers$1.getInheritedFactory = {
2551 name: 'ɵɵgetInheritedFactory',
2552 moduleName: CORE$1,
2553 };
2554 // sanitization-related functions
2555 Identifiers$1.sanitizeHtml = { name: 'ɵɵsanitizeHtml', moduleName: CORE$1 };
2556 Identifiers$1.sanitizeStyle = { name: 'ɵɵsanitizeStyle', moduleName: CORE$1 };
2557 Identifiers$1.sanitizeResourceUrl = { name: 'ɵɵsanitizeResourceUrl', moduleName: CORE$1 };
2558 Identifiers$1.sanitizeScript = { name: 'ɵɵsanitizeScript', moduleName: CORE$1 };
2559 Identifiers$1.sanitizeUrl = { name: 'ɵɵsanitizeUrl', moduleName: CORE$1 };
2560 Identifiers$1.sanitizeUrlOrResourceUrl = { name: 'ɵɵsanitizeUrlOrResourceUrl', moduleName: CORE$1 };
2561 Identifiers$1.trustConstantHtml = { name: 'ɵɵtrustConstantHtml', moduleName: CORE$1 };
2562 Identifiers$1.trustConstantResourceUrl = { name: 'ɵɵtrustConstantResourceUrl', moduleName: CORE$1 };
2563
2564 /**
2565 * @license
2566 * Copyright Google LLC All Rights Reserved.
2567 *
2568 * Use of this source code is governed by an MIT-style license that can be
2569 * found in the LICENSE file at https://angular.io/license
2570 */
2571 const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
2572 function dashCaseToCamelCase(input) {
2573 return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase());
2574 }
2575 function splitAtColon(input, defaultValues) {
2576 return _splitAt(input, ':', defaultValues);
2577 }
2578 function splitAtPeriod(input, defaultValues) {
2579 return _splitAt(input, '.', defaultValues);
2580 }
2581 function _splitAt(input, character, defaultValues) {
2582 const characterIndex = input.indexOf(character);
2583 if (characterIndex == -1)
2584 return defaultValues;
2585 return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
2586 }
2587 function error(msg) {
2588 throw new Error(`Internal Error: ${msg}`);
2589 }
2590 function utf8Encode(str) {
2591 let encoded = [];
2592 for (let index = 0; index < str.length; index++) {
2593 let codePoint = str.charCodeAt(index);
2594 // decode surrogate
2595 // see https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
2596 if (codePoint >= 0xd800 && codePoint <= 0xdbff && str.length > (index + 1)) {
2597 const low = str.charCodeAt(index + 1);
2598 if (low >= 0xdc00 && low <= 0xdfff) {
2599 index++;
2600 codePoint = ((codePoint - 0xd800) << 10) + low - 0xdc00 + 0x10000;
2601 }
2602 }
2603 if (codePoint <= 0x7f) {
2604 encoded.push(codePoint);
2605 }
2606 else if (codePoint <= 0x7ff) {
2607 encoded.push(((codePoint >> 6) & 0x1F) | 0xc0, (codePoint & 0x3f) | 0x80);
2608 }
2609 else if (codePoint <= 0xffff) {
2610 encoded.push((codePoint >> 12) | 0xe0, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
2611 }
2612 else if (codePoint <= 0x1fffff) {
2613 encoded.push(((codePoint >> 18) & 0x07) | 0xf0, ((codePoint >> 12) & 0x3f) | 0x80, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
2614 }
2615 }
2616 return encoded;
2617 }
2618 function stringify(token) {
2619 if (typeof token === 'string') {
2620 return token;
2621 }
2622 if (Array.isArray(token)) {
2623 return '[' + token.map(stringify).join(', ') + ']';
2624 }
2625 if (token == null) {
2626 return '' + token;
2627 }
2628 if (token.overriddenName) {
2629 return `${token.overriddenName}`;
2630 }
2631 if (token.name) {
2632 return `${token.name}`;
2633 }
2634 if (!token.toString) {
2635 return 'object';
2636 }
2637 // WARNING: do not try to `JSON.stringify(token)` here
2638 // see https://github.com/angular/angular/issues/23440
2639 const res = token.toString();
2640 if (res == null) {
2641 return '' + res;
2642 }
2643 const newLineIndex = res.indexOf('\n');
2644 return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
2645 }
2646 class Version {
2647 constructor(full) {
2648 this.full = full;
2649 const splits = full.split('.');
2650 this.major = splits[0];
2651 this.minor = splits[1];
2652 this.patch = splits.slice(2).join('.');
2653 }
2654 }
2655 const __window = typeof window !== 'undefined' && window;
2656 const __self = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
2657 self instanceof WorkerGlobalScope && self;
2658 const __global = typeof global !== 'undefined' && global;
2659 // Check __global first, because in Node tests both __global and __window may be defined and _global
2660 // should be __global in that case.
2661 const _global = __global || __window || __self;
2662 function newArray(size, value) {
2663 const list = [];
2664 for (let i = 0; i < size; i++) {
2665 list.push(value);
2666 }
2667 return list;
2668 }
2669 /**
2670 * Partitions a given array into 2 arrays, based on a boolean value returned by the condition
2671 * function.
2672 *
2673 * @param arr Input array that should be partitioned
2674 * @param conditionFn Condition function that is called for each item in a given array and returns a
2675 * boolean value.
2676 */
2677 function partitionArray(arr, conditionFn) {
2678 const truthy = [];
2679 const falsy = [];
2680 for (const item of arr) {
2681 (conditionFn(item) ? truthy : falsy).push(item);
2682 }
2683 return [truthy, falsy];
2684 }
2685
2686 /**
2687 * @license
2688 * Copyright Google LLC All Rights Reserved.
2689 *
2690 * Use of this source code is governed by an MIT-style license that can be
2691 * found in the LICENSE file at https://angular.io/license
2692 */
2693 // https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
2694 const VERSION = 3;
2695 const JS_B64_PREFIX = '# sourceMappingURL=data:application/json;base64,';
2696 class SourceMapGenerator {
2697 constructor(file = null) {
2698 this.file = file;
2699 this.sourcesContent = new Map();
2700 this.lines = [];
2701 this.lastCol0 = 0;
2702 this.hasMappings = false;
2703 }
2704 // The content is `null` when the content is expected to be loaded using the URL
2705 addSource(url, content = null) {
2706 if (!this.sourcesContent.has(url)) {
2707 this.sourcesContent.set(url, content);
2708 }
2709 return this;
2710 }
2711 addLine() {
2712 this.lines.push([]);
2713 this.lastCol0 = 0;
2714 return this;
2715 }
2716 addMapping(col0, sourceUrl, sourceLine0, sourceCol0) {
2717 if (!this.currentLine) {
2718 throw new Error(`A line must be added before mappings can be added`);
2719 }
2720 if (sourceUrl != null && !this.sourcesContent.has(sourceUrl)) {
2721 throw new Error(`Unknown source file "${sourceUrl}"`);
2722 }
2723 if (col0 == null) {
2724 throw new Error(`The column in the generated code must be provided`);
2725 }
2726 if (col0 < this.lastCol0) {
2727 throw new Error(`Mapping should be added in output order`);
2728 }
2729 if (sourceUrl && (sourceLine0 == null || sourceCol0 == null)) {
2730 throw new Error(`The source location must be provided when a source url is provided`);
2731 }
2732 this.hasMappings = true;
2733 this.lastCol0 = col0;
2734 this.currentLine.push({ col0, sourceUrl, sourceLine0, sourceCol0 });
2735 return this;
2736 }
2737 /**
2738 * @internal strip this from published d.ts files due to
2739 * https://github.com/microsoft/TypeScript/issues/36216
2740 */
2741 get currentLine() {
2742 return this.lines.slice(-1)[0];
2743 }
2744 toJSON() {
2745 if (!this.hasMappings) {
2746 return null;
2747 }
2748 const sourcesIndex = new Map();
2749 const sources = [];
2750 const sourcesContent = [];
2751 Array.from(this.sourcesContent.keys()).forEach((url, i) => {
2752 sourcesIndex.set(url, i);
2753 sources.push(url);
2754 sourcesContent.push(this.sourcesContent.get(url) || null);
2755 });
2756 let mappings = '';
2757 let lastCol0 = 0;
2758 let lastSourceIndex = 0;
2759 let lastSourceLine0 = 0;
2760 let lastSourceCol0 = 0;
2761 this.lines.forEach(segments => {
2762 lastCol0 = 0;
2763 mappings += segments
2764 .map(segment => {
2765 // zero-based starting column of the line in the generated code
2766 let segAsStr = toBase64VLQ(segment.col0 - lastCol0);
2767 lastCol0 = segment.col0;
2768 if (segment.sourceUrl != null) {
2769 // zero-based index into the “sources” list
2770 segAsStr +=
2771 toBase64VLQ(sourcesIndex.get(segment.sourceUrl) - lastSourceIndex);
2772 lastSourceIndex = sourcesIndex.get(segment.sourceUrl);
2773 // the zero-based starting line in the original source
2774 segAsStr += toBase64VLQ(segment.sourceLine0 - lastSourceLine0);
2775 lastSourceLine0 = segment.sourceLine0;
2776 // the zero-based starting column in the original source
2777 segAsStr += toBase64VLQ(segment.sourceCol0 - lastSourceCol0);
2778 lastSourceCol0 = segment.sourceCol0;
2779 }
2780 return segAsStr;
2781 })
2782 .join(',');
2783 mappings += ';';
2784 });
2785 mappings = mappings.slice(0, -1);
2786 return {
2787 'file': this.file || '',
2788 'version': VERSION,
2789 'sourceRoot': '',
2790 'sources': sources,
2791 'sourcesContent': sourcesContent,
2792 'mappings': mappings,
2793 };
2794 }
2795 toJsComment() {
2796 return this.hasMappings ? '//' + JS_B64_PREFIX + toBase64String(JSON.stringify(this, null, 0)) :
2797 '';
2798 }
2799 }
2800 function toBase64String(value) {
2801 let b64 = '';
2802 const encoded = utf8Encode(value);
2803 for (let i = 0; i < encoded.length;) {
2804 const i1 = encoded[i++];
2805 const i2 = i < encoded.length ? encoded[i++] : null;
2806 const i3 = i < encoded.length ? encoded[i++] : null;
2807 b64 += toBase64Digit(i1 >> 2);
2808 b64 += toBase64Digit(((i1 & 3) << 4) | (i2 === null ? 0 : i2 >> 4));
2809 b64 += i2 === null ? '=' : toBase64Digit(((i2 & 15) << 2) | (i3 === null ? 0 : i3 >> 6));
2810 b64 += i2 === null || i3 === null ? '=' : toBase64Digit(i3 & 63);
2811 }
2812 return b64;
2813 }
2814 function toBase64VLQ(value) {
2815 value = value < 0 ? ((-value) << 1) + 1 : value << 1;
2816 let out = '';
2817 do {
2818 let digit = value & 31;
2819 value = value >> 5;
2820 if (value > 0) {
2821 digit = digit | 32;
2822 }
2823 out += toBase64Digit(digit);
2824 } while (value > 0);
2825 return out;
2826 }
2827 const B64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
2828 function toBase64Digit(value) {
2829 if (value < 0 || value >= 64) {
2830 throw new Error(`Can only encode value in the range [0, 63]`);
2831 }
2832 return B64_DIGITS[value];
2833 }
2834
2835 /**
2836 * @license
2837 * Copyright Google LLC All Rights Reserved.
2838 *
2839 * Use of this source code is governed by an MIT-style license that can be
2840 * found in the LICENSE file at https://angular.io/license
2841 */
2842 const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
2843 const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
2844 const _INDENT_WITH = ' ';
2845 const CATCH_ERROR_VAR = variable('error', null, null);
2846 const CATCH_STACK_VAR = variable('stack', null, null);
2847 class _EmittedLine {
2848 constructor(indent) {
2849 this.indent = indent;
2850 this.partsLength = 0;
2851 this.parts = [];
2852 this.srcSpans = [];
2853 }
2854 }
2855 class EmitterVisitorContext {
2856 constructor(_indent) {
2857 this._indent = _indent;
2858 this._classes = [];
2859 this._preambleLineCount = 0;
2860 this._lines = [new _EmittedLine(_indent)];
2861 }
2862 static createRoot() {
2863 return new EmitterVisitorContext(0);
2864 }
2865 /**
2866 * @internal strip this from published d.ts files due to
2867 * https://github.com/microsoft/TypeScript/issues/36216
2868 */
2869 get _currentLine() {
2870 return this._lines[this._lines.length - 1];
2871 }
2872 println(from, lastPart = '') {
2873 this.print(from || null, lastPart, true);
2874 }
2875 lineIsEmpty() {
2876 return this._currentLine.parts.length === 0;
2877 }
2878 lineLength() {
2879 return this._currentLine.indent * _INDENT_WITH.length + this._currentLine.partsLength;
2880 }
2881 print(from, part, newLine = false) {
2882 if (part.length > 0) {
2883 this._currentLine.parts.push(part);
2884 this._currentLine.partsLength += part.length;
2885 this._currentLine.srcSpans.push(from && from.sourceSpan || null);
2886 }
2887 if (newLine) {
2888 this._lines.push(new _EmittedLine(this._indent));
2889 }
2890 }
2891 removeEmptyLastLine() {
2892 if (this.lineIsEmpty()) {
2893 this._lines.pop();
2894 }
2895 }
2896 incIndent() {
2897 this._indent++;
2898 if (this.lineIsEmpty()) {
2899 this._currentLine.indent = this._indent;
2900 }
2901 }
2902 decIndent() {
2903 this._indent--;
2904 if (this.lineIsEmpty()) {
2905 this._currentLine.indent = this._indent;
2906 }
2907 }
2908 pushClass(clazz) {
2909 this._classes.push(clazz);
2910 }
2911 popClass() {
2912 return this._classes.pop();
2913 }
2914 get currentClass() {
2915 return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null;
2916 }
2917 toSource() {
2918 return this.sourceLines
2919 .map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '')
2920 .join('\n');
2921 }
2922 toSourceMapGenerator(genFilePath, startsAtLine = 0) {
2923 const map = new SourceMapGenerator(genFilePath);
2924 let firstOffsetMapped = false;
2925 const mapFirstOffsetIfNeeded = () => {
2926 if (!firstOffsetMapped) {
2927 // Add a single space so that tools won't try to load the file from disk.
2928 // Note: We are using virtual urls like `ng:///`, so we have to
2929 // provide a content here.
2930 map.addSource(genFilePath, ' ').addMapping(0, genFilePath, 0, 0);
2931 firstOffsetMapped = true;
2932 }
2933 };
2934 for (let i = 0; i < startsAtLine; i++) {
2935 map.addLine();
2936 mapFirstOffsetIfNeeded();
2937 }
2938 this.sourceLines.forEach((line, lineIdx) => {
2939 map.addLine();
2940 const spans = line.srcSpans;
2941 const parts = line.parts;
2942 let col0 = line.indent * _INDENT_WITH.length;
2943 let spanIdx = 0;
2944 // skip leading parts without source spans
2945 while (spanIdx < spans.length && !spans[spanIdx]) {
2946 col0 += parts[spanIdx].length;
2947 spanIdx++;
2948 }
2949 if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) {
2950 firstOffsetMapped = true;
2951 }
2952 else {
2953 mapFirstOffsetIfNeeded();
2954 }
2955 while (spanIdx < spans.length) {
2956 const span = spans[spanIdx];
2957 const source = span.start.file;
2958 const sourceLine = span.start.line;
2959 const sourceCol = span.start.col;
2960 map.addSource(source.url, source.content)
2961 .addMapping(col0, source.url, sourceLine, sourceCol);
2962 col0 += parts[spanIdx].length;
2963 spanIdx++;
2964 // assign parts without span or the same span to the previous segment
2965 while (spanIdx < spans.length && (span === spans[spanIdx] || !spans[spanIdx])) {
2966 col0 += parts[spanIdx].length;
2967 spanIdx++;
2968 }
2969 }
2970 });
2971 return map;
2972 }
2973 setPreambleLineCount(count) {
2974 return this._preambleLineCount = count;
2975 }
2976 spanOf(line, column) {
2977 const emittedLine = this._lines[line - this._preambleLineCount];
2978 if (emittedLine) {
2979 let columnsLeft = column - _createIndent(emittedLine.indent).length;
2980 for (let partIndex = 0; partIndex < emittedLine.parts.length; partIndex++) {
2981 const part = emittedLine.parts[partIndex];
2982 if (part.length > columnsLeft) {
2983 return emittedLine.srcSpans[partIndex];
2984 }
2985 columnsLeft -= part.length;
2986 }
2987 }
2988 return null;
2989 }
2990 /**
2991 * @internal strip this from published d.ts files due to
2992 * https://github.com/microsoft/TypeScript/issues/36216
2993 */
2994 get sourceLines() {
2995 if (this._lines.length && this._lines[this._lines.length - 1].parts.length === 0) {
2996 return this._lines.slice(0, -1);
2997 }
2998 return this._lines;
2999 }
3000 }
3001 class AbstractEmitterVisitor {
3002 constructor(_escapeDollarInStrings) {
3003 this._escapeDollarInStrings = _escapeDollarInStrings;
3004 }
3005 printLeadingComments(stmt, ctx) {
3006 if (stmt.leadingComments === undefined) {
3007 return;
3008 }
3009 for (const comment of stmt.leadingComments) {
3010 if (comment instanceof JSDocComment) {
3011 ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline);
3012 }
3013 else {
3014 if (comment.multiline) {
3015 ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline);
3016 }
3017 else {
3018 comment.text.split('\n').forEach((line) => {
3019 ctx.println(stmt, `// ${line}`);
3020 });
3021 }
3022 }
3023 }
3024 }
3025 visitExpressionStmt(stmt, ctx) {
3026 this.printLeadingComments(stmt, ctx);
3027 stmt.expr.visitExpression(this, ctx);
3028 ctx.println(stmt, ';');
3029 return null;
3030 }
3031 visitReturnStmt(stmt, ctx) {
3032 this.printLeadingComments(stmt, ctx);
3033 ctx.print(stmt, `return `);
3034 stmt.value.visitExpression(this, ctx);
3035 ctx.println(stmt, ';');
3036 return null;
3037 }
3038 visitIfStmt(stmt, ctx) {
3039 this.printLeadingComments(stmt, ctx);
3040 ctx.print(stmt, `if (`);
3041 stmt.condition.visitExpression(this, ctx);
3042 ctx.print(stmt, `) {`);
3043 const hasElseCase = stmt.falseCase != null && stmt.falseCase.length > 0;
3044 if (stmt.trueCase.length <= 1 && !hasElseCase) {
3045 ctx.print(stmt, ` `);
3046 this.visitAllStatements(stmt.trueCase, ctx);
3047 ctx.removeEmptyLastLine();
3048 ctx.print(stmt, ` `);
3049 }
3050 else {
3051 ctx.println();
3052 ctx.incIndent();
3053 this.visitAllStatements(stmt.trueCase, ctx);
3054 ctx.decIndent();
3055 if (hasElseCase) {
3056 ctx.println(stmt, `} else {`);
3057 ctx.incIndent();
3058 this.visitAllStatements(stmt.falseCase, ctx);
3059 ctx.decIndent();
3060 }
3061 }
3062 ctx.println(stmt, `}`);
3063 return null;
3064 }
3065 visitThrowStmt(stmt, ctx) {
3066 this.printLeadingComments(stmt, ctx);
3067 ctx.print(stmt, `throw `);
3068 stmt.error.visitExpression(this, ctx);
3069 ctx.println(stmt, `;`);
3070 return null;
3071 }
3072 visitWriteVarExpr(expr, ctx) {
3073 const lineWasEmpty = ctx.lineIsEmpty();
3074 if (!lineWasEmpty) {
3075 ctx.print(expr, '(');
3076 }
3077 ctx.print(expr, `${expr.name} = `);
3078 expr.value.visitExpression(this, ctx);
3079 if (!lineWasEmpty) {
3080 ctx.print(expr, ')');
3081 }
3082 return null;
3083 }
3084 visitWriteKeyExpr(expr, ctx) {
3085 const lineWasEmpty = ctx.lineIsEmpty();
3086 if (!lineWasEmpty) {
3087 ctx.print(expr, '(');
3088 }
3089 expr.receiver.visitExpression(this, ctx);
3090 ctx.print(expr, `[`);
3091 expr.index.visitExpression(this, ctx);
3092 ctx.print(expr, `] = `);
3093 expr.value.visitExpression(this, ctx);
3094 if (!lineWasEmpty) {
3095 ctx.print(expr, ')');
3096 }
3097 return null;
3098 }
3099 visitWritePropExpr(expr, ctx) {
3100 const lineWasEmpty = ctx.lineIsEmpty();
3101 if (!lineWasEmpty) {
3102 ctx.print(expr, '(');
3103 }
3104 expr.receiver.visitExpression(this, ctx);
3105 ctx.print(expr, `.${expr.name} = `);
3106 expr.value.visitExpression(this, ctx);
3107 if (!lineWasEmpty) {
3108 ctx.print(expr, ')');
3109 }
3110 return null;
3111 }
3112 visitInvokeFunctionExpr(expr, ctx) {
3113 expr.fn.visitExpression(this, ctx);
3114 ctx.print(expr, `(`);
3115 this.visitAllExpressions(expr.args, ctx, ',');
3116 ctx.print(expr, `)`);
3117 return null;
3118 }
3119 visitTaggedTemplateExpr(expr, ctx) {
3120 expr.tag.visitExpression(this, ctx);
3121 ctx.print(expr, '`' + expr.template.elements[0].rawText);
3122 for (let i = 1; i < expr.template.elements.length; i++) {
3123 ctx.print(expr, '${');
3124 expr.template.expressions[i - 1].visitExpression(this, ctx);
3125 ctx.print(expr, `}${expr.template.elements[i].rawText}`);
3126 }
3127 ctx.print(expr, '`');
3128 return null;
3129 }
3130 visitWrappedNodeExpr(ast, ctx) {
3131 throw new Error('Abstract emitter cannot visit WrappedNodeExpr.');
3132 }
3133 visitTypeofExpr(expr, ctx) {
3134 ctx.print(expr, 'typeof ');
3135 expr.expr.visitExpression(this, ctx);
3136 }
3137 visitReadVarExpr(ast, ctx) {
3138 let varName = ast.name;
3139 if (ast.builtin != null) {
3140 switch (ast.builtin) {
3141 case BuiltinVar.Super:
3142 varName = 'super';
3143 break;
3144 case BuiltinVar.This:
3145 varName = 'this';
3146 break;
3147 case BuiltinVar.CatchError:
3148 varName = CATCH_ERROR_VAR.name;
3149 break;
3150 case BuiltinVar.CatchStack:
3151 varName = CATCH_STACK_VAR.name;
3152 break;
3153 default:
3154 throw new Error(`Unknown builtin variable ${ast.builtin}`);
3155 }
3156 }
3157 ctx.print(ast, varName);
3158 return null;
3159 }
3160 visitInstantiateExpr(ast, ctx) {
3161 ctx.print(ast, `new `);
3162 ast.classExpr.visitExpression(this, ctx);
3163 ctx.print(ast, `(`);
3164 this.visitAllExpressions(ast.args, ctx, ',');
3165 ctx.print(ast, `)`);
3166 return null;
3167 }
3168 visitLiteralExpr(ast, ctx) {
3169 const value = ast.value;
3170 if (typeof value === 'string') {
3171 ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings));
3172 }
3173 else {
3174 ctx.print(ast, `${value}`);
3175 }
3176 return null;
3177 }
3178 visitLocalizedString(ast, ctx) {
3179 const head = ast.serializeI18nHead();
3180 ctx.print(ast, '$localize `' + head.raw);
3181 for (let i = 1; i < ast.messageParts.length; i++) {
3182 ctx.print(ast, '${');
3183 ast.expressions[i - 1].visitExpression(this, ctx);
3184 ctx.print(ast, `}${ast.serializeI18nTemplatePart(i).raw}`);
3185 }
3186 ctx.print(ast, '`');
3187 return null;
3188 }
3189 visitConditionalExpr(ast, ctx) {
3190 ctx.print(ast, `(`);
3191 ast.condition.visitExpression(this, ctx);
3192 ctx.print(ast, '? ');
3193 ast.trueCase.visitExpression(this, ctx);
3194 ctx.print(ast, ': ');
3195 ast.falseCase.visitExpression(this, ctx);
3196 ctx.print(ast, `)`);
3197 return null;
3198 }
3199 visitNotExpr(ast, ctx) {
3200 ctx.print(ast, '!');
3201 ast.condition.visitExpression(this, ctx);
3202 return null;
3203 }
3204 visitAssertNotNullExpr(ast, ctx) {
3205 ast.condition.visitExpression(this, ctx);
3206 return null;
3207 }
3208 visitUnaryOperatorExpr(ast, ctx) {
3209 let opStr;
3210 switch (ast.operator) {
3211 case UnaryOperator.Plus:
3212 opStr = '+';
3213 break;
3214 case UnaryOperator.Minus:
3215 opStr = '-';
3216 break;
3217 default:
3218 throw new Error(`Unknown operator ${ast.operator}`);
3219 }
3220 if (ast.parens)
3221 ctx.print(ast, `(`);
3222 ctx.print(ast, opStr);
3223 ast.expr.visitExpression(this, ctx);
3224 if (ast.parens)
3225 ctx.print(ast, `)`);
3226 return null;
3227 }
3228 visitBinaryOperatorExpr(ast, ctx) {
3229 let opStr;
3230 switch (ast.operator) {
3231 case BinaryOperator.Equals:
3232 opStr = '==';
3233 break;
3234 case BinaryOperator.Identical:
3235 opStr = '===';
3236 break;
3237 case BinaryOperator.NotEquals:
3238 opStr = '!=';
3239 break;
3240 case BinaryOperator.NotIdentical:
3241 opStr = '!==';
3242 break;
3243 case BinaryOperator.And:
3244 opStr = '&&';
3245 break;
3246 case BinaryOperator.BitwiseAnd:
3247 opStr = '&';
3248 break;
3249 case BinaryOperator.Or:
3250 opStr = '||';
3251 break;
3252 case BinaryOperator.Plus:
3253 opStr = '+';
3254 break;
3255 case BinaryOperator.Minus:
3256 opStr = '-';
3257 break;
3258 case BinaryOperator.Divide:
3259 opStr = '/';
3260 break;
3261 case BinaryOperator.Multiply:
3262 opStr = '*';
3263 break;
3264 case BinaryOperator.Modulo:
3265 opStr = '%';
3266 break;
3267 case BinaryOperator.Lower:
3268 opStr = '<';
3269 break;
3270 case BinaryOperator.LowerEquals:
3271 opStr = '<=';
3272 break;
3273 case BinaryOperator.Bigger:
3274 opStr = '>';
3275 break;
3276 case BinaryOperator.BiggerEquals:
3277 opStr = '>=';
3278 break;
3279 case BinaryOperator.NullishCoalesce:
3280 opStr = '??';
3281 break;
3282 default:
3283 throw new Error(`Unknown operator ${ast.operator}`);
3284 }
3285 if (ast.parens)
3286 ctx.print(ast, `(`);
3287 ast.lhs.visitExpression(this, ctx);
3288 ctx.print(ast, ` ${opStr} `);
3289 ast.rhs.visitExpression(this, ctx);
3290 if (ast.parens)
3291 ctx.print(ast, `)`);
3292 return null;
3293 }
3294 visitReadPropExpr(ast, ctx) {
3295 ast.receiver.visitExpression(this, ctx);
3296 ctx.print(ast, `.`);
3297 ctx.print(ast, ast.name);
3298 return null;
3299 }
3300 visitReadKeyExpr(ast, ctx) {
3301 ast.receiver.visitExpression(this, ctx);
3302 ctx.print(ast, `[`);
3303 ast.index.visitExpression(this, ctx);
3304 ctx.print(ast, `]`);
3305 return null;
3306 }
3307 visitLiteralArrayExpr(ast, ctx) {
3308 ctx.print(ast, `[`);
3309 this.visitAllExpressions(ast.entries, ctx, ',');
3310 ctx.print(ast, `]`);
3311 return null;
3312 }
3313 visitLiteralMapExpr(ast, ctx) {
3314 ctx.print(ast, `{`);
3315 this.visitAllObjects(entry => {
3316 ctx.print(ast, `${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`);
3317 entry.value.visitExpression(this, ctx);
3318 }, ast.entries, ctx, ',');
3319 ctx.print(ast, `}`);
3320 return null;
3321 }
3322 visitCommaExpr(ast, ctx) {
3323 ctx.print(ast, '(');
3324 this.visitAllExpressions(ast.parts, ctx, ',');
3325 ctx.print(ast, ')');
3326 return null;
3327 }
3328 visitAllExpressions(expressions, ctx, separator) {
3329 this.visitAllObjects(expr => expr.visitExpression(this, ctx), expressions, ctx, separator);
3330 }
3331 visitAllObjects(handler, expressions, ctx, separator) {
3332 let incrementedIndent = false;
3333 for (let i = 0; i < expressions.length; i++) {
3334 if (i > 0) {
3335 if (ctx.lineLength() > 80) {
3336 ctx.print(null, separator, true);
3337 if (!incrementedIndent) {
3338 // continuation are marked with double indent.
3339 ctx.incIndent();
3340 ctx.incIndent();
3341 incrementedIndent = true;
3342 }
3343 }
3344 else {
3345 ctx.print(null, separator, false);
3346 }
3347 }
3348 handler(expressions[i]);
3349 }
3350 if (incrementedIndent) {
3351 // continuation are marked with double indent.
3352 ctx.decIndent();
3353 ctx.decIndent();
3354 }
3355 }
3356 visitAllStatements(statements, ctx) {
3357 statements.forEach((stmt) => stmt.visitStatement(this, ctx));
3358 }
3359 }
3360 function escapeIdentifier(input, escapeDollar, alwaysQuote = true) {
3361 if (input == null) {
3362 return null;
3363 }
3364 const body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match) => {
3365 if (match[0] == '$') {
3366 return escapeDollar ? '\\$' : '$';
3367 }
3368 else if (match[0] == '\n') {
3369 return '\\n';
3370 }
3371 else if (match[0] == '\r') {
3372 return '\\r';
3373 }
3374 else {
3375 return `\\${match[0]}`;
3376 }
3377 });
3378 const requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body);
3379 return requiresQuotes ? `'${body}'` : body;
3380 }
3381 function _createIndent(count) {
3382 let res = '';
3383 for (let i = 0; i < count; i++) {
3384 res += _INDENT_WITH;
3385 }
3386 return res;
3387 }
3388
3389 /**
3390 * @license
3391 * Copyright Google LLC All Rights Reserved.
3392 *
3393 * Use of this source code is governed by an MIT-style license that can be
3394 * found in the LICENSE file at https://angular.io/license
3395 */
3396 function typeWithParameters(type, numParams) {
3397 if (numParams === 0) {
3398 return expressionType(type);
3399 }
3400 const params = [];
3401 for (let i = 0; i < numParams; i++) {
3402 params.push(DYNAMIC_TYPE);
3403 }
3404 return expressionType(type, undefined, params);
3405 }
3406 const ANIMATE_SYMBOL_PREFIX = '@';
3407 function prepareSyntheticPropertyName(name) {
3408 return `${ANIMATE_SYMBOL_PREFIX}${name}`;
3409 }
3410 function prepareSyntheticListenerName(name, phase) {
3411 return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`;
3412 }
3413 function getSafePropertyAccessString(accessor, name) {
3414 const escapedName = escapeIdentifier(name, false, false);
3415 return escapedName !== name ? `${accessor}[${escapedName}]` : `${accessor}.${name}`;
3416 }
3417 function prepareSyntheticListenerFunctionName(name, phase) {
3418 return `animation_${name}_${phase}`;
3419 }
3420 function jitOnlyGuardedExpression(expr) {
3421 return guardedExpression('ngJitMode', expr);
3422 }
3423 function devOnlyGuardedExpression(expr) {
3424 return guardedExpression('ngDevMode', expr);
3425 }
3426 function guardedExpression(guard, expr) {
3427 const guardExpr = new ExternalExpr({ name: guard, moduleName: null });
3428 const guardNotDefined = new BinaryOperatorExpr(BinaryOperator.Identical, new TypeofExpr(guardExpr), literal$1('undefined'));
3429 const guardUndefinedOrTrue = new BinaryOperatorExpr(BinaryOperator.Or, guardNotDefined, guardExpr, /* type */ undefined,
3430 /* sourceSpan */ undefined, true);
3431 return new BinaryOperatorExpr(BinaryOperator.And, guardUndefinedOrTrue, expr);
3432 }
3433 function wrapReference(value) {
3434 const wrapped = new WrappedNodeExpr(value);
3435 return { value: wrapped, type: wrapped };
3436 }
3437 function refsToArray(refs, shouldForwardDeclare) {
3438 const values = literalArr(refs.map(ref => ref.value));
3439 return shouldForwardDeclare ? fn([], [new ReturnStatement(values)]) : values;
3440 }
3441 function createMayBeForwardRefExpression(expression, forwardRef) {
3442 return { expression, forwardRef };
3443 }
3444 /**
3445 * Convert a `MaybeForwardRefExpression` to an `Expression`, possibly wrapping its expression in a
3446 * `forwardRef()` call.
3447 *
3448 * If `MaybeForwardRefExpression.forwardRef` is `ForwardRefHandling.Unwrapped` then the expression
3449 * was originally wrapped in a `forwardRef()` call to prevent the value from being eagerly evaluated
3450 * in the code.
3451 *
3452 * See `packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts` and
3453 * `packages/compiler/src/jit_compiler_facade.ts` for more information.
3454 */
3455 function convertFromMaybeForwardRefExpression({ expression, forwardRef }) {
3456 switch (forwardRef) {
3457 case 0 /* None */:
3458 case 1 /* Wrapped */:
3459 return expression;
3460 case 2 /* Unwrapped */:
3461 return generateForwardRef(expression);
3462 }
3463 }
3464 /**
3465 * Generate an expression that has the given `expr` wrapped in the following form:
3466 *
3467 * ```
3468 * forwardRef(() => expr)
3469 * ```
3470 */
3471 function generateForwardRef(expr) {
3472 return importExpr(Identifiers$1.forwardRef).callFn([fn([], [new ReturnStatement(expr)])]);
3473 }
3474
3475 var R3FactoryDelegateType;
3476 (function (R3FactoryDelegateType) {
3477 R3FactoryDelegateType[R3FactoryDelegateType["Class"] = 0] = "Class";
3478 R3FactoryDelegateType[R3FactoryDelegateType["Function"] = 1] = "Function";
3479 })(R3FactoryDelegateType || (R3FactoryDelegateType = {}));
3480 var FactoryTarget$1;
3481 (function (FactoryTarget) {
3482 FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive";
3483 FactoryTarget[FactoryTarget["Component"] = 1] = "Component";
3484 FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable";
3485 FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe";
3486 FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule";
3487 })(FactoryTarget$1 || (FactoryTarget$1 = {}));
3488 /**
3489 * Construct a factory function expression for the given `R3FactoryMetadata`.
3490 */
3491 function compileFactoryFunction(meta) {
3492 const t = variable('t');
3493 let baseFactoryVar = null;
3494 // The type to instantiate via constructor invocation. If there is no delegated factory, meaning
3495 // this type is always created by constructor invocation, then this is the type-to-create
3496 // parameter provided by the user (t) if specified, or the current type if not. If there is a
3497 // delegated factory (which is used to create the current type) then this is only the type-to-
3498 // create parameter (t).
3499 const typeForCtor = !isDelegatedFactoryMetadata(meta) ?
3500 new BinaryOperatorExpr(BinaryOperator.Or, t, meta.internalType) :
3501 t;
3502 let ctorExpr = null;
3503 if (meta.deps !== null) {
3504 // There is a constructor (either explicitly or implicitly defined).
3505 if (meta.deps !== 'invalid') {
3506 ctorExpr = new InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.target));
3507 }
3508 }
3509 else {
3510 // There is no constructor, use the base class' factory to construct typeForCtor.
3511 baseFactoryVar = variable(${meta.name}_BaseFactory`);
3512 ctorExpr = baseFactoryVar.callFn([typeForCtor]);
3513 }
3514 const body = [];
3515 let retExpr = null;
3516 function makeConditionalFactory(nonCtorExpr) {
3517 const r = variable('r');
3518 body.push(r.set(NULL_EXPR).toDeclStmt());
3519 const ctorStmt = ctorExpr !== null ? r.set(ctorExpr).toStmt() :
3520 importExpr(Identifiers$1.invalidFactory).callFn([]).toStmt();
3521 body.push(ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()]));
3522 return r;
3523 }
3524 if (isDelegatedFactoryMetadata(meta)) {
3525 // This type is created with a delegated factory. If a type parameter is not specified, call
3526 // the factory instead.
3527 const delegateArgs = injectDependencies(meta.delegateDeps, meta.target);
3528 // Either call `new delegate(...)` or `delegate(...)` depending on meta.delegateType.
3529 const factoryExpr = new (meta.delegateType === R3FactoryDelegateType.Class ?
3530 InstantiateExpr :
3531 InvokeFunctionExpr)(meta.delegate, delegateArgs);
3532 retExpr = makeConditionalFactory(factoryExpr);
3533 }
3534 else if (isExpressionFactoryMetadata(meta)) {
3535 // TODO(alxhub): decide whether to lower the value here or in the caller
3536 retExpr = makeConditionalFactory(meta.expression);
3537 }
3538 else {
3539 retExpr = ctorExpr;
3540 }
3541 if (retExpr === null) {
3542 // The expression cannot be formed so render an `ɵɵinvalidFactory()` call.
3543 body.push(importExpr(Identifiers$1.invalidFactory).callFn([]).toStmt());
3544 }
3545 else if (baseFactoryVar !== null) {
3546 // This factory uses a base factory, so call `ɵɵgetInheritedFactory()` to compute it.
3547 const getInheritedFactoryCall = importExpr(Identifiers$1.getInheritedFactory).callFn([meta.internalType]);
3548 // Memoize the base factoryFn: `baseFactory || (baseFactory = ɵɵgetInheritedFactory(...))`
3549 const baseFactory = new BinaryOperatorExpr(BinaryOperator.Or, baseFactoryVar, baseFactoryVar.set(getInheritedFactoryCall));
3550 body.push(new ReturnStatement(baseFactory.callFn([typeForCtor])));
3551 }
3552 else {
3553 // This is straightforward factory, just return it.
3554 body.push(new ReturnStatement(retExpr));
3555 }
3556 let factoryFn = fn([new FnParam('t', DYNAMIC_TYPE)], body, INFERRED_TYPE, undefined, `${meta.name}_Factory`);
3557 if (baseFactoryVar !== null) {
3558 // There is a base factory variable so wrap its declaration along with the factory function into
3559 // an IIFE.
3560 factoryFn = fn([], [
3561 new DeclareVarStmt(baseFactoryVar.name), new ReturnStatement(factoryFn)
3562 ]).callFn([], /* sourceSpan */ undefined, /* pure */ true);
3563 }
3564 return {
3565 expression: factoryFn,
3566 statements: [],
3567 type: createFactoryType(meta),
3568 };
3569 }
3570 function createFactoryType(meta) {
3571 const ctorDepsType = meta.deps !== null && meta.deps !== 'invalid' ? createCtorDepsType(meta.deps) : NONE_TYPE;
3572 return expressionType(importExpr(Identifiers$1.FactoryDeclaration, [typeWithParameters(meta.type.type, meta.typeArgumentCount), ctorDepsType]));
3573 }
3574 function injectDependencies(deps, target) {
3575 return deps.map((dep, index) => compileInjectDependency(dep, target, index));
3576 }
3577 function compileInjectDependency(dep, target, index) {
3578 // Interpret the dependency according to its resolved type.
3579 if (dep.token === null) {
3580 return importExpr(Identifiers$1.invalidFactoryDep).callFn([literal$1(index)]);
3581 }
3582 else if (dep.attributeNameType === null) {
3583 // Build up the injection flags according to the metadata.
3584 const flags = 0 /* Default */ | (dep.self ? 2 /* Self */ : 0) |
3585 (dep.skipSelf ? 4 /* SkipSelf */ : 0) | (dep.host ? 1 /* Host */ : 0) |
3586 (dep.optional ? 8 /* Optional */ : 0) |
3587 (target === FactoryTarget$1.Pipe ? 16 /* ForPipe */ : 0);
3588 // If this dependency is optional or otherwise has non-default flags, then additional
3589 // parameters describing how to inject the dependency must be passed to the inject function
3590 // that's being used.
3591 let flagsParam = (flags !== 0 /* Default */ || dep.optional) ? literal$1(flags) : null;
3592 // Build up the arguments to the injectFn call.
3593 const injectArgs = [dep.token];
3594 if (flagsParam) {
3595 injectArgs.push(flagsParam);
3596 }
3597 const injectFn = getInjectFn(target);
3598 return importExpr(injectFn).callFn(injectArgs);
3599 }
3600 else {
3601 // The `dep.attributeTypeName` value is defined, which indicates that this is an `@Attribute()`
3602 // type dependency. For the generated JS we still want to use the `dep.token` value in case the
3603 // name given for the attribute is not a string literal. For example given `@Attribute(foo())`,
3604 // we want to generate `ɵɵinjectAttribute(foo())`.
3605 //
3606 // The `dep.attributeTypeName` is only actually used (in `createCtorDepType()`) to generate
3607 // typings.
3608 return importExpr(Identifiers$1.injectAttribute).callFn([dep.token]);
3609 }
3610 }
3611 function createCtorDepsType(deps) {
3612 let hasTypes = false;
3613 const attributeTypes = deps.map(dep => {
3614 const type = createCtorDepType(dep);
3615 if (type !== null) {
3616 hasTypes = true;
3617 return type;
3618 }
3619 else {
3620 return literal$1(null);
3621 }
3622 });
3623 if (hasTypes) {
3624 return expressionType(literalArr(attributeTypes));
3625 }
3626 else {
3627 return NONE_TYPE;
3628 }
3629 }
3630 function createCtorDepType(dep) {
3631 const entries = [];
3632 if (dep.attributeNameType !== null) {
3633 entries.push({ key: 'attribute', value: dep.attributeNameType, quoted: false });
3634 }
3635 if (dep.optional) {
3636 entries.push({ key: 'optional', value: literal$1(true), quoted: false });
3637 }
3638 if (dep.host) {
3639 entries.push({ key: 'host', value: literal$1(true), quoted: false });
3640 }
3641 if (dep.self) {
3642 entries.push({ key: 'self', value: literal$1(true), quoted: false });
3643 }
3644 if (dep.skipSelf) {
3645 entries.push({ key: 'skipSelf', value: literal$1(true), quoted: false });
3646 }
3647 return entries.length > 0 ? literalMap(entries) : null;
3648 }
3649 function isDelegatedFactoryMetadata(meta) {
3650 return meta.delegateType !== undefined;
3651 }
3652 function isExpressionFactoryMetadata(meta) {
3653 return meta.expression !== undefined;
3654 }
3655 function getInjectFn(target) {
3656 switch (target) {
3657 case FactoryTarget$1.Component:
3658 case FactoryTarget$1.Directive:
3659 case FactoryTarget$1.Pipe:
3660 return Identifiers$1.directiveInject;
3661 case FactoryTarget$1.NgModule:
3662 case FactoryTarget$1.Injectable:
3663 default:
3664 return Identifiers$1.inject;
3665 }
3666 }
3667
3668 /**
3669 * @license
3670 * Copyright Google LLC All Rights Reserved.
3671 *
3672 * Use of this source code is governed by an MIT-style license that can be
3673 * found in the LICENSE file at https://angular.io/license
3674 */
3675 /**
3676 * This is an R3 `Node`-like wrapper for a raw `html.Comment` node. We do not currently
3677 * require the implementation of a visitor for Comments as they are only collected at
3678 * the top-level of the R3 AST, and only if `Render3ParseOptions['collectCommentNodes']`
3679 * is true.
3680 */
3681 class Comment$1 {
3682 constructor(value, sourceSpan) {
3683 this.value = value;
3684 this.sourceSpan = sourceSpan;
3685 }
3686 visit(_visitor) {
3687 throw new Error('visit() not implemented for Comment');
3688 }
3689 }
3690 class Text$2 {
3691 constructor(value, sourceSpan) {
3692 this.value = value;
3693 this.sourceSpan = sourceSpan;
3694 }
3695 visit(visitor) {
3696 return visitor.visitText(this);
3697 }
3698 }
3699 class BoundText {
3700 constructor(value, sourceSpan, i18n) {
3701 this.value = value;
3702 this.sourceSpan = sourceSpan;
3703 this.i18n = i18n;
3704 }
3705 visit(visitor) {
3706 return visitor.visitBoundText(this);
3707 }
3708 }
3709 /**
3710 * Represents a text attribute in the template.
3711 *
3712 * `valueSpan` may not be present in cases where there is no value `<div a></div>`.
3713 * `keySpan` may also not be present for synthetic attributes from ICU expansions.
3714 */
3715 class TextAttribute {
3716 constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) {
3717 this.name = name;
3718 this.value = value;
3719 this.sourceSpan = sourceSpan;
3720 this.keySpan = keySpan;
3721 this.valueSpan = valueSpan;
3722 this.i18n = i18n;
3723 }
3724 visit(visitor) {
3725 return visitor.visitTextAttribute(this);
3726 }
3727 }
3728 class BoundAttribute {
3729 constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan, i18n) {
3730 this.name = name;
3731 this.type = type;
3732 this.securityContext = securityContext;
3733 this.value = value;
3734 this.unit = unit;
3735 this.sourceSpan = sourceSpan;
3736 this.keySpan = keySpan;
3737 this.valueSpan = valueSpan;
3738 this.i18n = i18n;
3739 }
3740 static fromBoundElementProperty(prop, i18n) {
3741 if (prop.keySpan === undefined) {
3742 throw new Error(`Unexpected state: keySpan must be defined for bound attributes but was not for ${prop.name}: ${prop.sourceSpan}`);
3743 }
3744 return new BoundAttribute(prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n);
3745 }
3746 visit(visitor) {
3747 return visitor.visitBoundAttribute(this);
3748 }
3749 }
3750 class BoundEvent {
3751 constructor(name, type, handler, target, phase, sourceSpan, handlerSpan, keySpan) {
3752 this.name = name;
3753 this.type = type;
3754 this.handler = handler;
3755 this.target = target;
3756 this.phase = phase;
3757 this.sourceSpan = sourceSpan;
3758 this.handlerSpan = handlerSpan;
3759 this.keySpan = keySpan;
3760 }
3761 static fromParsedEvent(event) {
3762 const target = event.type === 0 /* Regular */ ? event.targetOrPhase : null;
3763 const phase = event.type === 1 /* Animation */ ? event.targetOrPhase : null;
3764 if (event.keySpan === undefined) {
3765 throw new Error(`Unexpected state: keySpan must be defined for bound event but was not for ${event.name}: ${event.sourceSpan}`);
3766 }
3767 return new BoundEvent(event.name, event.type, event.handler, target, phase, event.sourceSpan, event.handlerSpan, event.keySpan);
3768 }
3769 visit(visitor) {
3770 return visitor.visitBoundEvent(this);
3771 }
3772 }
3773 class Element$1 {
3774 constructor(name, attributes, inputs, outputs, children, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
3775 this.name = name;
3776 this.attributes = attributes;
3777 this.inputs = inputs;
3778 this.outputs = outputs;
3779 this.children = children;
3780 this.references = references;
3781 this.sourceSpan = sourceSpan;
3782 this.startSourceSpan = startSourceSpan;
3783 this.endSourceSpan = endSourceSpan;
3784 this.i18n = i18n;
3785 }
3786 visit(visitor) {
3787 return visitor.visitElement(this);
3788 }
3789 }
3790 class Template {
3791 constructor(tagName, attributes, inputs, outputs, templateAttrs, children, references, variables, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
3792 this.tagName = tagName;
3793 this.attributes = attributes;
3794 this.inputs = inputs;
3795 this.outputs = outputs;
3796 this.templateAttrs = templateAttrs;
3797 this.children = children;
3798 this.references = references;
3799 this.variables = variables;
3800 this.sourceSpan = sourceSpan;
3801 this.startSourceSpan = startSourceSpan;
3802 this.endSourceSpan = endSourceSpan;
3803 this.i18n = i18n;
3804 }
3805 visit(visitor) {
3806 return visitor.visitTemplate(this);
3807 }
3808 }
3809 class Content {
3810 constructor(selector, attributes, sourceSpan, i18n) {
3811 this.selector = selector;
3812 this.attributes = attributes;
3813 this.sourceSpan = sourceSpan;
3814 this.i18n = i18n;
3815 this.name = 'ng-content';
3816 }
3817 visit(visitor) {
3818 return visitor.visitContent(this);
3819 }
3820 }
3821 class Variable {
3822 constructor(name, value, sourceSpan, keySpan, valueSpan) {
3823 this.name = name;
3824 this.value = value;
3825 this.sourceSpan = sourceSpan;
3826 this.keySpan = keySpan;
3827 this.valueSpan = valueSpan;
3828 }
3829 visit(visitor) {
3830 return visitor.visitVariable(this);
3831 }
3832 }
3833 class Reference$1 {
3834 constructor(name, value, sourceSpan, keySpan, valueSpan) {
3835 this.name = name;
3836 this.value = value;
3837 this.sourceSpan = sourceSpan;
3838 this.keySpan = keySpan;
3839 this.valueSpan = valueSpan;
3840 }
3841 visit(visitor) {
3842 return visitor.visitReference(this);
3843 }
3844 }
3845 class Icu$1 {
3846 constructor(vars, placeholders, sourceSpan, i18n) {
3847 this.vars = vars;
3848 this.placeholders = placeholders;
3849 this.sourceSpan = sourceSpan;
3850 this.i18n = i18n;
3851 }
3852 visit(visitor) {
3853 return visitor.visitIcu(this);
3854 }
3855 }
3856 class RecursiveVisitor {
3857 visitElement(element) {
3858 visitAll$1(this, element.attributes);
3859 visitAll$1(this, element.inputs);
3860 visitAll$1(this, element.outputs);
3861 visitAll$1(this, element.children);
3862 visitAll$1(this, element.references);
3863 }
3864 visitTemplate(template) {
3865 visitAll$1(this, template.attributes);
3866 visitAll$1(this, template.inputs);
3867 visitAll$1(this, template.outputs);
3868 visitAll$1(this, template.children);
3869 visitAll$1(this, template.references);
3870 visitAll$1(this, template.variables);
3871 }
3872 visitContent(content) { }
3873 visitVariable(variable) { }
3874 visitReference(reference) { }
3875 visitTextAttribute(attribute) { }
3876 visitBoundAttribute(attribute) { }
3877 visitBoundEvent(attribute) { }
3878 visitText(text) { }
3879 visitBoundText(text) { }
3880 visitIcu(icu) { }
3881 }
3882 function visitAll$1(visitor, nodes) {
3883 const result = [];
3884 if (visitor.visit) {
3885 for (const node of nodes) {
3886 visitor.visit(node) || node.visit(visitor);
3887 }
3888 }
3889 else {
3890 for (const node of nodes) {
3891 const newNode = node.visit(visitor);
3892 if (newNode) {
3893 result.push(newNode);
3894 }
3895 }
3896 }
3897 return result;
3898 }
3899
3900 /**
3901 * @license
3902 * Copyright Google LLC All Rights Reserved.
3903 *
3904 * Use of this source code is governed by an MIT-style license that can be
3905 * found in the LICENSE file at https://angular.io/license
3906 */
3907 class Message {
3908 /**
3909 * @param nodes message AST
3910 * @param placeholders maps placeholder names to static content and their source spans
3911 * @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
3912 * @param meaning
3913 * @param description
3914 * @param customId
3915 */
3916 constructor(nodes, placeholders, placeholderToMessage, meaning, description, customId) {
3917 this.nodes = nodes;
3918 this.placeholders = placeholders;
3919 this.placeholderToMessage = placeholderToMessage;
3920 this.meaning = meaning;
3921 this.description = description;
3922 this.customId = customId;
3923 this.id = this.customId;
3924 /** The ids to use if there are no custom id and if `i18nLegacyMessageIdFormat` is not empty */
3925 this.legacyIds = [];
3926 if (nodes.length) {
3927 this.sources = [{
3928 filePath: nodes[0].sourceSpan.start.file.url,
3929 startLine: nodes[0].sourceSpan.start.line + 1,
3930 startCol: nodes[0].sourceSpan.start.col + 1,
3931 endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1,
3932 endCol: nodes[0].sourceSpan.start.col + 1
3933 }];
3934 }
3935 else {
3936 this.sources = [];
3937 }
3938 }
3939 }
3940 class Text$1 {
3941 constructor(value, sourceSpan) {
3942 this.value = value;
3943 this.sourceSpan = sourceSpan;
3944 }
3945 visit(visitor, context) {
3946 return visitor.visitText(this, context);
3947 }
3948 }
3949 // TODO(vicb): do we really need this node (vs an array) ?
3950 class Container {
3951 constructor(children, sourceSpan) {
3952 this.children = children;
3953 this.sourceSpan = sourceSpan;
3954 }
3955 visit(visitor, context) {
3956 return visitor.visitContainer(this, context);
3957 }
3958 }
3959 class Icu {
3960 constructor(expression, type, cases, sourceSpan) {
3961 this.expression = expression;
3962 this.type = type;
3963 this.cases = cases;
3964 this.sourceSpan = sourceSpan;
3965 }
3966 visit(visitor, context) {
3967 return visitor.visitIcu(this, context);
3968 }
3969 }
3970 class TagPlaceholder {
3971 constructor(tag, attrs, startName, closeName, children, isVoid,
3972 // TODO sourceSpan should cover all (we need a startSourceSpan and endSourceSpan)
3973 sourceSpan, startSourceSpan, endSourceSpan) {
3974 this.tag = tag;
3975 this.attrs = attrs;
3976 this.startName = startName;
3977 this.closeName = closeName;
3978 this.children = children;
3979 this.isVoid = isVoid;
3980 this.sourceSpan = sourceSpan;
3981 this.startSourceSpan = startSourceSpan;
3982 this.endSourceSpan = endSourceSpan;
3983 }
3984 visit(visitor, context) {
3985 return visitor.visitTagPlaceholder(this, context);
3986 }
3987 }
3988 class Placeholder {
3989 constructor(value, name, sourceSpan) {
3990 this.value = value;
3991 this.name = name;
3992 this.sourceSpan = sourceSpan;
3993 }
3994 visit(visitor, context) {
3995 return visitor.visitPlaceholder(this, context);
3996 }
3997 }
3998 class IcuPlaceholder {
3999 constructor(value, name, sourceSpan) {
4000 this.value = value;
4001 this.name = name;
4002 this.sourceSpan = sourceSpan;
4003 }
4004 visit(visitor, context) {
4005 return visitor.visitIcuPlaceholder(this, context);
4006 }
4007 }
4008
4009 /**
4010 * @license
4011 * Copyright Google LLC All Rights Reserved.
4012 *
4013 * Use of this source code is governed by an MIT-style license that can be
4014 * found in the LICENSE file at https://angular.io/license
4015 */
4016 /**
4017 * Represents a big integer using a buffer of its individual digits, with the least significant
4018 * digit stored at the beginning of the array (little endian).
4019 *
4020 * For performance reasons, each instance is mutable. The addition operation can be done in-place
4021 * to reduce memory pressure of allocation for the digits array.
4022 */
4023 class BigInteger {
4024 /**
4025 * Creates a big integer using its individual digits in little endian storage.
4026 */
4027 constructor(digits) {
4028 this.digits = digits;
4029 }
4030 static zero() {
4031 return new BigInteger([0]);
4032 }
4033 static one() {
4034 return new BigInteger([1]);
4035 }
4036 /**
4037 * Creates a clone of this instance.
4038 */
4039 clone() {
4040 return new BigInteger(this.digits.slice());
4041 }
4042 /**
4043 * Returns a new big integer with the sum of `this` and `other` as its value. This does not mutate
4044 * `this` but instead returns a new instance, unlike `addToSelf`.
4045 */
4046 add(other) {
4047 const result = this.clone();
4048 result.addToSelf(other);
4049 return result;
4050 }
4051 /**
4052 * Adds `other` to the instance itself, thereby mutating its value.
4053 */
4054 addToSelf(other) {
4055 const maxNrOfDigits = Math.max(this.digits.length, other.digits.length);
4056 let carry = 0;
4057 for (let i = 0; i < maxNrOfDigits; i++) {
4058 let digitSum = carry;
4059 if (i < this.digits.length) {
4060 digitSum += this.digits[i];
4061 }
4062 if (i < other.digits.length) {
4063 digitSum += other.digits[i];
4064 }
4065 if (digitSum >= 10) {
4066 this.digits[i] = digitSum - 10;
4067 carry = 1;
4068 }
4069 else {
4070 this.digits[i] = digitSum;
4071 carry = 0;
4072 }
4073 }
4074 // Apply a remaining carry if needed.
4075 if (carry > 0) {
4076 this.digits[maxNrOfDigits] = 1;
4077 }
4078 }
4079 /**
4080 * Builds the decimal string representation of the big integer. As this is stored in
4081 * little endian, the digits are concatenated in reverse order.
4082 */
4083 toString() {
4084 let res = '';
4085 for (let i = this.digits.length - 1; i >= 0; i--) {
4086 res += this.digits[i];
4087 }
4088 return res;
4089 }
4090 }
4091 /**
4092 * Represents a big integer which is optimized for multiplication operations, as its power-of-twos
4093 * are memoized. See `multiplyBy()` for details on the multiplication algorithm.
4094 */
4095 class BigIntForMultiplication {
4096 constructor(value) {
4097 this.powerOfTwos = [value];
4098 }
4099 /**
4100 * Returns the big integer itself.
4101 */
4102 getValue() {
4103 return this.powerOfTwos[0];
4104 }
4105 /**
4106 * Computes the value for `num * b`, where `num` is a JS number and `b` is a big integer. The
4107 * value for `b` is represented by a storage model that is optimized for this computation.
4108 *
4109 * This operation is implemented in N(log2(num)) by continuous halving of the number, where the
4110 * least-significant bit (LSB) is tested in each iteration. If the bit is set, the bit's index is
4111 * used as exponent into the power-of-two multiplication of `b`.
4112 *
4113 * As an example, consider the multiplication num=42, b=1337. In binary 42 is 0b00101010 and the
4114 * algorithm unrolls into the following iterations:
4115 *
4116 * Iteration | num | LSB | b * 2^iter | Add? | product
4117 * -----------|------------|------|------------|------|--------
4118 * 0 | 0b00101010 | 0 | 1337 | No | 0
4119 * 1 | 0b00010101 | 1 | 2674 | Yes | 2674
4120 * 2 | 0b00001010 | 0 | 5348 | No | 2674
4121 * 3 | 0b00000101 | 1 | 10696 | Yes | 13370
4122 * 4 | 0b00000010 | 0 | 21392 | No | 13370
4123 * 5 | 0b00000001 | 1 | 42784 | Yes | 56154
4124 * 6 | 0b00000000 | 0 | 85568 | No | 56154
4125 *
4126 * The computed product of 56154 is indeed the correct result.
4127 *
4128 * The `BigIntForMultiplication` representation for a big integer provides memoized access to the
4129 * power-of-two values to reduce the workload in computing those values.
4130 */
4131 multiplyBy(num) {
4132 const product = BigInteger.zero();
4133 this.multiplyByAndAddTo(num, product);
4134 return product;
4135 }
4136 /**
4137 * See `multiplyBy()` for details. This function allows for the computed product to be added
4138 * directly to the provided result big integer.
4139 */
4140 multiplyByAndAddTo(num, result) {
4141 for (let exponent = 0; num !== 0; num = num >>> 1, exponent++) {
4142 if (num & 1) {
4143 const value = this.getMultipliedByPowerOfTwo(exponent);
4144 result.addToSelf(value);
4145 }
4146 }
4147 }
4148 /**
4149 * Computes and memoizes the big integer value for `this.number * 2^exponent`.
4150 */
4151 getMultipliedByPowerOfTwo(exponent) {
4152 // Compute the powers up until the requested exponent, where each value is computed from its
4153 // predecessor. This is simple as `this.number * 2^(exponent - 1)` only has to be doubled (i.e.
4154 // added to itself) to reach `this.number * 2^exponent`.
4155 for (let i = this.powerOfTwos.length; i <= exponent; i++) {
4156 const previousPower = this.powerOfTwos[i - 1];
4157 this.powerOfTwos[i] = previousPower.add(previousPower);
4158 }
4159 return this.powerOfTwos[exponent];
4160 }
4161 }
4162 /**
4163 * Represents an exponentiation operation for the provided base, of which exponents are computed and
4164 * memoized. The results are represented by a `BigIntForMultiplication` which is tailored for
4165 * multiplication operations by memoizing the power-of-twos. This effectively results in a matrix
4166 * representation that is lazily computed upon request.
4167 */
4168 class BigIntExponentiation {
4169 constructor(base) {
4170 this.base = base;
4171 this.exponents = [new BigIntForMultiplication(BigInteger.one())];
4172 }
4173 /**
4174 * Compute the value for `this.base^exponent`, resulting in a big integer that is optimized for
4175 * further multiplication operations.
4176 */
4177 toThePowerOf(exponent) {
4178 // Compute the results up until the requested exponent, where every value is computed from its
4179 // predecessor. This is because `this.base^(exponent - 1)` only has to be multiplied by `base`
4180 // to reach `this.base^exponent`.
4181 for (let i = this.exponents.length; i <= exponent; i++) {
4182 const value = this.exponents[i - 1].multiplyBy(this.base);
4183 this.exponents[i] = new BigIntForMultiplication(value);
4184 }
4185 return this.exponents[exponent];
4186 }
4187 }
4188
4189 /**
4190 * @license
4191 * Copyright Google LLC All Rights Reserved.
4192 *
4193 * Use of this source code is governed by an MIT-style license that can be
4194 * found in the LICENSE file at https://angular.io/license
4195 */
4196 /**
4197 * Compute the message id using the XLIFF1 digest.
4198 */
4199 function computeDigest(message) {
4200 return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
4201 }
4202 /**
4203 * Return the message id or compute it using the XLIFF2/XMB/$localize digest.
4204 */
4205 function decimalDigest(message) {
4206 return message.id || computeDecimalDigest(message);
4207 }
4208 /**
4209 * Compute the message id using the XLIFF2/XMB/$localize digest.
4210 */
4211 function computeDecimalDigest(message) {
4212 const visitor = new _SerializerIgnoreIcuExpVisitor();
4213 const parts = message.nodes.map(a => a.visit(visitor, null));
4214 return computeMsgId(parts.join(''), message.meaning);
4215 }
4216 /**
4217 * Serialize the i18n ast to something xml-like in order to generate an UID.
4218 *
4219 * The visitor is also used in the i18n parser tests
4220 *
4221 * @internal
4222 */
4223 class _SerializerVisitor {
4224 visitText(text, context) {
4225 return text.value;
4226 }
4227 visitContainer(container, context) {
4228 return `[${container.children.map(child => child.visit(this)).join(', ')}]`;
4229 }
4230 visitIcu(icu, context) {
4231 const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
4232 return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`;
4233 }
4234 visitTagPlaceholder(ph, context) {
4235 return ph.isVoid ?
4236 `<ph tag name="${ph.startName}"/>` :
4237 `<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`;
4238 }
4239 visitPlaceholder(ph, context) {
4240 return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`;
4241 }
4242 visitIcuPlaceholder(ph, context) {
4243 return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`;
4244 }
4245 }
4246 const serializerVisitor$2 = new _SerializerVisitor();
4247 function serializeNodes(nodes) {
4248 return nodes.map(a => a.visit(serializerVisitor$2, null));
4249 }
4250 /**
4251 * Serialize the i18n ast to something xml-like in order to generate an UID.
4252 *
4253 * Ignore the ICU expressions so that message IDs stays identical if only the expression changes.
4254 *
4255 * @internal
4256 */
4257 class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor {
4258 visitIcu(icu, context) {
4259 let strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
4260 // Do not take the expression into account
4261 return `{${icu.type}, ${strCases.join(', ')}}`;
4262 }
4263 }
4264 /**
4265 * Compute the SHA1 of the given string
4266 *
4267 * see https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
4268 *
4269 * WARNING: this function has not been designed not tested with security in mind.
4270 * DO NOT USE IT IN A SECURITY SENSITIVE CONTEXT.
4271 */
4272 function sha1(str) {
4273 const utf8 = utf8Encode(str);
4274 const words32 = bytesToWords32(utf8, Endian.Big);
4275 const len = utf8.length * 8;
4276 const w = newArray(80);
4277 let a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476, e = 0xc3d2e1f0;
4278 words32[len >> 5] |= 0x80 << (24 - len % 32);
4279 words32[((len + 64 >> 9) << 4) + 15] = len;
4280 for (let i = 0; i < words32.length; i += 16) {
4281 const h0 = a, h1 = b, h2 = c, h3 = d, h4 = e;
4282 for (let j = 0; j < 80; j++) {
4283 if (j < 16) {
4284 w[j] = words32[i + j];
4285 }
4286 else {
4287 w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
4288 }
4289 const fkVal = fk(j, b, c, d);
4290 const f = fkVal[0];
4291 const k = fkVal[1];
4292 const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32);
4293 e = d;
4294 d = c;
4295 c = rol32(b, 30);
4296 b = a;
4297 a = temp;
4298 }
4299 a = add32(a, h0);
4300 b = add32(b, h1);
4301 c = add32(c, h2);
4302 d = add32(d, h3);
4303 e = add32(e, h4);
4304 }
4305 return bytesToHexString(words32ToByteString([a, b, c, d, e]));
4306 }
4307 function fk(index, b, c, d) {
4308 if (index < 20) {
4309 return [(b & c) | (~b & d), 0x5a827999];
4310 }
4311 if (index < 40) {
4312 return [b ^ c ^ d, 0x6ed9eba1];
4313 }
4314 if (index < 60) {
4315 return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
4316 }
4317 return [b ^ c ^ d, 0xca62c1d6];
4318 }
4319 /**
4320 * Compute the fingerprint of the given string
4321 *
4322 * The output is 64 bit number encoded as a decimal string
4323 *
4324 * based on:
4325 * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java
4326 */
4327 function fingerprint(str) {
4328 const utf8 = utf8Encode(str);
4329 let hi = hash32(utf8, 0);
4330 let lo = hash32(utf8, 102072);
4331 if (hi == 0 && (lo == 0 || lo == 1)) {
4332 hi = hi ^ 0x130f9bef;
4333 lo = lo ^ -0x6b5f56d8;
4334 }
4335 return [hi, lo];
4336 }
4337 function computeMsgId(msg, meaning = '') {
4338 let msgFingerprint = fingerprint(msg);
4339 if (meaning) {
4340 const meaningFingerprint = fingerprint(meaning);
4341 msgFingerprint = add64(rol64(msgFingerprint, 1), meaningFingerprint);
4342 }
4343 const hi = msgFingerprint[0];
4344 const lo = msgFingerprint[1];
4345 return wordsToDecimalString(hi & 0x7fffffff, lo);
4346 }
4347 function hash32(bytes, c) {
4348 let a = 0x9e3779b9, b = 0x9e3779b9;
4349 let i;
4350 const len = bytes.length;
4351 for (i = 0; i + 12 <= len; i += 12) {
4352 a = add32(a, wordAt(bytes, i, Endian.Little));
4353 b = add32(b, wordAt(bytes, i + 4, Endian.Little));
4354 c = add32(c, wordAt(bytes, i + 8, Endian.Little));
4355 const res = mix(a, b, c);
4356 a = res[0], b = res[1], c = res[2];
4357 }
4358 a = add32(a, wordAt(bytes, i, Endian.Little));
4359 b = add32(b, wordAt(bytes, i + 4, Endian.Little));
4360 // the first byte of c is reserved for the length
4361 c = add32(c, len);
4362 c = add32(c, wordAt(bytes, i + 8, Endian.Little) << 8);
4363 return mix(a, b, c)[2];
4364 }
4365 // clang-format off
4366 function mix(a, b, c) {
4367 a = sub32(a, b);
4368 a = sub32(a, c);
4369 a ^= c >>> 13;
4370 b = sub32(b, c);
4371 b = sub32(b, a);
4372 b ^= a << 8;
4373 c = sub32(c, a);
4374 c = sub32(c, b);
4375 c ^= b >>> 13;
4376 a = sub32(a, b);
4377 a = sub32(a, c);
4378 a ^= c >>> 12;
4379 b = sub32(b, c);
4380 b = sub32(b, a);
4381 b ^= a << 16;
4382 c = sub32(c, a);
4383 c = sub32(c, b);
4384 c ^= b >>> 5;
4385 a = sub32(a, b);
4386 a = sub32(a, c);
4387 a ^= c >>> 3;
4388 b = sub32(b, c);
4389 b = sub32(b, a);
4390 b ^= a << 10;
4391 c = sub32(c, a);
4392 c = sub32(c, b);
4393 c ^= b >>> 15;
4394 return [a, b, c];
4395 }
4396 // clang-format on
4397 // Utils
4398 var Endian;
4399 (function (Endian) {
4400 Endian[Endian["Little"] = 0] = "Little";
4401 Endian[Endian["Big"] = 1] = "Big";
4402 })(Endian || (Endian = {}));
4403 function add32(a, b) {
4404 return add32to64(a, b)[1];
4405 }
4406 function add32to64(a, b) {
4407 const low = (a & 0xffff) + (b & 0xffff);
4408 const high = (a >>> 16) + (b >>> 16) + (low >>> 16);
4409 return [high >>> 16, (high << 16) | (low & 0xffff)];
4410 }
4411 function add64(a, b) {
4412 const ah = a[0], al = a[1];
4413 const bh = b[0], bl = b[1];
4414 const result = add32to64(al, bl);
4415 const carry = result[0];
4416 const l = result[1];
4417 const h = add32(add32(ah, bh), carry);
4418 return [h, l];
4419 }
4420 function sub32(a, b) {
4421 const low = (a & 0xffff) - (b & 0xffff);
4422 const high = (a >> 16) - (b >> 16) + (low >> 16);
4423 return (high << 16) | (low & 0xffff);
4424 }
4425 // Rotate a 32b number left `count` position
4426 function rol32(a, count) {
4427 return (a << count) | (a >>> (32 - count));
4428 }
4429 // Rotate a 64b number left `count` position
4430 function rol64(num, count) {
4431 const hi = num[0], lo = num[1];
4432 const h = (hi << count) | (lo >>> (32 - count));
4433 const l = (lo << count) | (hi >>> (32 - count));
4434 return [h, l];
4435 }
4436 function bytesToWords32(bytes, endian) {
4437 const size = (bytes.length + 3) >>> 2;
4438 const words32 = [];
4439 for (let i = 0; i < size; i++) {
4440 words32[i] = wordAt(bytes, i * 4, endian);
4441 }
4442 return words32;
4443 }
4444 function byteAt(bytes, index) {
4445 return index >= bytes.length ? 0 : bytes[index];
4446 }
4447 function wordAt(bytes, index, endian) {
4448 let word = 0;
4449 if (endian === Endian.Big) {
4450 for (let i = 0; i < 4; i++) {
4451 word += byteAt(bytes, index + i) << (24 - 8 * i);
4452 }
4453 }
4454 else {
4455 for (let i = 0; i < 4; i++) {
4456 word += byteAt(bytes, index + i) << 8 * i;
4457 }
4458 }
4459 return word;
4460 }
4461 function words32ToByteString(words32) {
4462 return words32.reduce((bytes, word) => bytes.concat(word32ToByteString(word)), []);
4463 }
4464 function word32ToByteString(word) {
4465 let bytes = [];
4466 for (let i = 0; i < 4; i++) {
4467 bytes.push((word >>> 8 * (3 - i)) & 0xff);
4468 }
4469 return bytes;
4470 }
4471 function bytesToHexString(bytes) {
4472 let hex = '';
4473 for (let i = 0; i < bytes.length; i++) {
4474 const b = byteAt(bytes, i);
4475 hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16);
4476 }
4477 return hex.toLowerCase();
4478 }
4479 /**
4480 * Create a shared exponentiation pool for base-256 computations. This shared pool provides memoized
4481 * power-of-256 results with memoized power-of-two computations for efficient multiplication.
4482 *
4483 * For our purposes, this can be safely stored as a global without memory concerns. The reason is
4484 * that we encode two words, so only need the 0th (for the low word) and 4th (for the high word)
4485 * exponent.
4486 */
4487 const base256 = new BigIntExponentiation(256);
4488 /**
4489 * Represents two 32-bit words as a single decimal number. This requires a big integer storage
4490 * model as JS numbers are not accurate enough to represent the 64-bit number.
4491 *
4492 * Based on https://www.danvk.org/hex2dec.html
4493 */
4494 function wordsToDecimalString(hi, lo) {
4495 // Encode the four bytes in lo in the lower digits of the decimal number.
4496 // Note: the multiplication results in lo itself but represented by a big integer using its
4497 // decimal digits.
4498 const decimal = base256.toThePowerOf(0).multiplyBy(lo);
4499 // Encode the four bytes in hi above the four lo bytes. lo is a maximum of (2^8)^4, which is why
4500 // this multiplication factor is applied.
4501 base256.toThePowerOf(4).multiplyByAndAddTo(hi, decimal);
4502 return decimal.toString();
4503 }
4504
4505 /**
4506 * @license
4507 * Copyright Google LLC All Rights Reserved.
4508 *
4509 * Use of this source code is governed by an MIT-style license that can be
4510 * found in the LICENSE file at https://angular.io/license
4511 */
4512 // XMB/XTB placeholders can only contain A-Z, 0-9 and _
4513 function toPublicName(internalName) {
4514 return internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
4515 }
4516
4517 /**
4518 * @license
4519 * Copyright Google LLC All Rights Reserved.
4520 *
4521 * Use of this source code is governed by an MIT-style license that can be
4522 * found in the LICENSE file at https://angular.io/license
4523 */
4524 /* Closure variables holding messages must be named `MSG_[A-Z0-9]+` */
4525 const CLOSURE_TRANSLATION_VAR_PREFIX = 'MSG_';
4526 /**
4527 * Prefix for non-`goog.getMsg` i18n-related vars.
4528 * Note: the prefix uses lowercase characters intentionally due to a Closure behavior that
4529 * considers variables like `I18N_0` as constants and throws an error when their value changes.
4530 */
4531 const TRANSLATION_VAR_PREFIX = 'i18n_';
4532 /** Name of the i18n attributes **/
4533 const I18N_ATTR = 'i18n';
4534 const I18N_ATTR_PREFIX = 'i18n-';
4535 /** Prefix of var expressions used in ICUs */
4536 const I18N_ICU_VAR_PREFIX = 'VAR_';
4537 /** Prefix of ICU expressions for post processing */
4538 const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
4539 /** Placeholder wrapper for i18n expressions **/
4540 const I18N_PLACEHOLDER_SYMBOL = '�';
4541 function isI18nAttribute(name) {
4542 return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX);
4543 }
4544 function isI18nRootNode(meta) {
4545 return meta instanceof Message;
4546 }
4547 function isSingleI18nIcu(meta) {
4548 return isI18nRootNode(meta) && meta.nodes.length === 1 && meta.nodes[0] instanceof Icu;
4549 }
4550 function hasI18nMeta(node) {
4551 return !!node.i18n;
4552 }
4553 function hasI18nAttrs(element) {
4554 return element.attrs.some((attr) => isI18nAttribute(attr.name));
4555 }
4556 function icuFromI18nMessage(message) {
4557 return message.nodes[0];
4558 }
4559 function wrapI18nPlaceholder(content, contextId = 0) {
4560 const blockId = contextId > 0 ? `:${contextId}` : '';
4561 return `${I18N_PLACEHOLDER_SYMBOL}${content}${blockId}${I18N_PLACEHOLDER_SYMBOL}`;
4562 }
4563 function assembleI18nBoundString(strings, bindingStartIndex = 0, contextId = 0) {
4564 if (!strings.length)
4565 return '';
4566 let acc = '';
4567 const lastIdx = strings.length - 1;
4568 for (let i = 0; i < lastIdx; i++) {
4569 acc += `${strings[i]}${wrapI18nPlaceholder(bindingStartIndex + i, contextId)}`;
4570 }
4571 acc += strings[lastIdx];
4572 return acc;
4573 }
4574 function getSeqNumberGenerator(startsAt = 0) {
4575 let current = startsAt;
4576 return () => current++;
4577 }
4578 function placeholdersToParams(placeholders) {
4579 const params = {};
4580 placeholders.forEach((values, key) => {
4581 params[key] = literal$1(values.length > 1 ? `[${values.join('|')}]` : values[0]);
4582 });
4583 return params;
4584 }
4585 function updatePlaceholderMap(map, name, ...values) {
4586 const current = map.get(name) || [];
4587 current.push(...values);
4588 map.set(name, current);
4589 }
4590 function assembleBoundTextPlaceholders(meta, bindingStartIndex = 0, contextId = 0) {
4591 const startIdx = bindingStartIndex;
4592 const placeholders = new Map();
4593 const node = meta instanceof Message ? meta.nodes.find(node => node instanceof Container) : meta;
4594 if (node) {
4595 node
4596 .children
4597 .filter((child) => child instanceof Placeholder)
4598 .forEach((child, idx) => {
4599 const content = wrapI18nPlaceholder(startIdx + idx, contextId);
4600 updatePlaceholderMap(placeholders, child.name, content);
4601 });
4602 }
4603 return placeholders;
4604 }
4605 /**
4606 * Format the placeholder names in a map of placeholders to expressions.
4607 *
4608 * The placeholder names are converted from "internal" format (e.g. `START_TAG_DIV_1`) to "external"
4609 * format (e.g. `startTagDiv_1`).
4610 *
4611 * @param params A map of placeholder names to expressions.
4612 * @param useCamelCase whether to camelCase the placeholder name when formatting.
4613 * @returns A new map of formatted placeholder names to expressions.
4614 */
4615 function i18nFormatPlaceholderNames(params = {}, useCamelCase) {
4616 const _params = {};
4617 if (params && Object.keys(params).length) {
4618 Object.keys(params).forEach(key => _params[formatI18nPlaceholderName(key, useCamelCase)] = params[key]);
4619 }
4620 return _params;
4621 }
4622 /**
4623 * Converts internal placeholder names to public-facing format
4624 * (for example to use in goog.getMsg call).
4625 * Example: `START_TAG_DIV_1` is converted to `startTagDiv_1`.
4626 *
4627 * @param name The placeholder name that should be formatted
4628 * @returns Formatted placeholder name
4629 */
4630 function formatI18nPlaceholderName(name, useCamelCase = true) {
4631 const publicName = toPublicName(name);
4632 if (!useCamelCase) {
4633 return publicName;
4634 }
4635 const chunks = publicName.split('_');
4636 if (chunks.length === 1) {
4637 // if no "_" found - just lowercase the value
4638 return name.toLowerCase();
4639 }
4640 let postfix;
4641 // eject last element if it's a number
4642 if (/^\d+$/.test(chunks[chunks.length - 1])) {
4643 postfix = chunks.pop();
4644 }
4645 let raw = chunks.shift().toLowerCase();
4646 if (chunks.length) {
4647 raw += chunks.map(c => c.charAt(0).toUpperCase() + c.slice(1).toLowerCase()).join('');
4648 }
4649 return postfix ? `${raw}_${postfix}` : raw;
4650 }
4651 /**
4652 * Generates a prefix for translation const name.
4653 *
4654 * @param extra Additional local prefix that should be injected into translation var name
4655 * @returns Complete translation const prefix
4656 */
4657 function getTranslationConstPrefix(extra) {
4658 return `${CLOSURE_TRANSLATION_VAR_PREFIX}${extra}`.toUpperCase();
4659 }
4660 /**
4661 * Generate AST to declare a variable. E.g. `var I18N_1;`.
4662 * @param variable the name of the variable to declare.
4663 */
4664 function declareI18nVariable(variable) {
4665 return new DeclareVarStmt(variable.name, undefined, INFERRED_TYPE, undefined, variable.sourceSpan);
4666 }
4667
4668 /**
4669 * @license
4670 * Copyright Google LLC All Rights Reserved.
4671 *
4672 * Use of this source code is governed by an MIT-style license that can be
4673 * found in the LICENSE file at https://angular.io/license
4674 */
4675 /**
4676 * Checks whether an object key contains potentially unsafe chars, thus the key should be wrapped in
4677 * quotes. Note: we do not wrap all keys into quotes, as it may have impact on minification and may
4678 * bot work in some cases when object keys are mangled by minifier.
4679 *
4680 * TODO(FW-1136): this is a temporary solution, we need to come up with a better way of working with
4681 * inputs that contain potentially unsafe chars.
4682 */
4683 const UNSAFE_OBJECT_KEY_NAME_REGEXP = /[-.]/;
4684 /** Name of the temporary to use during data binding */
4685 const TEMPORARY_NAME = '_t';
4686 /** Name of the context parameter passed into a template function */
4687 const CONTEXT_NAME = 'ctx';
4688 /** Name of the RenderFlag passed into a template function */
4689 const RENDER_FLAGS = 'rf';
4690 /** The prefix reference variables */
4691 const REFERENCE_PREFIX = '_r';
4692 /** The name of the implicit context reference */
4693 const IMPLICIT_REFERENCE = '$implicit';
4694 /** Non bindable attribute name **/
4695 const NON_BINDABLE_ATTR = 'ngNonBindable';
4696 /** Name for the variable keeping track of the context returned by `ɵɵrestoreView`. */
4697 const RESTORED_VIEW_CONTEXT_NAME = 'restoredCtx';
4698 /**
4699 * Creates an allocator for a temporary variable.
4700 *
4701 * A variable declaration is added to the statements the first time the allocator is invoked.
4702 */
4703 function temporaryAllocator(statements, name) {
4704 let temp = null;
4705 return () => {
4706 if (!temp) {
4707 statements.push(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE));
4708 temp = variable(name);
4709 }
4710 return temp;
4711 };
4712 }
4713 function unsupported(feature) {
4714 if (this) {
4715 throw new Error(`Builder ${this.constructor.name} doesn't support ${feature} yet`);
4716 }
4717 throw new Error(`Feature ${feature} is not supported yet`);
4718 }
4719 function invalid(arg) {
4720 throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
4721 }
4722 function asLiteral(value) {
4723 if (Array.isArray(value)) {
4724 return literalArr(value.map(asLiteral));
4725 }
4726 return literal$1(value, INFERRED_TYPE);
4727 }
4728 function conditionallyCreateMapObjectLiteral(keys, keepDeclared) {
4729 if (Object.getOwnPropertyNames(keys).length > 0) {
4730 return mapToExpression(keys, keepDeclared);
4731 }
4732 return null;
4733 }
4734 function mapToExpression(map, keepDeclared) {
4735 return literalMap(Object.getOwnPropertyNames(map).map(key => {
4736 // canonical syntax: `dirProp: publicProp`
4737 // if there is no `:`, use dirProp = elProp
4738 const value = map[key];
4739 let declaredName;
4740 let publicName;
4741 let minifiedName;
4742 let needsDeclaredName;
4743 if (Array.isArray(value)) {
4744 [publicName, declaredName] = value;
4745 minifiedName = key;
4746 needsDeclaredName = publicName !== declaredName;
4747 }
4748 else {
4749 [declaredName, publicName] = splitAtColon(key, [key, value]);
4750 minifiedName = declaredName;
4751 // Only include the declared name if extracted from the key, i.e. the key contains a colon.
4752 // Otherwise the declared name should be omitted even if it is different from the public name,
4753 // as it may have already been minified.
4754 needsDeclaredName = publicName !== declaredName && key.includes(':');
4755 }
4756 return {
4757 key: minifiedName,
4758 // put quotes around keys that contain potentially unsafe characters
4759 quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(minifiedName),
4760 value: (keepDeclared && needsDeclaredName) ?
4761 literalArr([asLiteral(publicName), asLiteral(declaredName)]) :
4762 asLiteral(publicName)
4763 };
4764 }));
4765 }
4766 /**
4767 * Remove trailing null nodes as they are implied.
4768 */
4769 function trimTrailingNulls(parameters) {
4770 while (isNull(parameters[parameters.length - 1])) {
4771 parameters.pop();
4772 }
4773 return parameters;
4774 }
4775 function getQueryPredicate(query, constantPool) {
4776 if (Array.isArray(query.predicate)) {
4777 let predicate = [];
4778 query.predicate.forEach((selector) => {
4779 // Each item in predicates array may contain strings with comma-separated refs
4780 // (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them
4781 // as separate array entities
4782 const selectors = selector.split(',').map(token => literal$1(token.trim()));
4783 predicate.push(...selectors);
4784 });
4785 return constantPool.getConstLiteral(literalArr(predicate), true);
4786 }
4787 else {
4788 // The original predicate may have been wrapped in a `forwardRef()` call.
4789 switch (query.predicate.forwardRef) {
4790 case 0 /* None */:
4791 case 2 /* Unwrapped */:
4792 return query.predicate.expression;
4793 case 1 /* Wrapped */:
4794 return importExpr(Identifiers$1.resolveForwardRef).callFn([query.predicate.expression]);
4795 }
4796 }
4797 }
4798 /**
4799 * A representation for an object literal used during codegen of definition objects. The generic
4800 * type `T` allows to reference a documented type of the generated structure, such that the
4801 * property names that are set can be resolved to their documented declaration.
4802 */
4803 class DefinitionMap {
4804 constructor() {
4805 this.values = [];
4806 }
4807 set(key, value) {
4808 if (value) {
4809 this.values.push({ key: key, value, quoted: false });
4810 }
4811 }
4812 toLiteralMap() {
4813 return literalMap(this.values);
4814 }
4815 }
4816 /**
4817 * Extract a map of properties to values for a given element or template node, which can be used
4818 * by the directive matching machinery.
4819 *
4820 * @param elOrTpl the element or template in question
4821 * @return an object set up for directive matching. For attributes on the element/template, this
4822 * object maps a property name to its (static) value. For any bindings, this map simply maps the
4823 * property name to an empty string.
4824 */
4825 function getAttrsForDirectiveMatching(elOrTpl) {
4826 const attributesMap = {};
4827 if (elOrTpl instanceof Template && elOrTpl.tagName !== 'ng-template') {
4828 elOrTpl.templateAttrs.forEach(a => attributesMap[a.name] = '');
4829 }
4830 else {
4831 elOrTpl.attributes.forEach(a => {
4832 if (!isI18nAttribute(a.name)) {
4833 attributesMap[a.name] = a.value;
4834 }
4835 });
4836 elOrTpl.inputs.forEach(i => {
4837 attributesMap[i.name] = '';
4838 });
4839 elOrTpl.outputs.forEach(o => {
4840 attributesMap[o.name] = '';
4841 });
4842 }
4843 return attributesMap;
4844 }
4845 /** Returns a call expression to a chained instruction, e.g. `property(params[0])(params[1])`. */
4846 function chainedInstruction(reference, calls, span) {
4847 let expression = importExpr(reference, null, span);
4848 if (calls.length > 0) {
4849 for (let i = 0; i < calls.length; i++) {
4850 expression = expression.callFn(calls[i], span);
4851 }
4852 }
4853 else {
4854 // Add a blank invocation, in case the `calls` array is empty.
4855 expression = expression.callFn([], span);
4856 }
4857 return expression;
4858 }
4859 /**
4860 * Gets the number of arguments expected to be passed to a generated instruction in the case of
4861 * interpolation instructions.
4862 * @param interpolation An interpolation ast
4863 */
4864 function getInterpolationArgsLength(interpolation) {
4865 const { expressions, strings } = interpolation;
4866 if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
4867 // If the interpolation has one interpolated value, but the prefix and suffix are both empty
4868 // strings, we only pass one argument, to a special instruction like `propertyInterpolate` or
4869 // `textInterpolate`.
4870 return 1;
4871 }
4872 else {
4873 return expressions.length + strings.length;
4874 }
4875 }
4876
4877 /**
4878 * @license
4879 * Copyright Google LLC All Rights Reserved.
4880 *
4881 * Use of this source code is governed by an MIT-style license that can be
4882 * found in the LICENSE file at https://angular.io/license
4883 */
4884 function compileInjectable(meta, resolveForwardRefs) {
4885 let result = null;
4886 const factoryMeta = {
4887 name: meta.name,
4888 type: meta.type,
4889 internalType: meta.internalType,
4890 typeArgumentCount: meta.typeArgumentCount,
4891 deps: [],
4892 target: FactoryTarget$1.Injectable,
4893 };
4894 if (meta.useClass !== undefined) {
4895 // meta.useClass has two modes of operation. Either deps are specified, in which case `new` is
4896 // used to instantiate the class with dependencies injected, or deps are not specified and
4897 // the factory of the class is used to instantiate it.
4898 //
4899 // A special case exists for useClass: Type where Type is the injectable type itself and no
4900 // deps are specified, in which case 'useClass' is effectively ignored.
4901 const useClassOnSelf = meta.useClass.expression.isEquivalent(meta.internalType);
4902 let deps = undefined;
4903 if (meta.deps !== undefined) {
4904 deps = meta.deps;
4905 }
4906 if (deps !== undefined) {
4907 // factory: () => new meta.useClass(...deps)
4908 result = compileFactoryFunction({
4909 ...factoryMeta,
4910 delegate: meta.useClass.expression,
4911 delegateDeps: deps,
4912 delegateType: R3FactoryDelegateType.Class,
4913 });
4914 }
4915 else if (useClassOnSelf) {
4916 result = compileFactoryFunction(factoryMeta);
4917 }
4918 else {
4919 result = {
4920 statements: [],
4921 expression: delegateToFactory(meta.type.value, meta.useClass.expression, resolveForwardRefs)
4922 };
4923 }
4924 }
4925 else if (meta.useFactory !== undefined) {
4926 if (meta.deps !== undefined) {
4927 result = compileFactoryFunction({
4928 ...factoryMeta,
4929 delegate: meta.useFactory,
4930 delegateDeps: meta.deps || [],
4931 delegateType: R3FactoryDelegateType.Function,
4932 });
4933 }
4934 else {
4935 result = {
4936 statements: [],
4937 expression: fn([], [new ReturnStatement(meta.useFactory.callFn([]))])
4938 };
4939 }
4940 }
4941 else if (meta.useValue !== undefined) {
4942 // Note: it's safe to use `meta.useValue` instead of the `USE_VALUE in meta` check used for
4943 // client code because meta.useValue is an Expression which will be defined even if the actual
4944 // value is undefined.
4945 result = compileFactoryFunction({
4946 ...factoryMeta,
4947 expression: meta.useValue.expression,
4948 });
4949 }
4950 else if (meta.useExisting !== undefined) {
4951 // useExisting is an `inject` call on the existing token.
4952 result = compileFactoryFunction({
4953 ...factoryMeta,
4954 expression: importExpr(Identifiers$1.inject).callFn([meta.useExisting.expression]),
4955 });
4956 }
4957 else {
4958 result = {
4959 statements: [],
4960 expression: delegateToFactory(meta.type.value, meta.internalType, resolveForwardRefs)
4961 };
4962 }
4963 const token = meta.internalType;
4964 const injectableProps = new DefinitionMap();
4965 injectableProps.set('token', token);
4966 injectableProps.set('factory', result.expression);
4967 // Only generate providedIn property if it has a non-null value
4968 if (meta.providedIn.expression.value !== null) {
4969 injectableProps.set('providedIn', convertFromMaybeForwardRefExpression(meta.providedIn));
4970 }
4971 const expression = importExpr(Identifiers$1.ɵɵdefineInjectable)
4972 .callFn([injectableProps.toLiteralMap()], undefined, true);
4973 return {
4974 expression,
4975 type: createInjectableType(meta),
4976 statements: result.statements,
4977 };
4978 }
4979 function createInjectableType(meta) {
4980 return new ExpressionType(importExpr(Identifiers$1.InjectableDeclaration, [typeWithParameters(meta.type.type, meta.typeArgumentCount)]));
4981 }
4982 function delegateToFactory(type, internalType, unwrapForwardRefs) {
4983 if (type.node === internalType.node) {
4984 // The types are the same, so we can simply delegate directly to the type's factory.
4985 // ```
4986 // factory: type.ɵfac
4987 // ```
4988 return internalType.prop('ɵfac');
4989 }
4990 if (!unwrapForwardRefs) {
4991 // The type is not wrapped in a `forwardRef()`, so we create a simple factory function that
4992 // accepts a sub-type as an argument.
4993 // ```
4994 // factory: function(t) { return internalType.ɵfac(t); }
4995 // ```
4996 return createFactoryFunction(internalType);
4997 }
4998 // The internalType is actually wrapped in a `forwardRef()` so we need to resolve that before
4999 // calling its factory.
5000 // ```
5001 // factory: function(t) { return core.resolveForwardRef(type).ɵfac(t); }
5002 // ```
5003 const unwrappedType = importExpr(Identifiers$1.resolveForwardRef).callFn([internalType]);
5004 return createFactoryFunction(unwrappedType);
5005 }
5006 function createFactoryFunction(type) {
5007 return fn([new FnParam('t', DYNAMIC_TYPE)], [new ReturnStatement(type.prop('ɵfac').callFn([variable('t')]))]);
5008 }
5009
5010 /**
5011 * @license
5012 * Copyright Google LLC All Rights Reserved.
5013 *
5014 * Use of this source code is governed by an MIT-style license that can be
5015 * found in the LICENSE file at https://angular.io/license
5016 */
5017 const UNUSABLE_INTERPOLATION_REGEXPS = [
5018 /^\s*$/,
5019 /[<>]/,
5020 /^[{}]$/,
5021 /&(#|[a-z])/i,
5022 /^\/\//, // comment
5023 ];
5024 function assertInterpolationSymbols(identifier, value) {
5025 if (value != null && !(Array.isArray(value) && value.length == 2)) {
5026 throw new Error(`Expected '${identifier}' to be an array, [start, end].`);
5027 }
5028 else if (value != null) {
5029 const start = value[0];
5030 const end = value[1];
5031 // Check for unusable interpolation symbols
5032 UNUSABLE_INTERPOLATION_REGEXPS.forEach(regexp => {
5033 if (regexp.test(start) || regexp.test(end)) {
5034 throw new Error(`['${start}', '${end}'] contains unusable interpolation symbol.`);
5035 }
5036 });
5037 }
5038 }
5039
5040 /**
5041 * @license
5042 * Copyright Google LLC All Rights Reserved.
5043 *
5044 * Use of this source code is governed by an MIT-style license that can be
5045 * found in the LICENSE file at https://angular.io/license
5046 */
5047 class InterpolationConfig {
5048 constructor(start, end) {
5049 this.start = start;
5050 this.end = end;
5051 }
5052 static fromArray(markers) {
5053 if (!markers) {
5054 return DEFAULT_INTERPOLATION_CONFIG;
5055 }
5056 assertInterpolationSymbols('interpolation', markers);
5057 return new InterpolationConfig(markers[0], markers[1]);
5058 }
5059 }
5060 const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}');
5061
5062 /**
5063 * @license
5064 * Copyright Google LLC All Rights Reserved.
5065 *
5066 * Use of this source code is governed by an MIT-style license that can be
5067 * found in the LICENSE file at https://angular.io/license
5068 */
5069 /**
5070 * A token representing the a reference to a static type.
5071 *
5072 * This token is unique for a filePath and name and can be used as a hash table key.
5073 */
5074 class StaticSymbol {
5075 constructor(filePath, name, members) {
5076 this.filePath = filePath;
5077 this.name = name;
5078 this.members = members;
5079 }
5080 assertNoMembers() {
5081 if (this.members.length) {
5082 throw new Error(`Illegal state: symbol without members expected, but got ${JSON.stringify(this)}.`);
5083 }
5084 }
5085 }
5086
5087 /**
5088 * @license
5089 * Copyright Google LLC All Rights Reserved.
5090 *
5091 * Use of this source code is governed by an MIT-style license that can be
5092 * found in the LICENSE file at https://angular.io/license
5093 */
5094 const $EOF = 0;
5095 const $BSPACE = 8;
5096 const $TAB = 9;
5097 const $LF = 10;
5098 const $VTAB = 11;
5099 const $FF = 12;
5100 const $CR = 13;
5101 const $SPACE = 32;
5102 const $BANG = 33;
5103 const $DQ = 34;
5104 const $HASH = 35;
5105 const $$ = 36;
5106 const $PERCENT = 37;
5107 const $AMPERSAND = 38;
5108 const $SQ = 39;
5109 const $LPAREN = 40;
5110 const $RPAREN = 41;
5111 const $STAR = 42;
5112 const $PLUS = 43;
5113 const $COMMA = 44;
5114 const $MINUS = 45;
5115 const $PERIOD = 46;
5116 const $SLASH = 47;
5117 const $COLON = 58;
5118 const $SEMICOLON = 59;
5119 const $LT = 60;
5120 const $EQ = 61;
5121 const $GT = 62;
5122 const $QUESTION = 63;
5123 const $0 = 48;
5124 const $7 = 55;
5125 const $9 = 57;
5126 const $A = 65;
5127 const $E = 69;
5128 const $F = 70;
5129 const $X = 88;
5130 const $Z = 90;
5131 const $LBRACKET = 91;
5132 const $BACKSLASH = 92;
5133 const $RBRACKET = 93;
5134 const $CARET = 94;
5135 const $_ = 95;
5136 const $a = 97;
5137 const $b = 98;
5138 const $e = 101;
5139 const $f = 102;
5140 const $n = 110;
5141 const $r = 114;
5142 const $t = 116;
5143 const $u = 117;
5144 const $v = 118;
5145 const $x = 120;
5146 const $z = 122;
5147 const $LBRACE = 123;
5148 const $BAR = 124;
5149 const $RBRACE = 125;
5150 const $NBSP = 160;
5151 const $BT = 96;
5152 function isWhitespace(code) {
5153 return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
5154 }
5155 function isDigit(code) {
5156 return $0 <= code && code <= $9;
5157 }
5158 function isAsciiLetter(code) {
5159 return code >= $a && code <= $z || code >= $A && code <= $Z;
5160 }
5161 function isAsciiHexDigit(code) {
5162 return code >= $a && code <= $f || code >= $A && code <= $F || isDigit(code);
5163 }
5164 function isNewLine(code) {
5165 return code === $LF || code === $CR;
5166 }
5167 function isOctalDigit(code) {
5168 return $0 <= code && code <= $7;
5169 }
5170 function isQuote(code) {
5171 return code === $SQ || code === $DQ || code === $BT;
5172 }
5173
5174 /**
5175 * @license
5176 * Copyright Google LLC All Rights Reserved.
5177 *
5178 * Use of this source code is governed by an MIT-style license that can be
5179 * found in the LICENSE file at https://angular.io/license
5180 */
5181 class ParseLocation {
5182 constructor(file, offset, line, col) {
5183 this.file = file;
5184 this.offset = offset;
5185 this.line = line;
5186 this.col = col;
5187 }
5188 toString() {
5189 return this.offset != null ? `${this.file.url}@${this.line}:${this.col}` : this.file.url;
5190 }
5191 moveBy(delta) {
5192 const source = this.file.content;
5193 const len = source.length;
5194 let offset = this.offset;
5195 let line = this.line;
5196 let col = this.col;
5197 while (offset > 0 && delta < 0) {
5198 offset--;
5199 delta++;
5200 const ch = source.charCodeAt(offset);
5201 if (ch == $LF) {
5202 line--;
5203 const priorLine = source.substr(0, offset - 1).lastIndexOf(String.fromCharCode($LF));
5204 col = priorLine > 0 ? offset - priorLine : offset;
5205 }
5206 else {
5207 col--;
5208 }
5209 }
5210 while (offset < len && delta > 0) {
5211 const ch = source.charCodeAt(offset);
5212 offset++;
5213 delta--;
5214 if (ch == $LF) {
5215 line++;
5216 col = 0;
5217 }
5218 else {
5219 col++;
5220 }
5221 }
5222 return new ParseLocation(this.file, offset, line, col);
5223 }
5224 // Return the source around the location
5225 // Up to `maxChars` or `maxLines` on each side of the location
5226 getContext(maxChars, maxLines) {
5227 const content = this.file.content;
5228 let startOffset = this.offset;
5229 if (startOffset != null) {
5230 if (startOffset > content.length - 1) {
5231 startOffset = content.length - 1;
5232 }
5233 let endOffset = startOffset;
5234 let ctxChars = 0;
5235 let ctxLines = 0;
5236 while (ctxChars < maxChars && startOffset > 0) {
5237 startOffset--;
5238 ctxChars++;
5239 if (content[startOffset] == '\n') {
5240 if (++ctxLines == maxLines) {
5241 break;
5242 }
5243 }
5244 }
5245 ctxChars = 0;
5246 ctxLines = 0;
5247 while (ctxChars < maxChars && endOffset < content.length - 1) {
5248 endOffset++;
5249 ctxChars++;
5250 if (content[endOffset] == '\n') {
5251 if (++ctxLines == maxLines) {
5252 break;
5253 }
5254 }
5255 }
5256 return {
5257 before: content.substring(startOffset, this.offset),
5258 after: content.substring(this.offset, endOffset + 1),
5259 };
5260 }
5261 return null;
5262 }
5263 }
5264 class ParseSourceFile {
5265 constructor(content, url) {
5266 this.content = content;
5267 this.url = url;
5268 }
5269 }
5270 class ParseSourceSpan {
5271 /**
5272 * Create an object that holds information about spans of tokens/nodes captured during
5273 * lexing/parsing of text.
5274 *
5275 * @param start
5276 * The location of the start of the span (having skipped leading trivia).
5277 * Skipping leading trivia makes source-spans more "user friendly", since things like HTML
5278 * elements will appear to begin at the start of the opening tag, rather than at the start of any
5279 * leading trivia, which could include newlines.
5280 *
5281 * @param end
5282 * The location of the end of the span.
5283 *
5284 * @param fullStart
5285 * The start of the token without skipping the leading trivia.
5286 * This is used by tooling that splits tokens further, such as extracting Angular interpolations
5287 * from text tokens. Such tooling creates new source-spans relative to the original token's
5288 * source-span. If leading trivia characters have been skipped then the new source-spans may be
5289 * incorrectly offset.
5290 *
5291 * @param details
5292 * Additional information (such as identifier names) that should be associated with the span.
5293 */
5294 constructor(start, end, fullStart = start, details = null) {
5295 this.start = start;
5296 this.end = end;
5297 this.fullStart = fullStart;
5298 this.details = details;
5299 }
5300 toString() {
5301 return this.start.file.content.substring(this.start.offset, this.end.offset);
5302 }
5303 }
5304 var ParseErrorLevel;
5305 (function (ParseErrorLevel) {
5306 ParseErrorLevel[ParseErrorLevel["WARNING"] = 0] = "WARNING";
5307 ParseErrorLevel[ParseErrorLevel["ERROR"] = 1] = "ERROR";
5308 })(ParseErrorLevel || (ParseErrorLevel = {}));
5309 class ParseError {
5310 constructor(span, msg, level = ParseErrorLevel.ERROR) {
5311 this.span = span;
5312 this.msg = msg;
5313 this.level = level;
5314 }
5315 contextualMessage() {
5316 const ctx = this.span.start.getContext(100, 3);
5317 return ctx ? `${this.msg} ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` :
5318 this.msg;
5319 }
5320 toString() {
5321 const details = this.span.details ? `, ${this.span.details}` : '';
5322 return `${this.contextualMessage()}: ${this.span.start}${details}`;
5323 }
5324 }
5325 /**
5326 * Generates Source Span object for a given R3 Type for JIT mode.
5327 *
5328 * @param kind Component or Directive.
5329 * @param typeName name of the Component or Directive.
5330 * @param sourceUrl reference to Component or Directive source.
5331 * @returns instance of ParseSourceSpan that represent a given Component or Directive.
5332 */
5333 function r3JitTypeSourceSpan(kind, typeName, sourceUrl) {
5334 const sourceFileName = `in ${kind} ${typeName} in ${sourceUrl}`;
5335 const sourceFile = new ParseSourceFile('', sourceFileName);
5336 return new ParseSourceSpan(new ParseLocation(sourceFile, -1, -1, -1), new ParseLocation(sourceFile, -1, -1, -1));
5337 }
5338 let _anonymousTypeIndex = 0;
5339 function identifierName(compileIdentifier) {
5340 if (!compileIdentifier || !compileIdentifier.reference) {
5341 return null;
5342 }
5343 const ref = compileIdentifier.reference;
5344 if (ref instanceof StaticSymbol) {
5345 return ref.name;
5346 }
5347 if (ref['__anonymousType']) {
5348 return ref['__anonymousType'];
5349 }
5350 if (ref['__forward_ref__']) {
5351 // We do not want to try to stringify a `forwardRef()` function because that would cause the
5352 // inner function to be evaluated too early, defeating the whole point of the `forwardRef`.
5353 return '__forward_ref__';
5354 }
5355 let identifier = stringify(ref);
5356 if (identifier.indexOf('(') >= 0) {
5357 // case: anonymous functions!
5358 identifier = `anonymous_${_anonymousTypeIndex++}`;
5359 ref['__anonymousType'] = identifier;
5360 }
5361 else {
5362 identifier = sanitizeIdentifier(identifier);
5363 }
5364 return identifier;
5365 }
5366 function sanitizeIdentifier(name) {
5367 return name.replace(/\W/g, '_');
5368 }
5369
5370 /**
5371 * @license
5372 * Copyright Google LLC All Rights Reserved.
5373 *
5374 * Use of this source code is governed by an MIT-style license that can be
5375 * found in the LICENSE file at https://angular.io/license
5376 */
5377 /**
5378 * In TypeScript, tagged template functions expect a "template object", which is an array of
5379 * "cooked" strings plus a `raw` property that contains an array of "raw" strings. This is
5380 * typically constructed with a function called `__makeTemplateObject(cooked, raw)`, but it may not
5381 * be available in all environments.
5382 *
5383 * This is a JavaScript polyfill that uses __makeTemplateObject when it's available, but otherwise
5384 * creates an inline helper with the same functionality.
5385 *
5386 * In the inline function, if `Object.defineProperty` is available we use that to attach the `raw`
5387 * array.
5388 */
5389 const makeTemplateObjectPolyfill = '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})';
5390 class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
5391 constructor() {
5392 super(false);
5393 }
5394 visitDeclareClassStmt(stmt, ctx) {
5395 ctx.pushClass(stmt);
5396 this._visitClassConstructor(stmt, ctx);
5397 if (stmt.parent != null) {
5398 ctx.print(stmt, `${stmt.name}.prototype = Object.create(`);
5399 stmt.parent.visitExpression(this, ctx);
5400 ctx.println(stmt, `.prototype);`);
5401 }
5402 stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx));
5403 stmt.methods.forEach((method) => this._visitClassMethod(stmt, method, ctx));
5404 ctx.popClass();
5405 return null;
5406 }
5407 _visitClassConstructor(stmt, ctx) {
5408 ctx.print(stmt, `function ${stmt.name}(`);
5409 if (stmt.constructorMethod != null) {
5410 this._visitParams(stmt.constructorMethod.params, ctx);
5411 }
5412 ctx.println(stmt, `) {`);
5413 ctx.incIndent();
5414 if (stmt.constructorMethod != null) {
5415 if (stmt.constructorMethod.body.length > 0) {
5416 ctx.println(stmt, `var self = this;`);
5417 this.visitAllStatements(stmt.constructorMethod.body, ctx);
5418 }
5419 }
5420 ctx.decIndent();
5421 ctx.println(stmt, `}`);
5422 }
5423 _visitClassGetter(stmt, getter, ctx) {
5424 ctx.println(stmt, `Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`);
5425 ctx.incIndent();
5426 if (getter.body.length > 0) {
5427 ctx.println(stmt, `var self = this;`);
5428 this.visitAllStatements(getter.body, ctx);
5429 }
5430 ctx.decIndent();
5431 ctx.println(stmt, `}});`);
5432 }
5433 _visitClassMethod(stmt, method, ctx) {
5434 ctx.print(stmt, `${stmt.name}.prototype.${method.name} = function(`);
5435 this._visitParams(method.params, ctx);
5436 ctx.println(stmt, `) {`);
5437 ctx.incIndent();
5438 if (method.body.length > 0) {
5439 ctx.println(stmt, `var self = this;`);
5440 this.visitAllStatements(method.body, ctx);
5441 }
5442 ctx.decIndent();
5443 ctx.println(stmt, `};`);
5444 }
5445 visitWrappedNodeExpr(ast, ctx) {
5446 throw new Error('Cannot emit a WrappedNodeExpr in Javascript.');
5447 }
5448 visitReadVarExpr(ast, ctx) {
5449 if (ast.builtin === BuiltinVar.This) {
5450 ctx.print(ast, 'self');
5451 }
5452 else if (ast.builtin === BuiltinVar.Super) {
5453 throw new Error(`'super' needs to be handled at a parent ast node, not at the variable level!`);
5454 }
5455 else {
5456 super.visitReadVarExpr(ast, ctx);
5457 }
5458 return null;
5459 }
5460 visitDeclareVarStmt(stmt, ctx) {
5461 ctx.print(stmt, `var ${stmt.name}`);
5462 if (stmt.value) {
5463 ctx.print(stmt, ' = ');
5464 stmt.value.visitExpression(this, ctx);
5465 }
5466 ctx.println(stmt, `;`);
5467 return null;
5468 }
5469 visitCastExpr(ast, ctx) {
5470 ast.value.visitExpression(this, ctx);
5471 return null;
5472 }
5473 visitInvokeFunctionExpr(expr, ctx) {
5474 const fnExpr = expr.fn;
5475 if (fnExpr instanceof ReadVarExpr && fnExpr.builtin === BuiltinVar.Super) {
5476 ctx.currentClass.parent.visitExpression(this, ctx);
5477 ctx.print(expr, `.call(this`);
5478 if (expr.args.length > 0) {
5479 ctx.print(expr, `, `);
5480 this.visitAllExpressions(expr.args, ctx, ',');
5481 }
5482 ctx.print(expr, `)`);
5483 }
5484 else {
5485 super.visitInvokeFunctionExpr(expr, ctx);
5486 }
5487 return null;
5488 }
5489 visitTaggedTemplateExpr(ast, ctx) {
5490 // The following convoluted piece of code is effectively the downlevelled equivalent of
5491 // ```
5492 // tag`...`
5493 // ```
5494 // which is effectively like:
5495 // ```
5496 // tag(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
5497 // ```
5498 const elements = ast.template.elements;
5499 ast.tag.visitExpression(this, ctx);
5500 ctx.print(ast, `(${makeTemplateObjectPolyfill}(`);
5501 ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.text, false)).join(', ')}], `);
5502 ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.rawText, false)).join(', ')}])`);
5503 ast.template.expressions.forEach(expression => {
5504 ctx.print(ast, ', ');
5505 expression.visitExpression(this, ctx);
5506 });
5507 ctx.print(ast, ')');
5508 return null;
5509 }
5510 visitFunctionExpr(ast, ctx) {
5511 ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`);
5512 this._visitParams(ast.params, ctx);
5513 ctx.println(ast, `) {`);
5514 ctx.incIndent();
5515 this.visitAllStatements(ast.statements, ctx);
5516 ctx.decIndent();
5517 ctx.print(ast, `}`);
5518 return null;
5519 }
5520 visitDeclareFunctionStmt(stmt, ctx) {
5521 ctx.print(stmt, `function ${stmt.name}(`);
5522 this._visitParams(stmt.params, ctx);
5523 ctx.println(stmt, `) {`);
5524 ctx.incIndent();
5525 this.visitAllStatements(stmt.statements, ctx);
5526 ctx.decIndent();
5527 ctx.println(stmt, `}`);
5528 return null;
5529 }
5530 visitTryCatchStmt(stmt, ctx) {
5531 ctx.println(stmt, `try {`);
5532 ctx.incIndent();
5533 this.visitAllStatements(stmt.bodyStmts, ctx);
5534 ctx.decIndent();
5535 ctx.println(stmt, `} catch (${CATCH_ERROR_VAR.name}) {`);
5536 ctx.incIndent();
5537 const catchStmts = [CATCH_STACK_VAR.set(CATCH_ERROR_VAR.prop('stack')).toDeclStmt(null, [
5538 StmtModifier.Final
5539 ])].concat(stmt.catchStmts);
5540 this.visitAllStatements(catchStmts, ctx);
5541 ctx.decIndent();
5542 ctx.println(stmt, `}`);
5543 return null;
5544 }
5545 visitLocalizedString(ast, ctx) {
5546 // The following convoluted piece of code is effectively the downlevelled equivalent of
5547 // ```
5548 // $localize `...`
5549 // ```
5550 // which is effectively like:
5551 // ```
5552 // $localize(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
5553 // ```
5554 ctx.print(ast, `$localize(${makeTemplateObjectPolyfill}(`);
5555 const parts = [ast.serializeI18nHead()];
5556 for (let i = 1; i < ast.messageParts.length; i++) {
5557 parts.push(ast.serializeI18nTemplatePart(i));
5558 }
5559 ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.cooked, false)).join(', ')}], `);
5560 ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.raw, false)).join(', ')}])`);
5561 ast.expressions.forEach(expression => {
5562 ctx.print(ast, ', ');
5563 expression.visitExpression(this, ctx);
5564 });
5565 ctx.print(ast, ')');
5566 return null;
5567 }
5568 _visitParams(params, ctx) {
5569 this.visitAllObjects(param => ctx.print(null, param.name), params, ctx, ',');
5570 }
5571 getBuiltinMethodName(method) {
5572 let name;
5573 switch (method) {
5574 case BuiltinMethod.ConcatArray:
5575 name = 'concat';
5576 break;
5577 case BuiltinMethod.SubscribeObservable:
5578 name = 'subscribe';
5579 break;
5580 case BuiltinMethod.Bind:
5581 name = 'bind';
5582 break;
5583 default:
5584 throw new Error(`Unknown builtin method: ${method}`);
5585 }
5586 return name;
5587 }
5588 }
5589
5590 /**
5591 * @license
5592 * Copyright Google LLC All Rights Reserved.
5593 *
5594 * Use of this source code is governed by an MIT-style license that can be
5595 * found in the LICENSE file at https://angular.io/license
5596 */
5597 /**
5598 * The Trusted Types policy, or null if Trusted Types are not
5599 * enabled/supported, or undefined if the policy has not been created yet.
5600 */
5601 let policy;
5602 /**
5603 * Returns the Trusted Types policy, or null if Trusted Types are not
5604 * enabled/supported. The first call to this function will create the policy.
5605 */
5606 function getPolicy() {
5607 if (policy === undefined) {
5608 policy = null;
5609 if (_global.trustedTypes) {
5610 try {
5611 policy =
5612 _global.trustedTypes.createPolicy('angular#unsafe-jit', {
5613 createScript: (s) => s,
5614 });
5615 }
5616 catch {
5617 // trustedTypes.createPolicy throws if called with a name that is
5618 // already registered, even in report-only mode. Until the API changes,
5619 // catch the error not to break the applications functionally. In such
5620 // cases, the code will fall back to using strings.
5621 }
5622 }
5623 }
5624 return policy;
5625 }
5626 /**
5627 * Unsafely promote a string to a TrustedScript, falling back to strings when
5628 * Trusted Types are not available.
5629 * @security In particular, it must be assured that the provided string will
5630 * never cause an XSS vulnerability if used in a context that will be
5631 * interpreted and executed as a script by a browser, e.g. when calling eval.
5632 */
5633 function trustedScriptFromString(script) {
5634 return getPolicy()?.createScript(script) || script;
5635 }
5636 /**
5637 * Unsafely call the Function constructor with the given string arguments.
5638 * @security This is a security-sensitive function; any use of this function
5639 * must go through security review. In particular, it must be assured that it
5640 * is only called from the JIT compiler, as use in other code can lead to XSS
5641 * vulnerabilities.
5642 */
5643 function newTrustedFunctionForJIT(...args) {
5644 if (!_global.trustedTypes) {
5645 // In environments that don't support Trusted Types, fall back to the most
5646 // straightforward implementation:
5647 return new Function(...args);
5648 }
5649 // Chrome currently does not support passing TrustedScript to the Function
5650 // constructor. The following implements the workaround proposed on the page
5651 // below, where the Chromium bug is also referenced:
5652 // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
5653 const fnArgs = args.slice(0, -1).join(',');
5654 const fnBody = args[args.length - 1];
5655 const body = `(function anonymous(${fnArgs}
5656) { ${fnBody}
5657})`;
5658 // Using eval directly confuses the compiler and prevents this module from
5659 // being stripped out of JS binaries even if not used. The global['eval']
5660 // indirection fixes that.
5661 const fn = _global['eval'](trustedScriptFromString(body));
5662 if (fn.bind === undefined) {
5663 // Workaround for a browser bug that only exists in Chrome 83, where passing
5664 // a TrustedScript to eval just returns the TrustedScript back without
5665 // evaluating it. In that case, fall back to the most straightforward
5666 // implementation:
5667 return new Function(...args);
5668 }
5669 // To completely mimic the behavior of calling "new Function", two more
5670 // things need to happen:
5671 // 1. Stringifying the resulting function should return its source code
5672 fn.toString = () => body;
5673 // 2. When calling the resulting function, `this` should refer to `global`
5674 return fn.bind(_global);
5675 // When Trusted Types support in Function constructors is widely available,
5676 // the implementation of this function can be simplified to:
5677 // return new Function(...args.map(a => trustedScriptFromString(a)));
5678 }
5679
5680 /**
5681 * @license
5682 * Copyright Google LLC All Rights Reserved.
5683 *
5684 * Use of this source code is governed by an MIT-style license that can be
5685 * found in the LICENSE file at https://angular.io/license
5686 */
5687 /**
5688 * A helper class to manage the evaluation of JIT generated code.
5689 */
5690 class JitEvaluator {
5691 /**
5692 *
5693 * @param sourceUrl The URL of the generated code.
5694 * @param statements An array of Angular statement AST nodes to be evaluated.
5695 * @param reflector A helper used when converting the statements to executable code.
5696 * @param createSourceMaps If true then create a source-map for the generated code and include it
5697 * inline as a source-map comment.
5698 * @returns A map of all the variables in the generated code.
5699 */
5700 evaluateStatements(sourceUrl, statements, reflector, createSourceMaps) {
5701 const converter = new JitEmitterVisitor(reflector);
5702 const ctx = EmitterVisitorContext.createRoot();
5703 // Ensure generated code is in strict mode
5704 if (statements.length > 0 && !isUseStrictStatement(statements[0])) {
5705 statements = [
5706 literal$1('use strict').toStmt(),
5707 ...statements,
5708 ];
5709 }
5710 converter.visitAllStatements(statements, ctx);
5711 converter.createReturnStmt(ctx);
5712 return this.evaluateCode(sourceUrl, ctx, converter.getArgs(), createSourceMaps);
5713 }
5714 /**
5715 * Evaluate a piece of JIT generated code.
5716 * @param sourceUrl The URL of this generated code.
5717 * @param ctx A context object that contains an AST of the code to be evaluated.
5718 * @param vars A map containing the names and values of variables that the evaluated code might
5719 * reference.
5720 * @param createSourceMap If true then create a source-map for the generated code and include it
5721 * inline as a source-map comment.
5722 * @returns The result of evaluating the code.
5723 */
5724 evaluateCode(sourceUrl, ctx, vars, createSourceMap) {
5725 let fnBody = `"use strict";${ctx.toSource()}\n//# sourceURL=${sourceUrl}`;
5726 const fnArgNames = [];
5727 const fnArgValues = [];
5728 for (const argName in vars) {
5729 fnArgValues.push(vars[argName]);
5730 fnArgNames.push(argName);
5731 }
5732 if (createSourceMap) {
5733 // using `new Function(...)` generates a header, 1 line of no arguments, 2 lines otherwise
5734 // E.g. ```
5735 // function anonymous(a,b,c
5736 // /**/) { ... }```
5737 // We don't want to hard code this fact, so we auto detect it via an empty function first.
5738 const emptyFn = newTrustedFunctionForJIT(...fnArgNames.concat('return null;')).toString();
5739 const headerLines = emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - 1;
5740 fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, headerLines).toJsComment()}`;
5741 }
5742 const fn = newTrustedFunctionForJIT(...fnArgNames.concat(fnBody));
5743 return this.executeFunction(fn, fnArgValues);
5744 }
5745 /**
5746 * Execute a JIT generated function by calling it.
5747 *
5748 * This method can be overridden in tests to capture the functions that are generated
5749 * by this `JitEvaluator` class.
5750 *
5751 * @param fn A function to execute.
5752 * @param args The arguments to pass to the function being executed.
5753 * @returns The return value of the executed function.
5754 */
5755 executeFunction(fn, args) {
5756 return fn(...args);
5757 }
5758 }
5759 /**
5760 * An Angular AST visitor that converts AST nodes into executable JavaScript code.
5761 */
5762 class JitEmitterVisitor extends AbstractJsEmitterVisitor {
5763 constructor(reflector) {
5764 super();
5765 this.reflector = reflector;
5766 this._evalArgNames = [];
5767 this._evalArgValues = [];
5768 this._evalExportedVars = [];
5769 }
5770 createReturnStmt(ctx) {
5771 const stmt = new ReturnStatement(new LiteralMapExpr(this._evalExportedVars.map(resultVar => new LiteralMapEntry(resultVar, variable(resultVar), false))));
5772 stmt.visitStatement(this, ctx);
5773 }
5774 getArgs() {
5775 const result = {};
5776 for (let i = 0; i < this._evalArgNames.length; i++) {
5777 result[this._evalArgNames[i]] = this._evalArgValues[i];
5778 }
5779 return result;
5780 }
5781 visitExternalExpr(ast, ctx) {
5782 this._emitReferenceToExternal(ast, this.reflector.resolveExternalReference(ast.value), ctx);
5783 return null;
5784 }
5785 visitWrappedNodeExpr(ast, ctx) {
5786 this._emitReferenceToExternal(ast, ast.node, ctx);
5787 return null;
5788 }
5789 visitDeclareVarStmt(stmt, ctx) {
5790 if (stmt.hasModifier(StmtModifier.Exported)) {
5791 this._evalExportedVars.push(stmt.name);
5792 }
5793 return super.visitDeclareVarStmt(stmt, ctx);
5794 }
5795 visitDeclareFunctionStmt(stmt, ctx) {
5796 if (stmt.hasModifier(StmtModifier.Exported)) {
5797 this._evalExportedVars.push(stmt.name);
5798 }
5799 return super.visitDeclareFunctionStmt(stmt, ctx);
5800 }
5801 visitDeclareClassStmt(stmt, ctx) {
5802 if (stmt.hasModifier(StmtModifier.Exported)) {
5803 this._evalExportedVars.push(stmt.name);
5804 }
5805 return super.visitDeclareClassStmt(stmt, ctx);
5806 }
5807 _emitReferenceToExternal(ast, value, ctx) {
5808 let id = this._evalArgValues.indexOf(value);
5809 if (id === -1) {
5810 id = this._evalArgValues.length;
5811 this._evalArgValues.push(value);
5812 const name = identifierName({ reference: value }) || 'val';
5813 this._evalArgNames.push(`jit_${name}_${id}`);
5814 }
5815 ctx.print(ast, this._evalArgNames[id]);
5816 }
5817 }
5818 function isUseStrictStatement(statement) {
5819 return statement.isEquivalent(literal$1('use strict').toStmt());
5820 }
5821
5822 /**
5823 * @license
5824 * Copyright Google LLC All Rights Reserved.
5825 *
5826 * Use of this source code is governed by an MIT-style license that can be
5827 * found in the LICENSE file at https://angular.io/license
5828 */
5829 function compileInjector(meta) {
5830 const definitionMap = new DefinitionMap();
5831 if (meta.providers !== null) {
5832 definitionMap.set('providers', meta.providers);
5833 }
5834 if (meta.imports.length > 0) {
5835 definitionMap.set('imports', literalArr(meta.imports));
5836 }
5837 const expression = importExpr(Identifiers$1.defineInjector).callFn([definitionMap.toLiteralMap()], undefined, true);
5838 const type = createInjectorType(meta);
5839 return { expression, type, statements: [] };
5840 }
5841 function createInjectorType(meta) {
5842 return new ExpressionType(importExpr(Identifiers$1.InjectorDeclaration, [new ExpressionType(meta.type.type)]));
5843 }
5844
5845 /**
5846 * @license
5847 * Copyright Google LLC All Rights Reserved.
5848 *
5849 * Use of this source code is governed by an MIT-style license that can be
5850 * found in the LICENSE file at https://angular.io/license
5851 */
5852 /**
5853 * Implementation of `CompileReflector` which resolves references to @angular/core
5854 * symbols at runtime, according to a consumer-provided mapping.
5855 *
5856 * Only supports `resolveExternalReference`, all other methods throw.
5857 */
5858 class R3JitReflector {
5859 constructor(context) {
5860 this.context = context;
5861 }
5862 resolveExternalReference(ref) {
5863 // This reflector only handles @angular/core imports.
5864 if (ref.moduleName !== '@angular/core') {
5865 throw new Error(`Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.`);
5866 }
5867 if (!this.context.hasOwnProperty(ref.name)) {
5868 throw new Error(`No value provided for @angular/core symbol '${ref.name}'.`);
5869 }
5870 return this.context[ref.name];
5871 }
5872 parameters(typeOrFunc) {
5873 throw new Error('Not implemented.');
5874 }
5875 annotations(typeOrFunc) {
5876 throw new Error('Not implemented.');
5877 }
5878 shallowAnnotations(typeOrFunc) {
5879 throw new Error('Not implemented.');
5880 }
5881 tryAnnotations(typeOrFunc) {
5882 throw new Error('Not implemented.');
5883 }
5884 propMetadata(typeOrFunc) {
5885 throw new Error('Not implemented.');
5886 }
5887 hasLifecycleHook(type, lcProperty) {
5888 throw new Error('Not implemented.');
5889 }
5890 guards(typeOrFunc) {
5891 throw new Error('Not implemented.');
5892 }
5893 componentModuleUrl(type, cmpMetadata) {
5894 throw new Error('Not implemented.');
5895 }
5896 }
5897
5898 /**
5899 * @license
5900 * Copyright Google LLC All Rights Reserved.
5901 *
5902 * Use of this source code is governed by an MIT-style license that can be
5903 * found in the LICENSE file at https://angular.io/license
5904 */
5905 /**
5906 * Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`.
5907 */
5908 function compileNgModule(meta) {
5909 const { internalType, bootstrap, declarations, imports, exports, schemas, containsForwardDecls, emitInline, id } = meta;
5910 const statements = [];
5911 const definitionMap = new DefinitionMap();
5912 definitionMap.set('type', internalType);
5913 if (bootstrap.length > 0) {
5914 definitionMap.set('bootstrap', refsToArray(bootstrap, containsForwardDecls));
5915 }
5916 // If requested to emit scope information inline, pass the `declarations`, `imports` and `exports`
5917 // to the `ɵɵdefineNgModule()` call. The JIT compilation uses this.
5918 if (emitInline) {
5919 if (declarations.length > 0) {
5920 definitionMap.set('declarations', refsToArray(declarations, containsForwardDecls));
5921 }
5922 if (imports.length > 0) {
5923 definitionMap.set('imports', refsToArray(imports, containsForwardDecls));
5924 }
5925 if (exports.length > 0) {
5926 definitionMap.set('exports', refsToArray(exports, containsForwardDecls));
5927 }
5928 }
5929 // If not emitting inline, the scope information is not passed into `ɵɵdefineNgModule` as it would
5930 // prevent tree-shaking of the declarations, imports and exports references.
5931 else {
5932 const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta);
5933 if (setNgModuleScopeCall !== null) {
5934 statements.push(setNgModuleScopeCall);
5935 }
5936 }
5937 if (schemas !== null && schemas.length > 0) {
5938 definitionMap.set('schemas', literalArr(schemas.map(ref => ref.value)));
5939 }
5940 if (id !== null) {
5941 definitionMap.set('id', id);
5942 }
5943 const expression = importExpr(Identifiers$1.defineNgModule).callFn([definitionMap.toLiteralMap()], undefined, true);
5944 const type = createNgModuleType(meta);
5945 return { expression, type, statements };
5946 }
5947 /**
5948 * This function is used in JIT mode to generate the call to `ɵɵdefineNgModule()` from a call to
5949 * `ɵɵngDeclareNgModule()`.
5950 */
5951 function compileNgModuleDeclarationExpression(meta) {
5952 const definitionMap = new DefinitionMap();
5953 definitionMap.set('type', new WrappedNodeExpr(meta.type));
5954 if (meta.bootstrap !== undefined) {
5955 definitionMap.set('bootstrap', new WrappedNodeExpr(meta.bootstrap));
5956 }
5957 if (meta.declarations !== undefined) {
5958 definitionMap.set('declarations', new WrappedNodeExpr(meta.declarations));
5959 }
5960 if (meta.imports !== undefined) {
5961 definitionMap.set('imports', new WrappedNodeExpr(meta.imports));
5962 }
5963 if (meta.exports !== undefined) {
5964 definitionMap.set('exports', new WrappedNodeExpr(meta.exports));
5965 }
5966 if (meta.schemas !== undefined) {
5967 definitionMap.set('schemas', new WrappedNodeExpr(meta.schemas));
5968 }
5969 if (meta.id !== undefined) {
5970 definitionMap.set('id', new WrappedNodeExpr(meta.id));
5971 }
5972 return importExpr(Identifiers$1.defineNgModule).callFn([definitionMap.toLiteralMap()]);
5973 }
5974 function createNgModuleType({ type: moduleType, declarations, imports, exports }) {
5975 return new ExpressionType(importExpr(Identifiers$1.NgModuleDeclaration, [
5976 new ExpressionType(moduleType.type), tupleTypeOf(declarations), tupleTypeOf(imports),
5977 tupleTypeOf(exports)
5978 ]));
5979 }
5980 /**
5981 * Generates a function call to `ɵɵsetNgModuleScope` with all necessary information so that the
5982 * transitive module scope can be computed during runtime in JIT mode. This call is marked pure
5983 * such that the references to declarations, imports and exports may be elided causing these
5984 * symbols to become tree-shakeable.
5985 */
5986 function generateSetNgModuleScopeCall(meta) {
5987 const { adjacentType: moduleType, declarations, imports, exports, containsForwardDecls } = meta;
5988 const scopeMap = new DefinitionMap();
5989 if (declarations.length > 0) {
5990 scopeMap.set('declarations', refsToArray(declarations, containsForwardDecls));
5991 }
5992 if (imports.length > 0) {
5993 scopeMap.set('imports', refsToArray(imports, containsForwardDecls));
5994 }
5995 if (exports.length > 0) {
5996 scopeMap.set('exports', refsToArray(exports, containsForwardDecls));
5997 }
5998 if (Object.keys(scopeMap.values).length === 0) {
5999 return null;
6000 }
6001 // setNgModuleScope(...)
6002 const fnCall = new InvokeFunctionExpr(
6003 /* fn */ importExpr(Identifiers$1.setNgModuleScope),
6004 /* args */ [moduleType, scopeMap.toLiteralMap()]);
6005 // (ngJitMode guard) && setNgModuleScope(...)
6006 const guardedCall = jitOnlyGuardedExpression(fnCall);
6007 // function() { (ngJitMode guard) && setNgModuleScope(...); }
6008 const iife = new FunctionExpr(
6009 /* params */ [],
6010 /* statements */ [guardedCall.toStmt()]);
6011 // (function() { (ngJitMode guard) && setNgModuleScope(...); })()
6012 const iifeCall = new InvokeFunctionExpr(
6013 /* fn */ iife,
6014 /* args */ []);
6015 return iifeCall.toStmt();
6016 }
6017 function tupleTypeOf(exp) {
6018 const types = exp.map(ref => typeofExpr(ref.type));
6019 return exp.length > 0 ? expressionType(literalArr(types)) : NONE_TYPE;
6020 }
6021
6022 /**
6023 * @license
6024 * Copyright Google LLC All Rights Reserved.
6025 *
6026 * Use of this source code is governed by an MIT-style license that can be
6027 * found in the LICENSE file at https://angular.io/license
6028 */
6029 function compilePipeFromMetadata(metadata) {
6030 const definitionMapValues = [];
6031 // e.g. `name: 'myPipe'`
6032 definitionMapValues.push({ key: 'name', value: literal$1(metadata.pipeName), quoted: false });
6033 // e.g. `type: MyPipe`
6034 definitionMapValues.push({ key: 'type', value: metadata.type.value, quoted: false });
6035 // e.g. `pure: true`
6036 definitionMapValues.push({ key: 'pure', value: literal$1(metadata.pure), quoted: false });
6037 const expression = importExpr(Identifiers$1.definePipe).callFn([literalMap(definitionMapValues)], undefined, true);
6038 const type = createPipeType(metadata);
6039 return { expression, type, statements: [] };
6040 }
6041 function createPipeType(metadata) {
6042 return new ExpressionType(importExpr(Identifiers$1.PipeDeclaration, [
6043 typeWithParameters(metadata.type.type, metadata.typeArgumentCount),
6044 new ExpressionType(new LiteralExpr(metadata.pipeName)),
6045 ]));
6046 }
6047
6048 /**
6049 * @license
6050 * Copyright Google LLC All Rights Reserved.
6051 *
6052 * Use of this source code is governed by an MIT-style license that can be
6053 * found in the LICENSE file at https://angular.io/license
6054 */
6055 class ParserError {
6056 constructor(message, input, errLocation, ctxLocation) {
6057 this.input = input;
6058 this.errLocation = errLocation;
6059 this.ctxLocation = ctxLocation;
6060 this.message = `Parser Error: ${message} ${errLocation} [${input}] in ${ctxLocation}`;
6061 }
6062 }
6063 class ParseSpan {
6064 constructor(start, end) {
6065 this.start = start;
6066 this.end = end;
6067 }
6068 toAbsolute(absoluteOffset) {
6069 return new AbsoluteSourceSpan$1(absoluteOffset + this.start, absoluteOffset + this.end);
6070 }
6071 }
6072 class AST {
6073 constructor(span,
6074 /**
6075 * Absolute location of the expression AST in a source code file.
6076 */
6077 sourceSpan) {
6078 this.span = span;
6079 this.sourceSpan = sourceSpan;
6080 }
6081 toString() {
6082 return 'AST';
6083 }
6084 }
6085 class ASTWithName extends AST {
6086 constructor(span, sourceSpan, nameSpan) {
6087 super(span, sourceSpan);
6088 this.nameSpan = nameSpan;
6089 }
6090 }
6091 /**
6092 * Represents a quoted expression of the form:
6093 *
6094 * quote = prefix `:` uninterpretedExpression
6095 * prefix = identifier
6096 * uninterpretedExpression = arbitrary string
6097 *
6098 * A quoted expression is meant to be pre-processed by an AST transformer that
6099 * converts it into another AST that no longer contains quoted expressions.
6100 * It is meant to allow third-party developers to extend Angular template
6101 * expression language. The `uninterpretedExpression` part of the quote is
6102 * therefore not interpreted by the Angular's own expression parser.
6103 */
6104 class Quote extends AST {
6105 constructor(span, sourceSpan, prefix, uninterpretedExpression, location) {
6106 super(span, sourceSpan);
6107 this.prefix = prefix;
6108 this.uninterpretedExpression = uninterpretedExpression;
6109 this.location = location;
6110 }
6111 visit(visitor, context = null) {
6112 return visitor.visitQuote(this, context);
6113 }
6114 toString() {
6115 return 'Quote';
6116 }
6117 }
6118 class EmptyExpr extends AST {
6119 visit(visitor, context = null) {
6120 // do nothing
6121 }
6122 }
6123 class ImplicitReceiver extends AST {
6124 visit(visitor, context = null) {
6125 return visitor.visitImplicitReceiver(this, context);
6126 }
6127 }
6128 /**
6129 * Receiver when something is accessed through `this` (e.g. `this.foo`). Note that this class
6130 * inherits from `ImplicitReceiver`, because accessing something through `this` is treated the
6131 * same as accessing it implicitly inside of an Angular template (e.g. `[attr.title]="this.title"`
6132 * is the same as `[attr.title]="title"`.). Inheriting allows for the `this` accesses to be treated
6133 * the same as implicit ones, except for a couple of exceptions like `$event` and `$any`.
6134 * TODO: we should find a way for this class not to extend from `ImplicitReceiver` in the future.
6135 */
6136 class ThisReceiver extends ImplicitReceiver {
6137 visit(visitor, context = null) {
6138 return visitor.visitThisReceiver?.(this, context);
6139 }
6140 }
6141 /**
6142 * Multiple expressions separated by a semicolon.
6143 */
6144 class Chain extends AST {
6145 constructor(span, sourceSpan, expressions) {
6146 super(span, sourceSpan);
6147 this.expressions = expressions;
6148 }
6149 visit(visitor, context = null) {
6150 return visitor.visitChain(this, context);
6151 }
6152 }
6153 class Conditional extends AST {
6154 constructor(span, sourceSpan, condition, trueExp, falseExp) {
6155 super(span, sourceSpan);
6156 this.condition = condition;
6157 this.trueExp = trueExp;
6158 this.falseExp = falseExp;
6159 }
6160 visit(visitor, context = null) {
6161 return visitor.visitConditional(this, context);
6162 }
6163 }
6164 class PropertyRead extends ASTWithName {
6165 constructor(span, sourceSpan, nameSpan, receiver, name) {
6166 super(span, sourceSpan, nameSpan);
6167 this.receiver = receiver;
6168 this.name = name;
6169 }
6170 visit(visitor, context = null) {
6171 return visitor.visitPropertyRead(this, context);
6172 }
6173 }
6174 class PropertyWrite extends ASTWithName {
6175 constructor(span, sourceSpan, nameSpan, receiver, name, value) {
6176 super(span, sourceSpan, nameSpan);
6177 this.receiver = receiver;
6178 this.name = name;
6179 this.value = value;
6180 }
6181 visit(visitor, context = null) {
6182 return visitor.visitPropertyWrite(this, context);
6183 }
6184 }
6185 class SafePropertyRead extends ASTWithName {
6186 constructor(span, sourceSpan, nameSpan, receiver, name) {
6187 super(span, sourceSpan, nameSpan);
6188 this.receiver = receiver;
6189 this.name = name;
6190 }
6191 visit(visitor, context = null) {
6192 return visitor.visitSafePropertyRead(this, context);
6193 }
6194 }
6195 class KeyedRead extends AST {
6196 constructor(span, sourceSpan, receiver, key) {
6197 super(span, sourceSpan);
6198 this.receiver = receiver;
6199 this.key = key;
6200 }
6201 visit(visitor, context = null) {
6202 return visitor.visitKeyedRead(this, context);
6203 }
6204 }
6205 class SafeKeyedRead extends AST {
6206 constructor(span, sourceSpan, receiver, key) {
6207 super(span, sourceSpan);
6208 this.receiver = receiver;
6209 this.key = key;
6210 }
6211 visit(visitor, context = null) {
6212 return visitor.visitSafeKeyedRead(this, context);
6213 }
6214 }
6215 class KeyedWrite extends AST {
6216 constructor(span, sourceSpan, receiver, key, value) {
6217 super(span, sourceSpan);
6218 this.receiver = receiver;
6219 this.key = key;
6220 this.value = value;
6221 }
6222 visit(visitor, context = null) {
6223 return visitor.visitKeyedWrite(this, context);
6224 }
6225 }
6226 class BindingPipe extends ASTWithName {
6227 constructor(span, sourceSpan, exp, name, args, nameSpan) {
6228 super(span, sourceSpan, nameSpan);
6229 this.exp = exp;
6230 this.name = name;
6231 this.args = args;
6232 }
6233 visit(visitor, context = null) {
6234 return visitor.visitPipe(this, context);
6235 }
6236 }
6237 class LiteralPrimitive extends AST {
6238 constructor(span, sourceSpan, value) {
6239 super(span, sourceSpan);
6240 this.value = value;
6241 }
6242 visit(visitor, context = null) {
6243 return visitor.visitLiteralPrimitive(this, context);
6244 }
6245 }
6246 class LiteralArray extends AST {
6247 constructor(span, sourceSpan, expressions) {
6248 super(span, sourceSpan);
6249 this.expressions = expressions;
6250 }
6251 visit(visitor, context = null) {
6252 return visitor.visitLiteralArray(this, context);
6253 }
6254 }
6255 class LiteralMap extends AST {
6256 constructor(span, sourceSpan, keys, values) {
6257 super(span, sourceSpan);
6258 this.keys = keys;
6259 this.values = values;
6260 }
6261 visit(visitor, context = null) {
6262 return visitor.visitLiteralMap(this, context);
6263 }
6264 }
6265 class Interpolation extends AST {
6266 constructor(span, sourceSpan, strings, expressions) {
6267 super(span, sourceSpan);
6268 this.strings = strings;
6269 this.expressions = expressions;
6270 }
6271 visit(visitor, context = null) {
6272 return visitor.visitInterpolation(this, context);
6273 }
6274 }
6275 class Binary extends AST {
6276 constructor(span, sourceSpan, operation, left, right) {
6277 super(span, sourceSpan);
6278 this.operation = operation;
6279 this.left = left;
6280 this.right = right;
6281 }
6282 visit(visitor, context = null) {
6283 return visitor.visitBinary(this, context);
6284 }
6285 }
6286 /**
6287 * For backwards compatibility reasons, `Unary` inherits from `Binary` and mimics the binary AST
6288 * node that was originally used. This inheritance relation can be deleted in some future major,
6289 * after consumers have been given a chance to fully support Unary.
6290 */
6291 class Unary extends Binary {
6292 /**
6293 * During the deprecation period this constructor is private, to avoid consumers from creating
6294 * a `Unary` with the fallback properties for `Binary`.
6295 */
6296 constructor(span, sourceSpan, operator, expr, binaryOp, binaryLeft, binaryRight) {
6297 super(span, sourceSpan, binaryOp, binaryLeft, binaryRight);
6298 this.operator = operator;
6299 this.expr = expr;
6300 // Redeclare the properties that are inherited from `Binary` as `never`, as consumers should not
6301 // depend on these fields when operating on `Unary`.
6302 this.left = null;
6303 this.right = null;
6304 this.operation = null;
6305 }
6306 /**
6307 * Creates a unary minus expression "-x", represented as `Binary` using "0 - x".
6308 */
6309 static createMinus(span, sourceSpan, expr) {
6310 return new Unary(span, sourceSpan, '-', expr, '-', new LiteralPrimitive(span, sourceSpan, 0), expr);
6311 }
6312 /**
6313 * Creates a unary plus expression "+x", represented as `Binary` using "x - 0".
6314 */
6315 static createPlus(span, sourceSpan, expr) {
6316 return new Unary(span, sourceSpan, '+', expr, '-', expr, new LiteralPrimitive(span, sourceSpan, 0));
6317 }
6318 visit(visitor, context = null) {
6319 if (visitor.visitUnary !== undefined) {
6320 return visitor.visitUnary(this, context);
6321 }
6322 return visitor.visitBinary(this, context);
6323 }
6324 }
6325 class PrefixNot extends AST {
6326 constructor(span, sourceSpan, expression) {
6327 super(span, sourceSpan);
6328 this.expression = expression;
6329 }
6330 visit(visitor, context = null) {
6331 return visitor.visitPrefixNot(this, context);
6332 }
6333 }
6334 class NonNullAssert extends AST {
6335 constructor(span, sourceSpan, expression) {
6336 super(span, sourceSpan);
6337 this.expression = expression;
6338 }
6339 visit(visitor, context = null) {
6340 return visitor.visitNonNullAssert(this, context);
6341 }
6342 }
6343 class Call extends AST {
6344 constructor(span, sourceSpan, receiver, args, argumentSpan) {
6345 super(span, sourceSpan);
6346 this.receiver = receiver;
6347 this.args = args;
6348 this.argumentSpan = argumentSpan;
6349 }
6350 visit(visitor, context = null) {
6351 return visitor.visitCall(this, context);
6352 }
6353 }
6354 /**
6355 * Records the absolute position of a text span in a source file, where `start` and `end` are the
6356 * starting and ending byte offsets, respectively, of the text span in a source file.
6357 */
6358 class AbsoluteSourceSpan$1 {
6359 constructor(start, end) {
6360 this.start = start;
6361 this.end = end;
6362 }
6363 }
6364 class ASTWithSource extends AST {
6365 constructor(ast, source, location, absoluteOffset, errors) {
6366 super(new ParseSpan(0, source === null ? 0 : source.length), new AbsoluteSourceSpan$1(absoluteOffset, source === null ? absoluteOffset : absoluteOffset + source.length));
6367 this.ast = ast;
6368 this.source = source;
6369 this.location = location;
6370 this.errors = errors;
6371 }
6372 visit(visitor, context = null) {
6373 if (visitor.visitASTWithSource) {
6374 return visitor.visitASTWithSource(this, context);
6375 }
6376 return this.ast.visit(visitor, context);
6377 }
6378 toString() {
6379 return `${this.source} in ${this.location}`;
6380 }
6381 }
6382 class VariableBinding {
6383 /**
6384 * @param sourceSpan entire span of the binding.
6385 * @param key name of the LHS along with its span.
6386 * @param value optional value for the RHS along with its span.
6387 */
6388 constructor(sourceSpan, key, value) {
6389 this.sourceSpan = sourceSpan;
6390 this.key = key;
6391 this.value = value;
6392 }
6393 }
6394 class ExpressionBinding {
6395 /**
6396 * @param sourceSpan entire span of the binding.
6397 * @param key binding name, like ngForOf, ngForTrackBy, ngIf, along with its
6398 * span. Note that the length of the span may not be the same as
6399 * `key.source.length`. For example,
6400 * 1. key.source = ngFor, key.span is for "ngFor"
6401 * 2. key.source = ngForOf, key.span is for "of"
6402 * 3. key.source = ngForTrackBy, key.span is for "trackBy"
6403 * @param value optional expression for the RHS.
6404 */
6405 constructor(sourceSpan, key, value) {
6406 this.sourceSpan = sourceSpan;
6407 this.key = key;
6408 this.value = value;
6409 }
6410 }
6411 class RecursiveAstVisitor {
6412 visit(ast, context) {
6413 // The default implementation just visits every node.
6414 // Classes that extend RecursiveAstVisitor should override this function
6415 // to selectively visit the specified node.
6416 ast.visit(this, context);
6417 }
6418 visitUnary(ast, context) {
6419 this.visit(ast.expr, context);
6420 }
6421 visitBinary(ast, context) {
6422 this.visit(ast.left, context);
6423 this.visit(ast.right, context);
6424 }
6425 visitChain(ast, context) {
6426 this.visitAll(ast.expressions, context);
6427 }
6428 visitConditional(ast, context) {
6429 this.visit(ast.condition, context);
6430 this.visit(ast.trueExp, context);
6431 this.visit(ast.falseExp, context);
6432 }
6433 visitPipe(ast, context) {
6434 this.visit(ast.exp, context);
6435 this.visitAll(ast.args, context);
6436 }
6437 visitImplicitReceiver(ast, context) { }
6438 visitThisReceiver(ast, context) { }
6439 visitInterpolation(ast, context) {
6440 this.visitAll(ast.expressions, context);
6441 }
6442 visitKeyedRead(ast, context) {
6443 this.visit(ast.receiver, context);
6444 this.visit(ast.key, context);
6445 }
6446 visitKeyedWrite(ast, context) {
6447 this.visit(ast.receiver, context);
6448 this.visit(ast.key, context);
6449 this.visit(ast.value, context);
6450 }
6451 visitLiteralArray(ast, context) {
6452 this.visitAll(ast.expressions, context);
6453 }
6454 visitLiteralMap(ast, context) {
6455 this.visitAll(ast.values, context);
6456 }
6457 visitLiteralPrimitive(ast, context) { }
6458 visitPrefixNot(ast, context) {
6459 this.visit(ast.expression, context);
6460 }
6461 visitNonNullAssert(ast, context) {
6462 this.visit(ast.expression, context);
6463 }
6464 visitPropertyRead(ast, context) {
6465 this.visit(ast.receiver, context);
6466 }
6467 visitPropertyWrite(ast, context) {
6468 this.visit(ast.receiver, context);
6469 this.visit(ast.value, context);
6470 }
6471 visitSafePropertyRead(ast, context) {
6472 this.visit(ast.receiver, context);
6473 }
6474 visitSafeKeyedRead(ast, context) {
6475 this.visit(ast.receiver, context);
6476 this.visit(ast.key, context);
6477 }
6478 visitCall(ast, context) {
6479 this.visit(ast.receiver, context);
6480 this.visitAll(ast.args, context);
6481 }
6482 visitQuote(ast, context) { }
6483 // This is not part of the AstVisitor interface, just a helper method
6484 visitAll(asts, context) {
6485 for (const ast of asts) {
6486 this.visit(ast, context);
6487 }
6488 }
6489 }
6490 class AstTransformer {
6491 visitImplicitReceiver(ast, context) {
6492 return ast;
6493 }
6494 visitThisReceiver(ast, context) {
6495 return ast;
6496 }
6497 visitInterpolation(ast, context) {
6498 return new Interpolation(ast.span, ast.sourceSpan, ast.strings, this.visitAll(ast.expressions));
6499 }
6500 visitLiteralPrimitive(ast, context) {
6501 return new LiteralPrimitive(ast.span, ast.sourceSpan, ast.value);
6502 }
6503 visitPropertyRead(ast, context) {
6504 return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name);
6505 }
6506 visitPropertyWrite(ast, context) {
6507 return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, ast.value.visit(this));
6508 }
6509 visitSafePropertyRead(ast, context) {
6510 return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name);
6511 }
6512 visitLiteralArray(ast, context) {
6513 return new LiteralArray(ast.span, ast.sourceSpan, this.visitAll(ast.expressions));
6514 }
6515 visitLiteralMap(ast, context) {
6516 return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, this.visitAll(ast.values));
6517 }
6518 visitUnary(ast, context) {
6519 switch (ast.operator) {
6520 case '+':
6521 return Unary.createPlus(ast.span, ast.sourceSpan, ast.expr.visit(this));
6522 case '-':
6523 return Unary.createMinus(ast.span, ast.sourceSpan, ast.expr.visit(this));
6524 default:
6525 throw new Error(`Unknown unary operator ${ast.operator}`);
6526 }
6527 }
6528 visitBinary(ast, context) {
6529 return new Binary(ast.span, ast.sourceSpan, ast.operation, ast.left.visit(this), ast.right.visit(this));
6530 }
6531 visitPrefixNot(ast, context) {
6532 return new PrefixNot(ast.span, ast.sourceSpan, ast.expression.visit(this));
6533 }
6534 visitNonNullAssert(ast, context) {
6535 return new NonNullAssert(ast.span, ast.sourceSpan, ast.expression.visit(this));
6536 }
6537 visitConditional(ast, context) {
6538 return new Conditional(ast.span, ast.sourceSpan, ast.condition.visit(this), ast.trueExp.visit(this), ast.falseExp.visit(this));
6539 }
6540 visitPipe(ast, context) {
6541 return new BindingPipe(ast.span, ast.sourceSpan, ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.nameSpan);
6542 }
6543 visitKeyedRead(ast, context) {
6544 return new KeyedRead(ast.span, ast.sourceSpan, ast.receiver.visit(this), ast.key.visit(this));
6545 }
6546 visitKeyedWrite(ast, context) {
6547 return new KeyedWrite(ast.span, ast.sourceSpan, ast.receiver.visit(this), ast.key.visit(this), ast.value.visit(this));
6548 }
6549 visitCall(ast, context) {
6550 return new Call(ast.span, ast.sourceSpan, ast.receiver.visit(this), this.visitAll(ast.args), ast.argumentSpan);
6551 }
6552 visitAll(asts) {
6553 const res = [];
6554 for (let i = 0; i < asts.length; ++i) {
6555 res[i] = asts[i].visit(this);
6556 }
6557 return res;
6558 }
6559 visitChain(ast, context) {
6560 return new Chain(ast.span, ast.sourceSpan, this.visitAll(ast.expressions));
6561 }
6562 visitQuote(ast, context) {
6563 return new Quote(ast.span, ast.sourceSpan, ast.prefix, ast.uninterpretedExpression, ast.location);
6564 }
6565 visitSafeKeyedRead(ast, context) {
6566 return new SafeKeyedRead(ast.span, ast.sourceSpan, ast.receiver.visit(this), ast.key.visit(this));
6567 }
6568 }
6569 // A transformer that only creates new nodes if the transformer makes a change or
6570 // a change is made a child node.
6571 class AstMemoryEfficientTransformer {
6572 visitImplicitReceiver(ast, context) {
6573 return ast;
6574 }
6575 visitThisReceiver(ast, context) {
6576 return ast;
6577 }
6578 visitInterpolation(ast, context) {
6579 const expressions = this.visitAll(ast.expressions);
6580 if (expressions !== ast.expressions)
6581 return new Interpolation(ast.span, ast.sourceSpan, ast.strings, expressions);
6582 return ast;
6583 }
6584 visitLiteralPrimitive(ast, context) {
6585 return ast;
6586 }
6587 visitPropertyRead(ast, context) {
6588 const receiver = ast.receiver.visit(this);
6589 if (receiver !== ast.receiver) {
6590 return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name);
6591 }
6592 return ast;
6593 }
6594 visitPropertyWrite(ast, context) {
6595 const receiver = ast.receiver.visit(this);
6596 const value = ast.value.visit(this);
6597 if (receiver !== ast.receiver || value !== ast.value) {
6598 return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, value);
6599 }
6600 return ast;
6601 }
6602 visitSafePropertyRead(ast, context) {
6603 const receiver = ast.receiver.visit(this);
6604 if (receiver !== ast.receiver) {
6605 return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name);
6606 }
6607 return ast;
6608 }
6609 visitLiteralArray(ast, context) {
6610 const expressions = this.visitAll(ast.expressions);
6611 if (expressions !== ast.expressions) {
6612 return new LiteralArray(ast.span, ast.sourceSpan, expressions);
6613 }
6614 return ast;
6615 }
6616 visitLiteralMap(ast, context) {
6617 const values = this.visitAll(ast.values);
6618 if (values !== ast.values) {
6619 return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, values);
6620 }
6621 return ast;
6622 }
6623 visitUnary(ast, context) {
6624 const expr = ast.expr.visit(this);
6625 if (expr !== ast.expr) {
6626 switch (ast.operator) {
6627 case '+':
6628 return Unary.createPlus(ast.span, ast.sourceSpan, expr);
6629 case '-':
6630 return Unary.createMinus(ast.span, ast.sourceSpan, expr);
6631 default:
6632 throw new Error(`Unknown unary operator ${ast.operator}`);
6633 }
6634 }
6635 return ast;
6636 }
6637 visitBinary(ast, context) {
6638 const left = ast.left.visit(this);
6639 const right = ast.right.visit(this);
6640 if (left !== ast.left || right !== ast.right) {
6641 return new Binary(ast.span, ast.sourceSpan, ast.operation, left, right);
6642 }
6643 return ast;
6644 }
6645 visitPrefixNot(ast, context) {
6646 const expression = ast.expression.visit(this);
6647 if (expression !== ast.expression) {
6648 return new PrefixNot(ast.span, ast.sourceSpan, expression);
6649 }
6650 return ast;
6651 }
6652 visitNonNullAssert(ast, context) {
6653 const expression = ast.expression.visit(this);
6654 if (expression !== ast.expression) {
6655 return new NonNullAssert(ast.span, ast.sourceSpan, expression);
6656 }
6657 return ast;
6658 }
6659 visitConditional(ast, context) {
6660 const condition = ast.condition.visit(this);
6661 const trueExp = ast.trueExp.visit(this);
6662 const falseExp = ast.falseExp.visit(this);
6663 if (condition !== ast.condition || trueExp !== ast.trueExp || falseExp !== ast.falseExp) {
6664 return new Conditional(ast.span, ast.sourceSpan, condition, trueExp, falseExp);
6665 }
6666 return ast;
6667 }
6668 visitPipe(ast, context) {
6669 const exp = ast.exp.visit(this);
6670 const args = this.visitAll(ast.args);
6671 if (exp !== ast.exp || args !== ast.args) {
6672 return new BindingPipe(ast.span, ast.sourceSpan, exp, ast.name, args, ast.nameSpan);
6673 }
6674 return ast;
6675 }
6676 visitKeyedRead(ast, context) {
6677 const obj = ast.receiver.visit(this);
6678 const key = ast.key.visit(this);
6679 if (obj !== ast.receiver || key !== ast.key) {
6680 return new KeyedRead(ast.span, ast.sourceSpan, obj, key);
6681 }
6682 return ast;
6683 }
6684 visitKeyedWrite(ast, context) {
6685 const obj = ast.receiver.visit(this);
6686 const key = ast.key.visit(this);
6687 const value = ast.value.visit(this);
6688 if (obj !== ast.receiver || key !== ast.key || value !== ast.value) {
6689 return new KeyedWrite(ast.span, ast.sourceSpan, obj, key, value);
6690 }
6691 return ast;
6692 }
6693 visitAll(asts) {
6694 const res = [];
6695 let modified = false;
6696 for (let i = 0; i < asts.length; ++i) {
6697 const original = asts[i];
6698 const value = original.visit(this);
6699 res[i] = value;
6700 modified = modified || value !== original;
6701 }
6702 return modified ? res : asts;
6703 }
6704 visitChain(ast, context) {
6705 const expressions = this.visitAll(ast.expressions);
6706 if (expressions !== ast.expressions) {
6707 return new Chain(ast.span, ast.sourceSpan, expressions);
6708 }
6709 return ast;
6710 }
6711 visitCall(ast, context) {
6712 const receiver = ast.receiver.visit(this);
6713 const args = this.visitAll(ast.args);
6714 if (receiver !== ast.receiver || args !== ast.args) {
6715 return new Call(ast.span, ast.sourceSpan, receiver, args, ast.argumentSpan);
6716 }
6717 return ast;
6718 }
6719 visitQuote(ast, context) {
6720 return ast;
6721 }
6722 visitSafeKeyedRead(ast, context) {
6723 const obj = ast.receiver.visit(this);
6724 const key = ast.key.visit(this);
6725 if (obj !== ast.receiver || key !== ast.key) {
6726 return new SafeKeyedRead(ast.span, ast.sourceSpan, obj, key);
6727 }
6728 return ast;
6729 }
6730 }
6731 // Bindings
6732 class ParsedProperty {
6733 constructor(name, expression, type,
6734 // TODO(FW-2095): `keySpan` should really be required but allows `undefined` so VE does
6735 // not need to be updated. Make `keySpan` required when VE is removed.
6736 sourceSpan, keySpan, valueSpan) {
6737 this.name = name;
6738 this.expression = expression;
6739 this.type = type;
6740 this.sourceSpan = sourceSpan;
6741 this.keySpan = keySpan;
6742 this.valueSpan = valueSpan;
6743 this.isLiteral = this.type === ParsedPropertyType.LITERAL_ATTR;
6744 this.isAnimation = this.type === ParsedPropertyType.ANIMATION;
6745 }
6746 }
6747 var ParsedPropertyType;
6748 (function (ParsedPropertyType) {
6749 ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT";
6750 ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR";
6751 ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION";
6752 })(ParsedPropertyType || (ParsedPropertyType = {}));
6753 class ParsedEvent {
6754 // Regular events have a target
6755 // Animation events have a phase
6756 constructor(name, targetOrPhase, type, handler, sourceSpan,
6757 // TODO(FW-2095): keySpan should be required but was made optional to avoid changing VE
6758 handlerSpan, keySpan) {
6759 this.name = name;
6760 this.targetOrPhase = targetOrPhase;
6761 this.type = type;
6762 this.handler = handler;
6763 this.sourceSpan = sourceSpan;
6764 this.handlerSpan = handlerSpan;
6765 this.keySpan = keySpan;
6766 }
6767 }
6768 /**
6769 * ParsedVariable represents a variable declaration in a microsyntax expression.
6770 */
6771 class ParsedVariable {
6772 constructor(name, value, sourceSpan, keySpan, valueSpan) {
6773 this.name = name;
6774 this.value = value;
6775 this.sourceSpan = sourceSpan;
6776 this.keySpan = keySpan;
6777 this.valueSpan = valueSpan;
6778 }
6779 }
6780 class BoundElementProperty {
6781 constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan) {
6782 this.name = name;
6783 this.type = type;
6784 this.securityContext = securityContext;
6785 this.value = value;
6786 this.unit = unit;
6787 this.sourceSpan = sourceSpan;
6788 this.keySpan = keySpan;
6789 this.valueSpan = valueSpan;
6790 }
6791 }
6792
6793 /**
6794 * @license
6795 * Copyright Google LLC All Rights Reserved.
6796 *
6797 * Use of this source code is governed by an MIT-style license that can be
6798 * found in the LICENSE file at https://angular.io/license
6799 */
6800 const CORE = '@angular/core';
6801 class Identifiers {
6802 }
6803 Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS = {
6804 name: 'ANALYZE_FOR_ENTRY_COMPONENTS',
6805 moduleName: CORE,
6806 };
6807 Identifiers.ElementRef = { name: 'ElementRef', moduleName: CORE };
6808 Identifiers.NgModuleRef = { name: 'NgModuleRef', moduleName: CORE };
6809 Identifiers.ViewContainerRef = { name: 'ViewContainerRef', moduleName: CORE };
6810 Identifiers.ChangeDetectorRef = {
6811 name: 'ChangeDetectorRef',
6812 moduleName: CORE,
6813 };
6814 Identifiers.QueryList = { name: 'QueryList', moduleName: CORE };
6815 Identifiers.TemplateRef = { name: 'TemplateRef', moduleName: CORE };
6816 Identifiers.Renderer2 = { name: 'Renderer2', moduleName: CORE };
6817 Identifiers.CodegenComponentFactoryResolver = {
6818 name: 'ɵCodegenComponentFactoryResolver',
6819 moduleName: CORE,
6820 };
6821 Identifiers.ComponentFactoryResolver = {
6822 name: 'ComponentFactoryResolver',
6823 moduleName: CORE,
6824 };
6825 Identifiers.ComponentFactory = { name: 'ComponentFactory', moduleName: CORE };
6826 Identifiers.ComponentRef = { name: 'ComponentRef', moduleName: CORE };
6827 Identifiers.NgModuleFactory = { name: 'NgModuleFactory', moduleName: CORE };
6828 Identifiers.createModuleFactory = {
6829 name: 'ɵcmf',
6830 moduleName: CORE,
6831 };
6832 Identifiers.moduleDef = {
6833 name: 'ɵmod',
6834 moduleName: CORE,
6835 };
6836 Identifiers.moduleProviderDef = {
6837 name: 'ɵmpd',
6838 moduleName: CORE,
6839 };
6840 Identifiers.RegisterModuleFactoryFn = {
6841 name: 'ɵregisterModuleFactory',
6842 moduleName: CORE,
6843 };
6844 Identifiers.inject = { name: 'ɵɵinject', moduleName: CORE };
6845 Identifiers.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE };
6846 Identifiers.INJECTOR = { name: 'INJECTOR', moduleName: CORE };
6847 Identifiers.Injector = { name: 'Injector', moduleName: CORE };
6848 Identifiers.ViewEncapsulation = {
6849 name: 'ViewEncapsulation',
6850 moduleName: CORE,
6851 };
6852 Identifiers.ChangeDetectionStrategy = {
6853 name: 'ChangeDetectionStrategy',
6854 moduleName: CORE,
6855 };
6856 Identifiers.SecurityContext = {
6857 name: 'SecurityContext',
6858 moduleName: CORE,
6859 };
6860 Identifiers.LOCALE_ID = { name: 'LOCALE_ID', moduleName: CORE };
6861 Identifiers.TRANSLATIONS_FORMAT = {
6862 name: 'TRANSLATIONS_FORMAT',
6863 moduleName: CORE,
6864 };
6865 Identifiers.inlineInterpolate = {
6866 name: 'ɵinlineInterpolate',
6867 moduleName: CORE,
6868 };
6869 Identifiers.interpolate = { name: 'ɵinterpolate', moduleName: CORE };
6870 Identifiers.EMPTY_ARRAY = { name: 'ɵEMPTY_ARRAY', moduleName: CORE };
6871 Identifiers.EMPTY_MAP = { name: 'ɵEMPTY_MAP', moduleName: CORE };
6872 Identifiers.Renderer = { name: 'Renderer', moduleName: CORE };
6873 Identifiers.viewDef = { name: 'ɵvid', moduleName: CORE };
6874 Identifiers.elementDef = { name: 'ɵeld', moduleName: CORE };
6875 Identifiers.anchorDef = { name: 'ɵand', moduleName: CORE };
6876 Identifiers.textDef = { name: 'ɵted', moduleName: CORE };
6877 Identifiers.directiveDef = { name: 'ɵdid', moduleName: CORE };
6878 Identifiers.providerDef = { name: 'ɵprd', moduleName: CORE };
6879 Identifiers.queryDef = { name: 'ɵqud', moduleName: CORE };
6880 Identifiers.pureArrayDef = { name: 'ɵpad', moduleName: CORE };
6881 Identifiers.pureObjectDef = { name: 'ɵpod', moduleName: CORE };
6882 Identifiers.purePipeDef = { name: 'ɵppd', moduleName: CORE };
6883 Identifiers.pipeDef = { name: 'ɵpid', moduleName: CORE };
6884 Identifiers.nodeValue = { name: 'ɵnov', moduleName: CORE };
6885 Identifiers.ngContentDef = { name: 'ɵncd', moduleName: CORE };
6886 Identifiers.createRendererType2 = { name: 'ɵcrt', moduleName: CORE };
6887 // type only
6888 Identifiers.RendererType2 = {
6889 name: 'RendererType2',
6890 moduleName: CORE,
6891 };
6892 // type only
6893 Identifiers.ViewDefinition = {
6894 name: 'ɵViewDefinition',
6895 moduleName: CORE,
6896 };
6897 Identifiers.createComponentFactory = { name: 'ɵccf', moduleName: CORE };
6898
6899 /**
6900 * @license
6901 * Copyright Google LLC All Rights Reserved.
6902 *
6903 * Use of this source code is governed by an MIT-style license that can be
6904 * found in the LICENSE file at https://angular.io/license
6905 */
6906 class EventHandlerVars {
6907 }
6908 EventHandlerVars.event = variable('$event');
6909 class ConvertActionBindingResult {
6910 constructor(
6911 /**
6912 * Render2 compatible statements,
6913 */
6914 stmts,
6915 /**
6916 * Variable name used with render2 compatible statements.
6917 */
6918 allowDefault) {
6919 this.stmts = stmts;
6920 this.allowDefault = allowDefault;
6921 /**
6922 * This is bit of a hack. It converts statements which render2 expects to statements which are
6923 * expected by render3.
6924 *
6925 * Example: `<div click="doSomething($event)">` will generate:
6926 *
6927 * Render3:
6928 * ```
6929 * const pd_b:any = ((<any>ctx.doSomething($event)) !== false);
6930 * return pd_b;
6931 * ```
6932 *
6933 * but render2 expects:
6934 * ```
6935 * return ctx.doSomething($event);
6936 * ```
6937 */
6938 // TODO(misko): remove this hack once we no longer support ViewEngine.
6939 this.render3Stmts = stmts.map((statement) => {
6940 if (statement instanceof DeclareVarStmt && statement.name == allowDefault.name &&
6941 statement.value instanceof BinaryOperatorExpr) {
6942 const lhs = statement.value.lhs;
6943 return new ReturnStatement(lhs.value);
6944 }
6945 return statement;
6946 });
6947 }
6948 }
6949 /**
6950 * Converts the given expression AST into an executable output AST, assuming the expression is
6951 * used in an action binding (e.g. an event handler).
6952 */
6953 function convertActionBinding(localResolver, implicitReceiver, action, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses, globals) {
6954 if (!localResolver) {
6955 localResolver = new DefaultLocalResolver(globals);
6956 }
6957 const actionWithoutBuiltins = convertPropertyBindingBuiltins({
6958 createLiteralArrayConverter: (argCount) => {
6959 // Note: no caching for literal arrays in actions.
6960 return (args) => literalArr(args);
6961 },
6962 createLiteralMapConverter: (keys) => {
6963 // Note: no caching for literal maps in actions.
6964 return (values) => {
6965 const entries = keys.map((k, i) => ({
6966 key: k.key,
6967 value: values[i],
6968 quoted: k.quoted,
6969 }));
6970 return literalMap(entries);
6971 };
6972 },
6973 createPipeConverter: (name) => {
6974 throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
6975 }
6976 }, action);
6977 const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses);
6978 const actionStmts = [];
6979 flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
6980 prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
6981 if (visitor.usesImplicitReceiver) {
6982 localResolver.notifyImplicitReceiverUse();
6983 }
6984 const lastIndex = actionStmts.length - 1;
6985 let preventDefaultVar = null;
6986 if (lastIndex >= 0) {
6987 const lastStatement = actionStmts[lastIndex];
6988 const returnExpr = convertStmtIntoExpression(lastStatement);
6989 if (returnExpr) {
6990 // Note: We need to cast the result of the method call to dynamic,
6991 // as it might be a void method!
6992 preventDefaultVar = createPreventDefaultVar(bindingId);
6993 actionStmts[lastIndex] =
6994 preventDefaultVar.set(returnExpr.cast(DYNAMIC_TYPE).notIdentical(literal$1(false)))
6995 .toDeclStmt(null, [StmtModifier.Final]);
6996 }
6997 }
6998 return new ConvertActionBindingResult(actionStmts, preventDefaultVar);
6999 }
7000 function convertPropertyBindingBuiltins(converterFactory, ast) {
7001 return convertBuiltins(converterFactory, ast);
7002 }
7003 class ConvertPropertyBindingResult {
7004 constructor(stmts, currValExpr) {
7005 this.stmts = stmts;
7006 this.currValExpr = currValExpr;
7007 }
7008 }
7009 var BindingForm;
7010 (function (BindingForm) {
7011 // The general form of binding expression, supports all expressions.
7012 BindingForm[BindingForm["General"] = 0] = "General";
7013 // Try to generate a simple binding (no temporaries or statements)
7014 // otherwise generate a general binding
7015 BindingForm[BindingForm["TrySimple"] = 1] = "TrySimple";
7016 // Inlines assignment of temporaries into the generated expression. The result may still
7017 // have statements attached for declarations of temporary variables.
7018 // This is the only relevant form for Ivy, the other forms are only used in ViewEngine.
7019 BindingForm[BindingForm["Expression"] = 2] = "Expression";
7020 })(BindingForm || (BindingForm = {}));
7021 /**
7022 * Converts the given expression AST into an executable output AST, assuming the expression
7023 * is used in property binding. The expression has to be preprocessed via
7024 * `convertPropertyBindingBuiltins`.
7025 */
7026 function convertPropertyBinding(localResolver, implicitReceiver, expressionWithoutBuiltins, bindingId, form, interpolationFunction) {
7027 if (!localResolver) {
7028 localResolver = new DefaultLocalResolver();
7029 }
7030 const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
7031 const outputExpr = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
7032 const stmts = getStatementsFromVisitor(visitor, bindingId);
7033 if (visitor.usesImplicitReceiver) {
7034 localResolver.notifyImplicitReceiverUse();
7035 }
7036 if (visitor.temporaryCount === 0 && form == BindingForm.TrySimple) {
7037 return new ConvertPropertyBindingResult([], outputExpr);
7038 }
7039 else if (form === BindingForm.Expression) {
7040 return new ConvertPropertyBindingResult(stmts, outputExpr);
7041 }
7042 const currValExpr = createCurrValueExpr(bindingId);
7043 stmts.push(currValExpr.set(outputExpr).toDeclStmt(DYNAMIC_TYPE, [StmtModifier.Final]));
7044 return new ConvertPropertyBindingResult(stmts, currValExpr);
7045 }
7046 /**
7047 * Given some expression, such as a binding or interpolation expression, and a context expression to
7048 * look values up on, visit each facet of the given expression resolving values from the context
7049 * expression such that a list of arguments can be derived from the found values that can be used as
7050 * arguments to an external update instruction.
7051 *
7052 * @param localResolver The resolver to use to look up expressions by name appropriately
7053 * @param contextVariableExpression The expression representing the context variable used to create
7054 * the final argument expressions
7055 * @param expressionWithArgumentsToExtract The expression to visit to figure out what values need to
7056 * be resolved and what arguments list to build.
7057 * @param bindingId A name prefix used to create temporary variable names if they're needed for the
7058 * arguments generated
7059 * @returns An array of expressions that can be passed as arguments to instruction expressions like
7060 * `o.importExpr(R3.propertyInterpolate).callFn(result)`
7061 */
7062 function convertUpdateArguments(localResolver, contextVariableExpression, expressionWithArgumentsToExtract, bindingId) {
7063 const visitor = new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, undefined);
7064 const outputExpr = expressionWithArgumentsToExtract.visit(visitor, _Mode.Expression);
7065 if (visitor.usesImplicitReceiver) {
7066 localResolver.notifyImplicitReceiverUse();
7067 }
7068 const stmts = getStatementsFromVisitor(visitor, bindingId);
7069 // Removing the first argument, because it was a length for ViewEngine, not Ivy.
7070 let args = outputExpr.args.slice(1);
7071 if (expressionWithArgumentsToExtract instanceof Interpolation) {
7072 // If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the
7073 // args returned to just the value, because we're going to pass it to a special instruction.
7074 const strings = expressionWithArgumentsToExtract.strings;
7075 if (args.length === 3 && strings[0] === '' && strings[1] === '') {
7076 // Single argument interpolate instructions.
7077 args = [args[1]];
7078 }
7079 else if (args.length >= 19) {
7080 // 19 or more arguments must be passed to the `interpolateV`-style instructions, which accept
7081 // an array of arguments
7082 args = [literalArr(args)];
7083 }
7084 }
7085 return { stmts, args };
7086 }
7087 function getStatementsFromVisitor(visitor, bindingId) {
7088 const stmts = [];
7089 for (let i = 0; i < visitor.temporaryCount; i++) {
7090 stmts.push(temporaryDeclaration(bindingId, i));
7091 }
7092 return stmts;
7093 }
7094 function convertBuiltins(converterFactory, ast) {
7095 const visitor = new _BuiltinAstConverter(converterFactory);
7096 return ast.visit(visitor);
7097 }
7098 function temporaryName(bindingId, temporaryNumber) {
7099 return `tmp_${bindingId}_${temporaryNumber}`;
7100 }
7101 function temporaryDeclaration(bindingId, temporaryNumber) {
7102 return new DeclareVarStmt(temporaryName(bindingId, temporaryNumber));
7103 }
7104 function prependTemporaryDecls(temporaryCount, bindingId, statements) {
7105 for (let i = temporaryCount - 1; i >= 0; i--) {
7106 statements.unshift(temporaryDeclaration(bindingId, i));
7107 }
7108 }
7109 var _Mode;
7110 (function (_Mode) {
7111 _Mode[_Mode["Statement"] = 0] = "Statement";
7112 _Mode[_Mode["Expression"] = 1] = "Expression";
7113 })(_Mode || (_Mode = {}));
7114 function ensureStatementMode(mode, ast) {
7115 if (mode !== _Mode.Statement) {
7116 throw new Error(`Expected a statement, but saw ${ast}`);
7117 }
7118 }
7119 function ensureExpressionMode(mode, ast) {
7120 if (mode !== _Mode.Expression) {
7121 throw new Error(`Expected an expression, but saw ${ast}`);
7122 }
7123 }
7124 function convertToStatementIfNeeded(mode, expr) {
7125 if (mode === _Mode.Statement) {
7126 return expr.toStmt();
7127 }
7128 else {
7129 return expr;
7130 }
7131 }
7132 class _BuiltinAstConverter extends AstTransformer {
7133 constructor(_converterFactory) {
7134 super();
7135 this._converterFactory = _converterFactory;
7136 }
7137 visitPipe(ast, context) {
7138 const args = [ast.exp, ...ast.args].map(ast => ast.visit(this, context));
7139 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createPipeConverter(ast.name, args.length));
7140 }
7141 visitLiteralArray(ast, context) {
7142 const args = ast.expressions.map(ast => ast.visit(this, context));
7143 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralArrayConverter(ast.expressions.length));
7144 }
7145 visitLiteralMap(ast, context) {
7146 const args = ast.values.map(ast => ast.visit(this, context));
7147 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralMapConverter(ast.keys));
7148 }
7149 }
7150 class _AstToIrVisitor {
7151 constructor(_localResolver, _implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses) {
7152 this._localResolver = _localResolver;
7153 this._implicitReceiver = _implicitReceiver;
7154 this.bindingId = bindingId;
7155 this.interpolationFunction = interpolationFunction;
7156 this.baseSourceSpan = baseSourceSpan;
7157 this.implicitReceiverAccesses = implicitReceiverAccesses;
7158 this._nodeMap = new Map();
7159 this._resultMap = new Map();
7160 this._currentTemporary = 0;
7161 this.temporaryCount = 0;
7162 this.usesImplicitReceiver = false;
7163 }
7164 visitUnary(ast, mode) {
7165 let op;
7166 switch (ast.operator) {
7167 case '+':
7168 op = UnaryOperator.Plus;
7169 break;
7170 case '-':
7171 op = UnaryOperator.Minus;
7172 break;
7173 default:
7174 throw new Error(`Unsupported operator ${ast.operator}`);
7175 }
7176 return convertToStatementIfNeeded(mode, new UnaryOperatorExpr(op, this._visit(ast.expr, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
7177 }
7178 visitBinary(ast, mode) {
7179 let op;
7180 switch (ast.operation) {
7181 case '+':
7182 op = BinaryOperator.Plus;
7183 break;
7184 case '-':
7185 op = BinaryOperator.Minus;
7186 break;
7187 case '*':
7188 op = BinaryOperator.Multiply;
7189 break;
7190 case '/':
7191 op = BinaryOperator.Divide;
7192 break;
7193 case '%':
7194 op = BinaryOperator.Modulo;
7195 break;
7196 case '&&':
7197 op = BinaryOperator.And;
7198 break;
7199 case '||':
7200 op = BinaryOperator.Or;
7201 break;
7202 case '==':
7203 op = BinaryOperator.Equals;
7204 break;
7205 case '!=':
7206 op = BinaryOperator.NotEquals;
7207 break;
7208 case '===':
7209 op = BinaryOperator.Identical;
7210 break;
7211 case '!==':
7212 op = BinaryOperator.NotIdentical;
7213 break;
7214 case '<':
7215 op = BinaryOperator.Lower;
7216 break;
7217 case '>':
7218 op = BinaryOperator.Bigger;
7219 break;
7220 case '<=':
7221 op = BinaryOperator.LowerEquals;
7222 break;
7223 case '>=':
7224 op = BinaryOperator.BiggerEquals;
7225 break;
7226 case '??':
7227 return this.convertNullishCoalesce(ast, mode);
7228 default:
7229 throw new Error(`Unsupported operation ${ast.operation}`);
7230 }
7231 return convertToStatementIfNeeded(mode, new BinaryOperatorExpr(op, this._visit(ast.left, _Mode.Expression), this._visit(ast.right, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
7232 }
7233 visitChain(ast, mode) {
7234 ensureStatementMode(mode, ast);
7235 return this.visitAll(ast.expressions, mode);
7236 }
7237 visitConditional(ast, mode) {
7238 const value = this._visit(ast.condition, _Mode.Expression);
7239 return convertToStatementIfNeeded(mode, value.conditional(this._visit(ast.trueExp, _Mode.Expression), this._visit(ast.falseExp, _Mode.Expression), this.convertSourceSpan(ast.span)));
7240 }
7241 visitPipe(ast, mode) {
7242 throw new Error(`Illegal state: Pipes should have been converted into functions. Pipe: ${ast.name}`);
7243 }
7244 visitImplicitReceiver(ast, mode) {
7245 ensureExpressionMode(mode, ast);
7246 this.usesImplicitReceiver = true;
7247 return this._implicitReceiver;
7248 }
7249 visitThisReceiver(ast, mode) {
7250 return this.visitImplicitReceiver(ast, mode);
7251 }
7252 visitInterpolation(ast, mode) {
7253 ensureExpressionMode(mode, ast);
7254 const args = [literal$1(ast.expressions.length)];
7255 for (let i = 0; i < ast.strings.length - 1; i++) {
7256 args.push(literal$1(ast.strings[i]));
7257 args.push(this._visit(ast.expressions[i], _Mode.Expression));
7258 }
7259 args.push(literal$1(ast.strings[ast.strings.length - 1]));
7260 if (this.interpolationFunction) {
7261 return this.interpolationFunction(args);
7262 }
7263 return ast.expressions.length <= 9 ?
7264 importExpr(Identifiers.inlineInterpolate).callFn(args) :
7265 importExpr(Identifiers.interpolate).callFn([
7266 args[0], literalArr(args.slice(1), undefined, this.convertSourceSpan(ast.span))
7267 ]);
7268 }
7269 visitKeyedRead(ast, mode) {
7270 const leftMostSafe = this.leftMostSafeNode(ast);
7271 if (leftMostSafe) {
7272 return this.convertSafeAccess(ast, leftMostSafe, mode);
7273 }
7274 else {
7275 return convertToStatementIfNeeded(mode, this._visit(ast.receiver, _Mode.Expression).key(this._visit(ast.key, _Mode.Expression)));
7276 }
7277 }
7278 visitKeyedWrite(ast, mode) {
7279 const obj = this._visit(ast.receiver, _Mode.Expression);
7280 const key = this._visit(ast.key, _Mode.Expression);
7281 const value = this._visit(ast.value, _Mode.Expression);
7282 if (obj === this._implicitReceiver) {
7283 this._localResolver.maybeRestoreView();
7284 }
7285 return convertToStatementIfNeeded(mode, obj.key(key).set(value));
7286 }
7287 visitLiteralArray(ast, mode) {
7288 throw new Error(`Illegal State: literal arrays should have been converted into functions`);
7289 }
7290 visitLiteralMap(ast, mode) {
7291 throw new Error(`Illegal State: literal maps should have been converted into functions`);
7292 }
7293 visitLiteralPrimitive(ast, mode) {
7294 // For literal values of null, undefined, true, or false allow type interference
7295 // to infer the type.
7296 const type = ast.value === null || ast.value === undefined || ast.value === true || ast.value === true ?
7297 INFERRED_TYPE :
7298 undefined;
7299 return convertToStatementIfNeeded(mode, literal$1(ast.value, type, this.convertSourceSpan(ast.span)));
7300 }
7301 _getLocal(name, receiver) {
7302 if (this._localResolver.globals?.has(name) && receiver instanceof ThisReceiver) {
7303 return null;
7304 }
7305 return this._localResolver.getLocal(name);
7306 }
7307 visitPrefixNot(ast, mode) {
7308 return convertToStatementIfNeeded(mode, not(this._visit(ast.expression, _Mode.Expression)));
7309 }
7310 visitNonNullAssert(ast, mode) {
7311 return convertToStatementIfNeeded(mode, assertNotNull(this._visit(ast.expression, _Mode.Expression)));
7312 }
7313 visitPropertyRead(ast, mode) {
7314 const leftMostSafe = this.leftMostSafeNode(ast);
7315 if (leftMostSafe) {
7316 return this.convertSafeAccess(ast, leftMostSafe, mode);
7317 }
7318 else {
7319 let result = null;
7320 const prevUsesImplicitReceiver = this.usesImplicitReceiver;
7321 const receiver = this._visit(ast.receiver, _Mode.Expression);
7322 if (receiver === this._implicitReceiver) {
7323 result = this._getLocal(ast.name, ast.receiver);
7324 if (result) {
7325 // Restore the previous "usesImplicitReceiver" state since the implicit
7326 // receiver has been replaced with a resolved local expression.
7327 this.usesImplicitReceiver = prevUsesImplicitReceiver;
7328 this.addImplicitReceiverAccess(ast.name);
7329 }
7330 }
7331 if (result == null) {
7332 result = receiver.prop(ast.name, this.convertSourceSpan(ast.span));
7333 }
7334 return convertToStatementIfNeeded(mode, result);
7335 }
7336 }
7337 visitPropertyWrite(ast, mode) {
7338 const receiver = this._visit(ast.receiver, _Mode.Expression);
7339 const prevUsesImplicitReceiver = this.usesImplicitReceiver;
7340 let varExpr = null;
7341 if (receiver === this._implicitReceiver) {
7342 const localExpr = this._getLocal(ast.name, ast.receiver);
7343 if (localExpr) {
7344 if (localExpr instanceof ReadPropExpr) {
7345 // If the local variable is a property read expression, it's a reference
7346 // to a 'context.property' value and will be used as the target of the
7347 // write expression.
7348 varExpr = localExpr;
7349 // Restore the previous "usesImplicitReceiver" state since the implicit
7350 // receiver has been replaced with a resolved local expression.
7351 this.usesImplicitReceiver = prevUsesImplicitReceiver;
7352 this.addImplicitReceiverAccess(ast.name);
7353 }
7354 else {
7355 // Otherwise it's an error.
7356 const receiver = ast.name;
7357 const value = (ast.value instanceof PropertyRead) ? ast.value.name : undefined;
7358 throw new Error(`Cannot assign value "${value}" to template variable "${receiver}". Template variables are read-only.`);
7359 }
7360 }
7361 }
7362 // If no local expression could be produced, use the original receiver's
7363 // property as the target.
7364 if (varExpr === null) {
7365 varExpr = receiver.prop(ast.name, this.convertSourceSpan(ast.span));
7366 }
7367 return convertToStatementIfNeeded(mode, varExpr.set(this._visit(ast.value, _Mode.Expression)));
7368 }
7369 visitSafePropertyRead(ast, mode) {
7370 return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
7371 }
7372 visitSafeKeyedRead(ast, mode) {
7373 return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
7374 }
7375 visitAll(asts, mode) {
7376 return asts.map(ast => this._visit(ast, mode));
7377 }
7378 visitQuote(ast, mode) {
7379 throw new Error(`Quotes are not supported for evaluation!
7380 Statement: ${ast.uninterpretedExpression} located at ${ast.location}`);
7381 }
7382 visitCall(ast, mode) {
7383 const leftMostSafe = this.leftMostSafeNode(ast);
7384 if (leftMostSafe) {
7385 return this.convertSafeAccess(ast, leftMostSafe, mode);
7386 }
7387 const convertedArgs = this.visitAll(ast.args, _Mode.Expression);
7388 if (ast instanceof BuiltinFunctionCall) {
7389 return convertToStatementIfNeeded(mode, ast.converter(convertedArgs));
7390 }
7391 const receiver = ast.receiver;
7392 if (receiver instanceof PropertyRead &&
7393 receiver.receiver instanceof ImplicitReceiver &&
7394 !(receiver.receiver instanceof ThisReceiver) && receiver.name === '$any') {
7395 if (convertedArgs.length !== 1) {
7396 throw new Error(`Invalid call to $any, expected 1 argument but received ${convertedArgs.length || 'none'}`);
7397 }
7398 return convertToStatementIfNeeded(mode, convertedArgs[0]
7399 .cast(DYNAMIC_TYPE, this.convertSourceSpan(ast.span)));
7400 }
7401 const call = this._visit(receiver, _Mode.Expression)
7402 .callFn(convertedArgs, this.convertSourceSpan(ast.span));
7403 return convertToStatementIfNeeded(mode, call);
7404 }
7405 _visit(ast, mode) {
7406 const result = this._resultMap.get(ast);
7407 if (result)
7408 return result;
7409 return (this._nodeMap.get(ast) || ast).visit(this, mode);
7410 }
7411 convertSafeAccess(ast, leftMostSafe, mode) {
7412 // If the expression contains a safe access node on the left it needs to be converted to
7413 // an expression that guards the access to the member by checking the receiver for blank. As
7414 // execution proceeds from left to right, the left most part of the expression must be guarded
7415 // first but, because member access is left associative, the right side of the expression is at
7416 // the top of the AST. The desired result requires lifting a copy of the left part of the
7417 // expression up to test it for blank before generating the unguarded version.
7418 // Consider, for example the following expression: a?.b.c?.d.e
7419 // This results in the ast:
7420 // .
7421 // / \
7422 // ?. e
7423 // / \
7424 // . d
7425 // / \
7426 // ?. c
7427 // / \
7428 // a b
7429 // The following tree should be generated:
7430 //
7431 // /---- ? ----\
7432 // / | \
7433 // a /--- ? ---\ null
7434 // / | \
7435 // . . null
7436 // / \ / \
7437 // . c . e
7438 // / \ / \
7439 // a b . d
7440 // / \
7441 // . c
7442 // / \
7443 // a b
7444 //
7445 // Notice that the first guard condition is the left hand of the left most safe access node
7446 // which comes in as leftMostSafe to this routine.
7447 let guardedExpression = this._visit(leftMostSafe.receiver, _Mode.Expression);
7448 let temporary = undefined;
7449 if (this.needsTemporaryInSafeAccess(leftMostSafe.receiver)) {
7450 // If the expression has method calls or pipes then we need to save the result into a
7451 // temporary variable to avoid calling stateful or impure code more than once.
7452 temporary = this.allocateTemporary();
7453 // Preserve the result in the temporary variable
7454 guardedExpression = temporary.set(guardedExpression);
7455 // Ensure all further references to the guarded expression refer to the temporary instead.
7456 this._resultMap.set(leftMostSafe.receiver, temporary);
7457 }
7458 const condition = guardedExpression.isBlank();
7459 // Convert the ast to an unguarded access to the receiver's member. The map will substitute
7460 // leftMostNode with its unguarded version in the call to `this.visit()`.
7461 if (leftMostSafe instanceof SafeKeyedRead) {
7462 this._nodeMap.set(leftMostSafe, new KeyedRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.receiver, leftMostSafe.key));
7463 }
7464 else {
7465 this._nodeMap.set(leftMostSafe, new PropertyRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name));
7466 }
7467 // Recursively convert the node now without the guarded member access.
7468 const access = this._visit(ast, _Mode.Expression);
7469 // Remove the mapping. This is not strictly required as the converter only traverses each node
7470 // once but is safer if the conversion is changed to traverse the nodes more than once.
7471 this._nodeMap.delete(leftMostSafe);
7472 // If we allocated a temporary, release it.
7473 if (temporary) {
7474 this.releaseTemporary(temporary);
7475 }
7476 // Produce the conditional
7477 return convertToStatementIfNeeded(mode, condition.conditional(NULL_EXPR, access));
7478 }
7479 convertNullishCoalesce(ast, mode) {
7480 const left = this._visit(ast.left, _Mode.Expression);
7481 const right = this._visit(ast.right, _Mode.Expression);
7482 const temporary = this.allocateTemporary();
7483 this.releaseTemporary(temporary);
7484 // Generate the following expression. It is identical to how TS
7485 // transpiles binary expressions with a nullish coalescing operator.
7486 // let temp;
7487 // (temp = a) !== null && temp !== undefined ? temp : b;
7488 return convertToStatementIfNeeded(mode, temporary.set(left)
7489 .notIdentical(NULL_EXPR)
7490 .and(temporary.notIdentical(literal$1(undefined)))
7491 .conditional(temporary, right));
7492 }
7493 // Given an expression of the form a?.b.c?.d.e then the left most safe node is
7494 // the (a?.b). The . and ?. are left associative thus can be rewritten as:
7495 // ((((a?.c).b).c)?.d).e. This returns the most deeply nested safe read or
7496 // safe method call as this needs to be transformed initially to:
7497 // a == null ? null : a.c.b.c?.d.e
7498 // then to:
7499 // a == null ? null : a.b.c == null ? null : a.b.c.d.e
7500 leftMostSafeNode(ast) {
7501 const visit = (visitor, ast) => {
7502 return (this._nodeMap.get(ast) || ast).visit(visitor);
7503 };
7504 return ast.visit({
7505 visitUnary(ast) {
7506 return null;
7507 },
7508 visitBinary(ast) {
7509 return null;
7510 },
7511 visitChain(ast) {
7512 return null;
7513 },
7514 visitConditional(ast) {
7515 return null;
7516 },
7517 visitCall(ast) {
7518 return visit(this, ast.receiver);
7519 },
7520 visitImplicitReceiver(ast) {
7521 return null;
7522 },
7523 visitThisReceiver(ast) {
7524 return null;
7525 },
7526 visitInterpolation(ast) {
7527 return null;
7528 },
7529 visitKeyedRead(ast) {
7530 return visit(this, ast.receiver);
7531 },
7532 visitKeyedWrite(ast) {
7533 return null;
7534 },
7535 visitLiteralArray(ast) {
7536 return null;
7537 },
7538 visitLiteralMap(ast) {
7539 return null;
7540 },
7541 visitLiteralPrimitive(ast) {
7542 return null;
7543 },
7544 visitPipe(ast) {
7545 return null;
7546 },
7547 visitPrefixNot(ast) {
7548 return null;
7549 },
7550 visitNonNullAssert(ast) {
7551 return null;
7552 },
7553 visitPropertyRead(ast) {
7554 return visit(this, ast.receiver);
7555 },
7556 visitPropertyWrite(ast) {
7557 return null;
7558 },
7559 visitQuote(ast) {
7560 return null;
7561 },
7562 visitSafePropertyRead(ast) {
7563 return visit(this, ast.receiver) || ast;
7564 },
7565 visitSafeKeyedRead(ast) {
7566 return visit(this, ast.receiver) || ast;
7567 }
7568 });
7569 }
7570 // Returns true of the AST includes a method or a pipe indicating that, if the
7571 // expression is used as the target of a safe property or method access then
7572 // the expression should be stored into a temporary variable.
7573 needsTemporaryInSafeAccess(ast) {
7574 const visit = (visitor, ast) => {
7575 return ast && (this._nodeMap.get(ast) || ast).visit(visitor);
7576 };
7577 const visitSome = (visitor, ast) => {
7578 return ast.some(ast => visit(visitor, ast));
7579 };
7580 return ast.visit({
7581 visitUnary(ast) {
7582 return visit(this, ast.expr);
7583 },
7584 visitBinary(ast) {
7585 return visit(this, ast.left) || visit(this, ast.right);
7586 },
7587 visitChain(ast) {
7588 return false;
7589 },
7590 visitConditional(ast) {
7591 return visit(this, ast.condition) || visit(this, ast.trueExp) || visit(this, ast.falseExp);
7592 },
7593 visitCall(ast) {
7594 return true;
7595 },
7596 visitImplicitReceiver(ast) {
7597 return false;
7598 },
7599 visitThisReceiver(ast) {
7600 return false;
7601 },
7602 visitInterpolation(ast) {
7603 return visitSome(this, ast.expressions);
7604 },
7605 visitKeyedRead(ast) {
7606 return false;
7607 },
7608 visitKeyedWrite(ast) {
7609 return false;
7610 },
7611 visitLiteralArray(ast) {
7612 return true;
7613 },
7614 visitLiteralMap(ast) {
7615 return true;
7616 },
7617 visitLiteralPrimitive(ast) {
7618 return false;
7619 },
7620 visitPipe(ast) {
7621 return true;
7622 },
7623 visitPrefixNot(ast) {
7624 return visit(this, ast.expression);
7625 },
7626 visitNonNullAssert(ast) {
7627 return visit(this, ast.expression);
7628 },
7629 visitPropertyRead(ast) {
7630 return false;
7631 },
7632 visitPropertyWrite(ast) {
7633 return false;
7634 },
7635 visitQuote(ast) {
7636 return false;
7637 },
7638 visitSafePropertyRead(ast) {
7639 return false;
7640 },
7641 visitSafeKeyedRead(ast) {
7642 return false;
7643 }
7644 });
7645 }
7646 allocateTemporary() {
7647 const tempNumber = this._currentTemporary++;
7648 this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount);
7649 return new ReadVarExpr(temporaryName(this.bindingId, tempNumber));
7650 }
7651 releaseTemporary(temporary) {
7652 this._currentTemporary--;
7653 if (temporary.name != temporaryName(this.bindingId, this._currentTemporary)) {
7654 throw new Error(`Temporary ${temporary.name} released out of order`);
7655 }
7656 }
7657 /**
7658 * Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`.
7659 *
7660 * `ParseSpan` objects are relative to the start of the expression.
7661 * This method converts these to full `ParseSourceSpan` objects that
7662 * show where the span is within the overall source file.
7663 *
7664 * @param span the relative span to convert.
7665 * @returns a `ParseSourceSpan` for the given span or null if no
7666 * `baseSourceSpan` was provided to this class.
7667 */
7668 convertSourceSpan(span) {
7669 if (this.baseSourceSpan) {
7670 const start = this.baseSourceSpan.start.moveBy(span.start);
7671 const end = this.baseSourceSpan.start.moveBy(span.end);
7672 const fullStart = this.baseSourceSpan.fullStart.moveBy(span.start);
7673 return new ParseSourceSpan(start, end, fullStart);
7674 }
7675 else {
7676 return null;
7677 }
7678 }
7679 /** Adds the name of an AST to the list of implicit receiver accesses. */
7680 addImplicitReceiverAccess(name) {
7681 if (this.implicitReceiverAccesses) {
7682 this.implicitReceiverAccesses.add(name);
7683 }
7684 }
7685 }
7686 function flattenStatements(arg, output) {
7687 if (Array.isArray(arg)) {
7688 arg.forEach((entry) => flattenStatements(entry, output));
7689 }
7690 else {
7691 output.push(arg);
7692 }
7693 }
7694 class DefaultLocalResolver {
7695 constructor(globals) {
7696 this.globals = globals;
7697 }
7698 notifyImplicitReceiverUse() { }
7699 maybeRestoreView() { }
7700 getLocal(name) {
7701 if (name === EventHandlerVars.event.name) {
7702 return EventHandlerVars.event;
7703 }
7704 return null;
7705 }
7706 }
7707 function createCurrValueExpr(bindingId) {
7708 return variable(`currVal_${bindingId}`); // fix syntax highlighting: `
7709 }
7710 function createPreventDefaultVar(bindingId) {
7711 return variable(`pd_${bindingId}`);
7712 }
7713 function convertStmtIntoExpression(stmt) {
7714 if (stmt instanceof ExpressionStatement) {
7715 return stmt.expr;
7716 }
7717 else if (stmt instanceof ReturnStatement) {
7718 return stmt.value;
7719 }
7720 return null;
7721 }
7722 class BuiltinFunctionCall extends Call {
7723 constructor(span, sourceSpan, args, converter) {
7724 super(span, sourceSpan, new EmptyExpr(span, sourceSpan), args, null);
7725 this.converter = converter;
7726 }
7727 }
7728
7729 /**
7730 * @license
7731 * Copyright Google LLC All Rights Reserved.
7732 *
7733 * Use of this source code is governed by an MIT-style license that can be
7734 * found in the LICENSE file at https://angular.io/license
7735 */
7736 /**
7737 * This file is a port of shadowCSS from webcomponents.js to TypeScript.
7738 *
7739 * Please make sure to keep to edits in sync with the source file.
7740 *
7741 * Source:
7742 * https://github.com/webcomponents/webcomponentsjs/blob/4efecd7e0e/src/ShadowCSS/ShadowCSS.js
7743 *
7744 * The original file level comment is reproduced below
7745 */
7746 /*
7747 This is a limited shim for ShadowDOM css styling.
7748 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#styles
7749
7750 The intention here is to support only the styling features which can be
7751 relatively simply implemented. The goal is to allow users to avoid the
7752 most obvious pitfalls and do so without compromising performance significantly.
7753 For ShadowDOM styling that's not covered here, a set of best practices
7754 can be provided that should allow users to accomplish more complex styling.
7755
7756 The following is a list of specific ShadowDOM styling features and a brief
7757 discussion of the approach used to shim.
7758
7759 Shimmed features:
7760
7761 * :host, :host-context: ShadowDOM allows styling of the shadowRoot's host
7762 element using the :host rule. To shim this feature, the :host styles are
7763 reformatted and prefixed with a given scope name and promoted to a
7764 document level stylesheet.
7765 For example, given a scope name of .foo, a rule like this:
7766
7767 :host {
7768 background: red;
7769 }
7770 }
7771
7772 becomes:
7773
7774 .foo {
7775 background: red;
7776 }
7777
7778 * encapsulation: Styles defined within ShadowDOM, apply only to
7779 dom inside the ShadowDOM. Polymer uses one of two techniques to implement
7780 this feature.
7781
7782 By default, rules are prefixed with the host element tag name
7783 as a descendant selector. This ensures styling does not leak out of the 'top'
7784 of the element's ShadowDOM. For example,
7785
7786 div {
7787 font-weight: bold;
7788 }
7789
7790 becomes:
7791
7792 x-foo div {
7793 font-weight: bold;
7794 }
7795
7796 becomes:
7797
7798
7799 Alternatively, if WebComponents.ShadowCSS.strictStyling is set to true then
7800 selectors are scoped by adding an attribute selector suffix to each
7801 simple selector that contains the host element tag name. Each element
7802 in the element's ShadowDOM template is also given the scope attribute.
7803 Thus, these rules match only elements that have the scope attribute.
7804 For example, given a scope name of x-foo, a rule like this:
7805
7806 div {
7807 font-weight: bold;
7808 }
7809
7810 becomes:
7811
7812 div[x-foo] {
7813 font-weight: bold;
7814 }
7815
7816 Note that elements that are dynamically added to a scope must have the scope
7817 selector added to them manually.
7818
7819 * upper/lower bound encapsulation: Styles which are defined outside a
7820 shadowRoot should not cross the ShadowDOM boundary and should not apply
7821 inside a shadowRoot.
7822
7823 This styling behavior is not emulated. Some possible ways to do this that
7824 were rejected due to complexity and/or performance concerns include: (1) reset
7825 every possible property for every possible selector for a given scope name;
7826 (2) re-implement css in javascript.
7827
7828 As an alternative, users should make sure to use selectors
7829 specific to the scope in which they are working.
7830
7831 * ::distributed: This behavior is not emulated. It's often not necessary
7832 to style the contents of a specific insertion point and instead, descendants
7833 of the host element can be styled selectively. Users can also create an
7834 extra node around an insertion point and style that node's contents
7835 via descendent selectors. For example, with a shadowRoot like this:
7836
7837 <style>
7838 ::content(div) {
7839 background: red;
7840 }
7841 </style>
7842 <content></content>
7843
7844 could become:
7845
7846 <style>
7847 / *@polyfill .content-container div * /
7848 ::content(div) {
7849 background: red;
7850 }
7851 </style>
7852 <div class="content-container">
7853 <content></content>
7854 </div>
7855
7856 Note the use of @polyfill in the comment above a ShadowDOM specific style
7857 declaration. This is a directive to the styling shim to use the selector
7858 in comments in lieu of the next selector when running under polyfill.
7859 */
7860 class ShadowCss {
7861 constructor() {
7862 this.strictStyling = true;
7863 }
7864 /*
7865 * Shim some cssText with the given selector. Returns cssText that can
7866 * be included in the document via WebComponents.ShadowCSS.addCssToDocument(css).
7867 *
7868 * When strictStyling is true:
7869 * - selector is the attribute added to all elements inside the host,
7870 * - hostSelector is the attribute added to the host itself.
7871 */
7872 shimCssText(cssText, selector, hostSelector = '') {
7873 const commentsWithHash = extractCommentsWithHash(cssText);
7874 cssText = stripComments(cssText);
7875 cssText = this._insertDirectives(cssText);
7876 const scopedCssText = this._scopeCssText(cssText, selector, hostSelector);
7877 return [scopedCssText, ...commentsWithHash].join('\n');
7878 }
7879 _insertDirectives(cssText) {
7880 cssText = this._insertPolyfillDirectivesInCssText(cssText);
7881 return this._insertPolyfillRulesInCssText(cssText);
7882 }
7883 /*
7884 * Process styles to convert native ShadowDOM rules that will trip
7885 * up the css parser; we rely on decorating the stylesheet with inert rules.
7886 *
7887 * For example, we convert this rule:
7888 *
7889 * polyfill-next-selector { content: ':host menu-item'; }
7890 * ::content menu-item {
7891 *
7892 * to this:
7893 *
7894 * scopeName menu-item {
7895 *
7896 **/
7897 _insertPolyfillDirectivesInCssText(cssText) {
7898 // Difference with webcomponents.js: does not handle comments
7899 return cssText.replace(_cssContentNextSelectorRe, function (...m) {
7900 return m[2] + '{';
7901 });
7902 }
7903 /*
7904 * Process styles to add rules which will only apply under the polyfill
7905 *
7906 * For example, we convert this rule:
7907 *
7908 * polyfill-rule {
7909 * content: ':host menu-item';
7910 * ...
7911 * }
7912 *
7913 * to this:
7914 *
7915 * scopeName menu-item {...}
7916 *
7917 **/
7918 _insertPolyfillRulesInCssText(cssText) {
7919 // Difference with webcomponents.js: does not handle comments
7920 return cssText.replace(_cssContentRuleRe, (...m) => {
7921 const rule = m[0].replace(m[1], '').replace(m[2], '');
7922 return m[4] + rule;
7923 });
7924 }
7925 /* Ensure styles are scoped. Pseudo-scoping takes a rule like:
7926 *
7927 * .foo {... }
7928 *
7929 * and converts this to
7930 *
7931 * scopeName .foo { ... }
7932 */
7933 _scopeCssText(cssText, scopeSelector, hostSelector) {
7934 const unscopedRules = this._extractUnscopedRulesFromCssText(cssText);
7935 // replace :host and :host-context -shadowcsshost and -shadowcsshost respectively
7936 cssText = this._insertPolyfillHostInCssText(cssText);
7937 cssText = this._convertColonHost(cssText);
7938 cssText = this._convertColonHostContext(cssText);
7939 cssText = this._convertShadowDOMSelectors(cssText);
7940 if (scopeSelector) {
7941 cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector);
7942 }
7943 cssText = cssText + '\n' + unscopedRules;
7944 return cssText.trim();
7945 }
7946 /*
7947 * Process styles to add rules which will only apply under the polyfill
7948 * and do not process via CSSOM. (CSSOM is destructive to rules on rare
7949 * occasions, e.g. -webkit-calc on Safari.)
7950 * For example, we convert this rule:
7951 *
7952 * @polyfill-unscoped-rule {
7953 * content: 'menu-item';
7954 * ... }
7955 *
7956 * to this:
7957 *
7958 * menu-item {...}
7959 *
7960 **/
7961 _extractUnscopedRulesFromCssText(cssText) {
7962 // Difference with webcomponents.js: does not handle comments
7963 let r = '';
7964 let m;
7965 _cssContentUnscopedRuleRe.lastIndex = 0;
7966 while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) {
7967 const rule = m[0].replace(m[2], '').replace(m[1], m[4]);
7968 r += rule + '\n\n';
7969 }
7970 return r;
7971 }
7972 /*
7973 * convert a rule like :host(.foo) > .bar { }
7974 *
7975 * to
7976 *
7977 * .foo<scopeName> > .bar
7978 */
7979 _convertColonHost(cssText) {
7980 return cssText.replace(_cssColonHostRe, (_, hostSelectors, otherSelectors) => {
7981 if (hostSelectors) {
7982 const convertedSelectors = [];
7983 const hostSelectorArray = hostSelectors.split(',').map(p => p.trim());
7984 for (const hostSelector of hostSelectorArray) {
7985 if (!hostSelector)
7986 break;
7987 const convertedSelector = _polyfillHostNoCombinator + hostSelector.replace(_polyfillHost, '') + otherSelectors;
7988 convertedSelectors.push(convertedSelector);
7989 }
7990 return convertedSelectors.join(',');
7991 }
7992 else {
7993 return _polyfillHostNoCombinator + otherSelectors;
7994 }
7995 });
7996 }
7997 /*
7998 * convert a rule like :host-context(.foo) > .bar { }
7999 *
8000 * to
8001 *
8002 * .foo<scopeName> > .bar, .foo <scopeName> > .bar { }
8003 *
8004 * and
8005 *
8006 * :host-context(.foo:host) .bar { ... }
8007 *
8008 * to
8009 *
8010 * .foo<scopeName> .bar { ... }
8011 */
8012 _convertColonHostContext(cssText) {
8013 return cssText.replace(_cssColonHostContextReGlobal, selectorText => {
8014 // We have captured a selector that contains a `:host-context` rule.
8015 // For backward compatibility `:host-context` may contain a comma separated list of selectors.
8016 // Each context selector group will contain a list of host-context selectors that must match
8017 // an ancestor of the host.
8018 // (Normally `contextSelectorGroups` will only contain a single array of context selectors.)
8019 const contextSelectorGroups = [[]];
8020 // There may be more than `:host-context` in this selector so `selectorText` could look like:
8021 // `:host-context(.one):host-context(.two)`.
8022 // Execute `_cssColonHostContextRe` over and over until we have extracted all the
8023 // `:host-context` selectors from this selector.
8024 let match;
8025 while (match = _cssColonHostContextRe.exec(selectorText)) {
8026 // `match` = [':host-context(<selectors>)<rest>', <selectors>, <rest>]
8027 // The `<selectors>` could actually be a comma separated list: `:host-context(.one, .two)`.
8028 const newContextSelectors = (match[1] ?? '').trim().split(',').map(m => m.trim()).filter(m => m !== '');
8029 // We must duplicate the current selector group for each of these new selectors.
8030 // For example if the current groups are:
8031 // ```
8032 // [
8033 // ['a', 'b', 'c'],
8034 // ['x', 'y', 'z'],
8035 // ]
8036 // ```
8037 // And we have a new set of comma separated selectors: `:host-context(m,n)` then the new
8038 // groups are:
8039 // ```
8040 // [
8041 // ['a', 'b', 'c', 'm'],
8042 // ['x', 'y', 'z', 'm'],
8043 // ['a', 'b', 'c', 'n'],
8044 // ['x', 'y', 'z', 'n'],
8045 // ]
8046 // ```
8047 const contextSelectorGroupsLength = contextSelectorGroups.length;
8048 repeatGroups(contextSelectorGroups, newContextSelectors.length);
8049 for (let i = 0; i < newContextSelectors.length; i++) {
8050 for (let j = 0; j < contextSelectorGroupsLength; j++) {
8051 contextSelectorGroups[j + (i * contextSelectorGroupsLength)].push(newContextSelectors[i]);
8052 }
8053 }
8054 // Update the `selectorText` and see repeat to see if there are more `:host-context`s.
8055 selectorText = match[2];
8056 }
8057 // The context selectors now must be combined with each other to capture all the possible
8058 // selectors that `:host-context` can match. See `combineHostContextSelectors()` for more
8059 // info about how this is done.
8060 return contextSelectorGroups
8061 .map(contextSelectors => combineHostContextSelectors(contextSelectors, selectorText))
8062 .join(', ');
8063 });
8064 }
8065 /*
8066 * Convert combinators like ::shadow and pseudo-elements like ::content
8067 * by replacing with space.
8068 */
8069 _convertShadowDOMSelectors(cssText) {
8070 return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText);
8071 }
8072 // change a selector like 'div' to 'name div'
8073 _scopeSelectors(cssText, scopeSelector, hostSelector) {
8074 return processRules(cssText, (rule) => {
8075 let selector = rule.selector;
8076 let content = rule.content;
8077 if (rule.selector[0] !== '@') {
8078 selector =
8079 this._scopeSelector(rule.selector, scopeSelector, hostSelector, this.strictStyling);
8080 }
8081 else if (rule.selector.startsWith('@media') || rule.selector.startsWith('@supports') ||
8082 rule.selector.startsWith('@document')) {
8083 content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
8084 }
8085 else if (rule.selector.startsWith('@font-face') || rule.selector.startsWith('@page')) {
8086 content = this._stripScopingSelectors(rule.content);
8087 }
8088 return new CssRule(selector, content);
8089 });
8090 }
8091 /**
8092 * Handle a css text that is within a rule that should not contain scope selectors by simply
8093 * removing them! An example of such a rule is `@font-face`.
8094 *
8095 * `@font-face` rules cannot contain nested selectors. Nor can they be nested under a selector.
8096 * Normally this would be a syntax error by the author of the styles. But in some rare cases, such
8097 * as importing styles from a library, and applying `:host ::ng-deep` to the imported styles, we
8098 * can end up with broken css if the imported styles happen to contain @font-face rules.
8099 *
8100 * For example:
8101 *
8102 * ```
8103 * :host ::ng-deep {
8104 * import 'some/lib/containing/font-face';
8105 * }
8106 *
8107 * Similar logic applies to `@page` rules which can contain a particular set of properties,
8108 * as well as some specific at-rules. Since they can't be encapsulated, we have to strip
8109 * any scoping selectors from them. For more information: https://www.w3.org/TR/css-page-3
8110 * ```
8111 */
8112 _stripScopingSelectors(cssText) {
8113 return processRules(cssText, rule => {
8114 const selector = rule.selector.replace(_shadowDeepSelectors, ' ')
8115 .replace(_polyfillHostNoCombinatorRe, ' ');
8116 return new CssRule(selector, rule.content);
8117 });
8118 }
8119 _scopeSelector(selector, scopeSelector, hostSelector, strict) {
8120 return selector.split(',')
8121 .map(part => part.trim().split(_shadowDeepSelectors))
8122 .map((deepParts) => {
8123 const [shallowPart, ...otherParts] = deepParts;
8124 const applyScope = (shallowPart) => {
8125 if (this._selectorNeedsScoping(shallowPart, scopeSelector)) {
8126 return strict ?
8127 this._applyStrictSelectorScope(shallowPart, scopeSelector, hostSelector) :
8128 this._applySelectorScope(shallowPart, scopeSelector, hostSelector);
8129 }
8130 else {
8131 return shallowPart;
8132 }
8133 };
8134 return [applyScope(shallowPart), ...otherParts].join(' ');
8135 })
8136 .join(', ');
8137 }
8138 _selectorNeedsScoping(selector, scopeSelector) {
8139 const re = this._makeScopeMatcher(scopeSelector);
8140 return !re.test(selector);
8141 }
8142 _makeScopeMatcher(scopeSelector) {
8143 const lre = /\[/g;
8144 const rre = /\]/g;
8145 scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]');
8146 return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm');
8147 }
8148 _applySelectorScope(selector, scopeSelector, hostSelector) {
8149 // Difference from webcomponents.js: scopeSelector could not be an array
8150 return this._applySimpleSelectorScope(selector, scopeSelector, hostSelector);
8151 }
8152 // scope via name and [is=name]
8153 _applySimpleSelectorScope(selector, scopeSelector, hostSelector) {
8154 // In Android browser, the lastIndex is not reset when the regex is used in String.replace()
8155 _polyfillHostRe.lastIndex = 0;
8156 if (_polyfillHostRe.test(selector)) {
8157 const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
8158 return selector
8159 .replace(_polyfillHostNoCombinatorRe, (hnc, selector) => {
8160 return selector.replace(/([^:]*)(:*)(.*)/, (_, before, colon, after) => {
8161 return before + replaceBy + colon + after;
8162 });
8163 })
8164 .replace(_polyfillHostRe, replaceBy + ' ');
8165 }
8166 return scopeSelector + ' ' + selector;
8167 }
8168 // return a selector with [name] suffix on each simple selector
8169 // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] /** @internal */
8170 _applyStrictSelectorScope(selector, scopeSelector, hostSelector) {
8171 const isRe = /\[is=([^\]]*)\]/g;
8172 scopeSelector = scopeSelector.replace(isRe, (_, ...parts) => parts[0]);
8173 const attrName = '[' + scopeSelector + ']';
8174 const _scopeSelectorPart = (p) => {
8175 let scopedP = p.trim();
8176 if (!scopedP) {
8177 return '';
8178 }
8179 if (p.indexOf(_polyfillHostNoCombinator) > -1) {
8180 scopedP = this._applySimpleSelectorScope(p, scopeSelector, hostSelector);
8181 }
8182 else {
8183 // remove :host since it should be unnecessary
8184 const t = p.replace(_polyfillHostRe, '');
8185 if (t.length > 0) {
8186 const matches = t.match(/([^:]*)(:*)(.*)/);
8187 if (matches) {
8188 scopedP = matches[1] + attrName + matches[2] + matches[3];
8189 }
8190 }
8191 }
8192 return scopedP;
8193 };
8194 const safeContent = new SafeSelector(selector);
8195 selector = safeContent.content();
8196 let scopedSelector = '';
8197 let startIndex = 0;
8198 let res;
8199 const sep = /( |>|\+|~(?!=))\s*/g;
8200 // If a selector appears before :host it should not be shimmed as it
8201 // matches on ancestor elements and not on elements in the host's shadow
8202 // `:host-context(div)` is transformed to
8203 // `-shadowcsshost-no-combinatordiv, div -shadowcsshost-no-combinator`
8204 // the `div` is not part of the component in the 2nd selectors and should not be scoped.
8205 // Historically `component-tag:host` was matching the component so we also want to preserve
8206 // this behavior to avoid breaking legacy apps (it should not match).
8207 // The behavior should be:
8208 // - `tag:host` -> `tag[h]` (this is to avoid breaking legacy apps, should not match anything)
8209 // - `tag :host` -> `tag [h]` (`tag` is not scoped because it's considered part of a
8210 // `:host-context(tag)`)
8211 const hasHost = selector.indexOf(_polyfillHostNoCombinator) > -1;
8212 // Only scope parts after the first `-shadowcsshost-no-combinator` when it is present
8213 let shouldScope = !hasHost;
8214 while ((res = sep.exec(selector)) !== null) {
8215 const separator = res[1];
8216 const part = selector.slice(startIndex, res.index).trim();
8217 shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
8218 const scopedPart = shouldScope ? _scopeSelectorPart(part) : part;
8219 scopedSelector += `${scopedPart} ${separator} `;
8220 startIndex = sep.lastIndex;
8221 }
8222 const part = selector.substring(startIndex);
8223 shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
8224 scopedSelector += shouldScope ? _scopeSelectorPart(part) : part;
8225 // replace the placeholders with their original values
8226 return safeContent.restore(scopedSelector);
8227 }
8228 _insertPolyfillHostInCssText(selector) {
8229 return selector.replace(_colonHostContextRe, _polyfillHostContext)
8230 .replace(_colonHostRe, _polyfillHost);
8231 }
8232 }
8233 class SafeSelector {
8234 constructor(selector) {
8235 this.placeholders = [];
8236 this.index = 0;
8237 // Replaces attribute selectors with placeholders.
8238 // The WS in [attr="va lue"] would otherwise be interpreted as a selector separator.
8239 selector = this._escapeRegexMatches(selector, /(\[[^\]]*\])/g);
8240 // CSS allows for certain special characters to be used in selectors if they're escaped.
8241 // E.g. `.foo:blue` won't match a class called `foo:blue`, because the colon denotes a
8242 // pseudo-class, but writing `.foo\:blue` will match, because the colon was escaped.
8243 // Replace all escape sequences (`\` followed by a character) with a placeholder so
8244 // that our handling of pseudo-selectors doesn't mess with them.
8245 selector = this._escapeRegexMatches(selector, /(\\.)/g);
8246 // Replaces the expression in `:nth-child(2n + 1)` with a placeholder.
8247 // WS and "+" would otherwise be interpreted as selector separators.
8248 this._content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => {
8249 const replaceBy = `__ph-${this.index}__`;
8250 this.placeholders.push(exp);
8251 this.index++;
8252 return pseudo + replaceBy;
8253 });
8254 }
8255 restore(content) {
8256 return content.replace(/__ph-(\d+)__/g, (_ph, index) => this.placeholders[+index]);
8257 }
8258 content() {
8259 return this._content;
8260 }
8261 /**
8262 * Replaces all of the substrings that match a regex within a
8263 * special string (e.g. `__ph-0__`, `__ph-1__`, etc).
8264 */
8265 _escapeRegexMatches(content, pattern) {
8266 return content.replace(pattern, (_, keep) => {
8267 const replaceBy = `__ph-${this.index}__`;
8268 this.placeholders.push(keep);
8269 this.index++;
8270 return replaceBy;
8271 });
8272 }
8273 }
8274 const _cssContentNextSelectorRe = /polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
8275 const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
8276 const _cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
8277 const _polyfillHost = '-shadowcsshost';
8278 // note: :host-context pre-processed to -shadowcsshostcontext.
8279 const _polyfillHostContext = '-shadowcsscontext';
8280 const _parenSuffix = '(?:\\((' +
8281 '(?:\\([^)(]*\\)|[^)(]*)+?' +
8282 ')\\))?([^,{]*)';
8283 const _cssColonHostRe = new RegExp(_polyfillHost + _parenSuffix, 'gim');
8284 const _cssColonHostContextReGlobal = new RegExp(_polyfillHostContext + _parenSuffix, 'gim');
8285 const _cssColonHostContextRe = new RegExp(_polyfillHostContext + _parenSuffix, 'im');
8286 const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';
8287 const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/;
8288 const _shadowDOMSelectorsRe = [
8289 /::shadow/g,
8290 /::content/g,
8291 // Deprecated selectors
8292 /\/shadow-deep\//g,
8293 /\/shadow\//g,
8294 ];
8295 // The deep combinator is deprecated in the CSS spec
8296 // Support for `>>>`, `deep`, `::ng-deep` is then also deprecated and will be removed in the future.
8297 // see https://github.com/angular/angular/pull/17677
8298 const _shadowDeepSelectors = /(?:>>>)|(?:\/deep\/)|(?:::ng-deep)/g;
8299 const _selectorReSuffix = '([>\\s~+[.,{:][\\s\\S]*)?$';
8300 const _polyfillHostRe = /-shadowcsshost/gim;
8301 const _colonHostRe = /:host/gim;
8302 const _colonHostContextRe = /:host-context/gim;
8303 const _commentRe = /\/\*[\s\S]*?\*\//g;
8304 function stripComments(input) {
8305 return input.replace(_commentRe, '');
8306 }
8307 const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=[\s\S]+?\*\//g;
8308 function extractCommentsWithHash(input) {
8309 return input.match(_commentWithHashRe) || [];
8310 }
8311 const BLOCK_PLACEHOLDER = '%BLOCK%';
8312 const QUOTE_PLACEHOLDER = '%QUOTED%';
8313 const _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g;
8314 const _quotedRe = /%QUOTED%/g;
8315 const CONTENT_PAIRS = new Map([['{', '}']]);
8316 const QUOTE_PAIRS = new Map([[`"`, `"`], [`'`, `'`]]);
8317 class CssRule {
8318 constructor(selector, content) {
8319 this.selector = selector;
8320 this.content = content;
8321 }
8322 }
8323 function processRules(input, ruleCallback) {
8324 const inputWithEscapedQuotes = escapeBlocks(input, QUOTE_PAIRS, QUOTE_PLACEHOLDER);
8325 const inputWithEscapedBlocks = escapeBlocks(inputWithEscapedQuotes.escapedString, CONTENT_PAIRS, BLOCK_PLACEHOLDER);
8326 let nextBlockIndex = 0;
8327 let nextQuoteIndex = 0;
8328 return inputWithEscapedBlocks.escapedString
8329 .replace(_ruleRe, (...m) => {
8330 const selector = m[2];
8331 let content = '';
8332 let suffix = m[4];
8333 let contentPrefix = '';
8334 if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) {
8335 content = inputWithEscapedBlocks.blocks[nextBlockIndex++];
8336 suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1);
8337 contentPrefix = '{';
8338 }
8339 const rule = ruleCallback(new CssRule(selector, content));
8340 return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`;
8341 })
8342 .replace(_quotedRe, () => inputWithEscapedQuotes.blocks[nextQuoteIndex++]);
8343 }
8344 class StringWithEscapedBlocks {
8345 constructor(escapedString, blocks) {
8346 this.escapedString = escapedString;
8347 this.blocks = blocks;
8348 }
8349 }
8350 function escapeBlocks(input, charPairs, placeholder) {
8351 const resultParts = [];
8352 const escapedBlocks = [];
8353 let openCharCount = 0;
8354 let nonBlockStartIndex = 0;
8355 let blockStartIndex = -1;
8356 let openChar;
8357 let closeChar;
8358 for (let i = 0; i < input.length; i++) {
8359 const char = input[i];
8360 if (char === '\\') {
8361 i++;
8362 }
8363 else if (char === closeChar) {
8364 openCharCount--;
8365 if (openCharCount === 0) {
8366 escapedBlocks.push(input.substring(blockStartIndex, i));
8367 resultParts.push(placeholder);
8368 nonBlockStartIndex = i;
8369 blockStartIndex = -1;
8370 openChar = closeChar = undefined;
8371 }
8372 }
8373 else if (char === openChar) {
8374 openCharCount++;
8375 }
8376 else if (openCharCount === 0 && charPairs.has(char)) {
8377 openChar = char;
8378 closeChar = charPairs.get(char);
8379 openCharCount = 1;
8380 blockStartIndex = i + 1;
8381 resultParts.push(input.substring(nonBlockStartIndex, blockStartIndex));
8382 }
8383 }
8384 if (blockStartIndex !== -1) {
8385 escapedBlocks.push(input.substring(blockStartIndex));
8386 resultParts.push(placeholder);
8387 }
8388 else {
8389 resultParts.push(input.substring(nonBlockStartIndex));
8390 }
8391 return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks);
8392 }
8393 /**
8394 * Combine the `contextSelectors` with the `hostMarker` and the `otherSelectors`
8395 * to create a selector that matches the same as `:host-context()`.
8396 *
8397 * Given a single context selector `A` we need to output selectors that match on the host and as an
8398 * ancestor of the host:
8399 *
8400 * ```
8401 * A <hostMarker>, A<hostMarker> {}
8402 * ```
8403 *
8404 * When there is more than one context selector we also have to create combinations of those
8405 * selectors with each other. For example if there are `A` and `B` selectors the output is:
8406 *
8407 * ```
8408 * AB<hostMarker>, AB <hostMarker>, A B<hostMarker>,
8409 * B A<hostMarker>, A B <hostMarker>, B A <hostMarker> {}
8410 * ```
8411 *
8412 * And so on...
8413 *
8414 * @param hostMarker the string that selects the host element.
8415 * @param contextSelectors an array of context selectors that will be combined.
8416 * @param otherSelectors the rest of the selectors that are not context selectors.
8417 */
8418 function combineHostContextSelectors(contextSelectors, otherSelectors) {
8419 const hostMarker = _polyfillHostNoCombinator;
8420 _polyfillHostRe.lastIndex = 0; // reset the regex to ensure we get an accurate test
8421 const otherSelectorsHasHost = _polyfillHostRe.test(otherSelectors);
8422 // If there are no context selectors then just output a host marker
8423 if (contextSelectors.length === 0) {
8424 return hostMarker + otherSelectors;
8425 }
8426 const combined = [contextSelectors.pop() || ''];
8427 while (contextSelectors.length > 0) {
8428 const length = combined.length;
8429 const contextSelector = contextSelectors.pop();
8430 for (let i = 0; i < length; i++) {
8431 const previousSelectors = combined[i];
8432 // Add the new selector as a descendant of the previous selectors
8433 combined[length * 2 + i] = previousSelectors + ' ' + contextSelector;
8434 // Add the new selector as an ancestor of the previous selectors
8435 combined[length + i] = contextSelector + ' ' + previousSelectors;
8436 // Add the new selector to act on the same element as the previous selectors
8437 combined[i] = contextSelector + previousSelectors;
8438 }
8439 }
8440 // Finally connect the selector to the `hostMarker`s: either acting directly on the host
8441 // (A<hostMarker>) or as an ancestor (A <hostMarker>).
8442 return combined
8443 .map(s => otherSelectorsHasHost ?
8444 `${s}${otherSelectors}` :
8445 `${s}${hostMarker}${otherSelectors}, ${s} ${hostMarker}${otherSelectors}`)
8446 .join(',');
8447 }
8448 /**
8449 * Mutate the given `groups` array so that there are `multiples` clones of the original array
8450 * stored.
8451 *
8452 * For example `repeatGroups([a, b], 3)` will result in `[a, b, a, b, a, b]` - but importantly the
8453 * newly added groups will be clones of the original.
8454 *
8455 * @param groups An array of groups of strings that will be repeated. This array is mutated
8456 * in-place.
8457 * @param multiples The number of times the current groups should appear.
8458 */
8459 function repeatGroups(groups, multiples) {
8460 const length = groups.length;
8461 for (let i = 1; i < multiples; i++) {
8462 for (let j = 0; j < length; j++) {
8463 groups[j + (i * length)] = groups[j].slice(0);
8464 }
8465 }
8466 }
8467
8468 /**
8469 * @license
8470 * Copyright Google LLC All Rights Reserved.
8471 *
8472 * Use of this source code is governed by an MIT-style license that can be
8473 * found in the LICENSE file at https://angular.io/license
8474 */
8475 var CompileSummaryKind;
8476 (function (CompileSummaryKind) {
8477 CompileSummaryKind[CompileSummaryKind["Pipe"] = 0] = "Pipe";
8478 CompileSummaryKind[CompileSummaryKind["Directive"] = 1] = "Directive";
8479 CompileSummaryKind[CompileSummaryKind["NgModule"] = 2] = "NgModule";
8480 CompileSummaryKind[CompileSummaryKind["Injectable"] = 3] = "Injectable";
8481 })(CompileSummaryKind || (CompileSummaryKind = {}));
8482 function flatten(list) {
8483 return list.reduce((flat, item) => {
8484 const flatItem = Array.isArray(item) ? flatten(item) : item;
8485 return flat.concat(flatItem);
8486 }, []);
8487 }
8488
8489 /**
8490 * @license
8491 * Copyright Google LLC All Rights Reserved.
8492 *
8493 * Use of this source code is governed by an MIT-style license that can be
8494 * found in the LICENSE file at https://angular.io/license
8495 */
8496 const COMPONENT_VARIABLE = '%COMP%';
8497 const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
8498 const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
8499
8500 /**
8501 * @license
8502 * Copyright Google LLC All Rights Reserved.
8503 *
8504 * Use of this source code is governed by an MIT-style license that can be
8505 * found in the LICENSE file at https://angular.io/license
8506 */
8507 class NodeWithI18n {
8508 constructor(sourceSpan, i18n) {
8509 this.sourceSpan = sourceSpan;
8510 this.i18n = i18n;
8511 }
8512 }
8513 class Text extends NodeWithI18n {
8514 constructor(value, sourceSpan, tokens, i18n) {
8515 super(sourceSpan, i18n);
8516 this.value = value;
8517 this.tokens = tokens;
8518 }
8519 visit(visitor, context) {
8520 return visitor.visitText(this, context);
8521 }
8522 }
8523 class Expansion extends NodeWithI18n {
8524 constructor(switchValue, type, cases, sourceSpan, switchValueSourceSpan, i18n) {
8525 super(sourceSpan, i18n);
8526 this.switchValue = switchValue;
8527 this.type = type;
8528 this.cases = cases;
8529 this.switchValueSourceSpan = switchValueSourceSpan;
8530 }
8531 visit(visitor, context) {
8532 return visitor.visitExpansion(this, context);
8533 }
8534 }
8535 class ExpansionCase {
8536 constructor(value, expression, sourceSpan, valueSourceSpan, expSourceSpan) {
8537 this.value = value;
8538 this.expression = expression;
8539 this.sourceSpan = sourceSpan;
8540 this.valueSourceSpan = valueSourceSpan;
8541 this.expSourceSpan = expSourceSpan;
8542 }
8543 visit(visitor, context) {
8544 return visitor.visitExpansionCase(this, context);
8545 }
8546 }
8547 class Attribute extends NodeWithI18n {
8548 constructor(name, value, sourceSpan, keySpan, valueSpan, valueTokens, i18n) {
8549 super(sourceSpan, i18n);
8550 this.name = name;
8551 this.value = value;
8552 this.keySpan = keySpan;
8553 this.valueSpan = valueSpan;
8554 this.valueTokens = valueTokens;
8555 }
8556 visit(visitor, context) {
8557 return visitor.visitAttribute(this, context);
8558 }
8559 }
8560 class Element extends NodeWithI18n {
8561 constructor(name, attrs, children, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) {
8562 super(sourceSpan, i18n);
8563 this.name = name;
8564 this.attrs = attrs;
8565 this.children = children;
8566 this.startSourceSpan = startSourceSpan;
8567 this.endSourceSpan = endSourceSpan;
8568 }
8569 visit(visitor, context) {
8570 return visitor.visitElement(this, context);
8571 }
8572 }
8573 class Comment {
8574 constructor(value, sourceSpan) {
8575 this.value = value;
8576 this.sourceSpan = sourceSpan;
8577 }
8578 visit(visitor, context) {
8579 return visitor.visitComment(this, context);
8580 }
8581 }
8582 function visitAll(visitor, nodes, context = null) {
8583 const result = [];
8584 const visit = visitor.visit ?
8585 (ast) => visitor.visit(ast, context) || ast.visit(visitor, context) :
8586 (ast) => ast.visit(visitor, context);
8587 nodes.forEach(ast => {
8588 const astResult = visit(ast);
8589 if (astResult) {
8590 result.push(astResult);
8591 }
8592 });
8593 return result;
8594 }
8595
8596 /**
8597 * @license
8598 * Copyright Google LLC All Rights Reserved.
8599 *
8600 * Use of this source code is governed by an MIT-style license that can be
8601 * found in the LICENSE file at https://angular.io/license
8602 */
8603 // Mapping between all HTML entity names and their unicode representation.
8604 // Generated from https://html.spec.whatwg.org/multipage/entities.json by stripping
8605 // the `&` and `;` from the keys and removing the duplicates.
8606 // see https://www.w3.org/TR/html51/syntax.html#named-character-references
8607 const NAMED_ENTITIES = {
8608 'AElig': '\u00C6',
8609 'AMP': '\u0026',
8610 'amp': '\u0026',
8611 'Aacute': '\u00C1',
8612 'Abreve': '\u0102',
8613 'Acirc': '\u00C2',
8614 'Acy': '\u0410',
8615 'Afr': '\uD835\uDD04',
8616 'Agrave': '\u00C0',
8617 'Alpha': '\u0391',
8618 'Amacr': '\u0100',
8619 'And': '\u2A53',
8620 'Aogon': '\u0104',
8621 'Aopf': '\uD835\uDD38',
8622 'ApplyFunction': '\u2061',
8623 'af': '\u2061',
8624 'Aring': '\u00C5',
8625 'angst': '\u00C5',
8626 'Ascr': '\uD835\uDC9C',
8627 'Assign': '\u2254',
8628 'colone': '\u2254',
8629 'coloneq': '\u2254',
8630 'Atilde': '\u00C3',
8631 'Auml': '\u00C4',
8632 'Backslash': '\u2216',
8633 'setminus': '\u2216',
8634 'setmn': '\u2216',
8635 'smallsetminus': '\u2216',
8636 'ssetmn': '\u2216',
8637 'Barv': '\u2AE7',
8638 'Barwed': '\u2306',
8639 'doublebarwedge': '\u2306',
8640 'Bcy': '\u0411',
8641 'Because': '\u2235',
8642 'becaus': '\u2235',
8643 'because': '\u2235',
8644 'Bernoullis': '\u212C',
8645 'Bscr': '\u212C',
8646 'bernou': '\u212C',
8647 'Beta': '\u0392',
8648 'Bfr': '\uD835\uDD05',
8649 'Bopf': '\uD835\uDD39',
8650 'Breve': '\u02D8',
8651 'breve': '\u02D8',
8652 'Bumpeq': '\u224E',
8653 'HumpDownHump': '\u224E',
8654 'bump': '\u224E',
8655 'CHcy': '\u0427',
8656 'COPY': '\u00A9',
8657 'copy': '\u00A9',
8658 'Cacute': '\u0106',
8659 'Cap': '\u22D2',
8660 'CapitalDifferentialD': '\u2145',
8661 'DD': '\u2145',
8662 'Cayleys': '\u212D',
8663 'Cfr': '\u212D',
8664 'Ccaron': '\u010C',
8665 'Ccedil': '\u00C7',
8666 'Ccirc': '\u0108',
8667 'Cconint': '\u2230',
8668 'Cdot': '\u010A',
8669 'Cedilla': '\u00B8',
8670 'cedil': '\u00B8',
8671 'CenterDot': '\u00B7',
8672 'centerdot': '\u00B7',
8673 'middot': '\u00B7',
8674 'Chi': '\u03A7',
8675 'CircleDot': '\u2299',
8676 'odot': '\u2299',
8677 'CircleMinus': '\u2296',
8678 'ominus': '\u2296',
8679 'CirclePlus': '\u2295',
8680 'oplus': '\u2295',
8681 'CircleTimes': '\u2297',
8682 'otimes': '\u2297',
8683 'ClockwiseContourIntegral': '\u2232',
8684 'cwconint': '\u2232',
8685 'CloseCurlyDoubleQuote': '\u201D',
8686 'rdquo': '\u201D',
8687 'rdquor': '\u201D',
8688 'CloseCurlyQuote': '\u2019',
8689 'rsquo': '\u2019',
8690 'rsquor': '\u2019',
8691 'Colon': '\u2237',
8692 'Proportion': '\u2237',
8693 'Colone': '\u2A74',
8694 'Congruent': '\u2261',
8695 'equiv': '\u2261',
8696 'Conint': '\u222F',
8697 'DoubleContourIntegral': '\u222F',
8698 'ContourIntegral': '\u222E',
8699 'conint': '\u222E',
8700 'oint': '\u222E',
8701 'Copf': '\u2102',
8702 'complexes': '\u2102',
8703 'Coproduct': '\u2210',
8704 'coprod': '\u2210',
8705 'CounterClockwiseContourIntegral': '\u2233',
8706 'awconint': '\u2233',
8707 'Cross': '\u2A2F',
8708 'Cscr': '\uD835\uDC9E',
8709 'Cup': '\u22D3',
8710 'CupCap': '\u224D',
8711 'asympeq': '\u224D',
8712 'DDotrahd': '\u2911',
8713 'DJcy': '\u0402',
8714 'DScy': '\u0405',
8715 'DZcy': '\u040F',
8716 'Dagger': '\u2021',
8717 'ddagger': '\u2021',
8718 'Darr': '\u21A1',
8719 'Dashv': '\u2AE4',
8720 'DoubleLeftTee': '\u2AE4',
8721 'Dcaron': '\u010E',
8722 'Dcy': '\u0414',
8723 'Del': '\u2207',
8724 'nabla': '\u2207',
8725 'Delta': '\u0394',
8726 'Dfr': '\uD835\uDD07',
8727 'DiacriticalAcute': '\u00B4',
8728 'acute': '\u00B4',
8729 'DiacriticalDot': '\u02D9',
8730 'dot': '\u02D9',
8731 'DiacriticalDoubleAcute': '\u02DD',
8732 'dblac': '\u02DD',
8733 'DiacriticalGrave': '\u0060',
8734 'grave': '\u0060',
8735 'DiacriticalTilde': '\u02DC',
8736 'tilde': '\u02DC',
8737 'Diamond': '\u22C4',
8738 'diam': '\u22C4',
8739 'diamond': '\u22C4',
8740 'DifferentialD': '\u2146',
8741 'dd': '\u2146',
8742 'Dopf': '\uD835\uDD3B',
8743 'Dot': '\u00A8',
8744 'DoubleDot': '\u00A8',
8745 'die': '\u00A8',
8746 'uml': '\u00A8',
8747 'DotDot': '\u20DC',
8748 'DotEqual': '\u2250',
8749 'doteq': '\u2250',
8750 'esdot': '\u2250',
8751 'DoubleDownArrow': '\u21D3',
8752 'Downarrow': '\u21D3',
8753 'dArr': '\u21D3',
8754 'DoubleLeftArrow': '\u21D0',
8755 'Leftarrow': '\u21D0',
8756 'lArr': '\u21D0',
8757 'DoubleLeftRightArrow': '\u21D4',
8758 'Leftrightarrow': '\u21D4',
8759 'hArr': '\u21D4',
8760 'iff': '\u21D4',
8761 'DoubleLongLeftArrow': '\u27F8',
8762 'Longleftarrow': '\u27F8',
8763 'xlArr': '\u27F8',
8764 'DoubleLongLeftRightArrow': '\u27FA',
8765 'Longleftrightarrow': '\u27FA',
8766 'xhArr': '\u27FA',
8767 'DoubleLongRightArrow': '\u27F9',
8768 'Longrightarrow': '\u27F9',
8769 'xrArr': '\u27F9',
8770 'DoubleRightArrow': '\u21D2',
8771 'Implies': '\u21D2',
8772 'Rightarrow': '\u21D2',
8773 'rArr': '\u21D2',
8774 'DoubleRightTee': '\u22A8',
8775 'vDash': '\u22A8',
8776 'DoubleUpArrow': '\u21D1',
8777 'Uparrow': '\u21D1',
8778 'uArr': '\u21D1',
8779 'DoubleUpDownArrow': '\u21D5',
8780 'Updownarrow': '\u21D5',
8781 'vArr': '\u21D5',
8782 'DoubleVerticalBar': '\u2225',
8783 'par': '\u2225',
8784 'parallel': '\u2225',
8785 'shortparallel': '\u2225',
8786 'spar': '\u2225',
8787 'DownArrow': '\u2193',
8788 'ShortDownArrow': '\u2193',
8789 'darr': '\u2193',
8790 'downarrow': '\u2193',
8791 'DownArrowBar': '\u2913',
8792 'DownArrowUpArrow': '\u21F5',
8793 'duarr': '\u21F5',
8794 'DownBreve': '\u0311',
8795 'DownLeftRightVector': '\u2950',
8796 'DownLeftTeeVector': '\u295E',
8797 'DownLeftVector': '\u21BD',
8798 'leftharpoondown': '\u21BD',
8799 'lhard': '\u21BD',
8800 'DownLeftVectorBar': '\u2956',
8801 'DownRightTeeVector': '\u295F',
8802 'DownRightVector': '\u21C1',
8803 'rhard': '\u21C1',
8804 'rightharpoondown': '\u21C1',
8805 'DownRightVectorBar': '\u2957',
8806 'DownTee': '\u22A4',
8807 'top': '\u22A4',
8808 'DownTeeArrow': '\u21A7',
8809 'mapstodown': '\u21A7',
8810 'Dscr': '\uD835\uDC9F',
8811 'Dstrok': '\u0110',
8812 'ENG': '\u014A',
8813 'ETH': '\u00D0',
8814 'Eacute': '\u00C9',
8815 'Ecaron': '\u011A',
8816 'Ecirc': '\u00CA',
8817 'Ecy': '\u042D',
8818 'Edot': '\u0116',
8819 'Efr': '\uD835\uDD08',
8820 'Egrave': '\u00C8',
8821 'Element': '\u2208',
8822 'in': '\u2208',
8823 'isin': '\u2208',
8824 'isinv': '\u2208',
8825 'Emacr': '\u0112',
8826 'EmptySmallSquare': '\u25FB',
8827 'EmptyVerySmallSquare': '\u25AB',
8828 'Eogon': '\u0118',
8829 'Eopf': '\uD835\uDD3C',
8830 'Epsilon': '\u0395',
8831 'Equal': '\u2A75',
8832 'EqualTilde': '\u2242',
8833 'eqsim': '\u2242',
8834 'esim': '\u2242',
8835 'Equilibrium': '\u21CC',
8836 'rightleftharpoons': '\u21CC',
8837 'rlhar': '\u21CC',
8838 'Escr': '\u2130',
8839 'expectation': '\u2130',
8840 'Esim': '\u2A73',
8841 'Eta': '\u0397',
8842 'Euml': '\u00CB',
8843 'Exists': '\u2203',
8844 'exist': '\u2203',
8845 'ExponentialE': '\u2147',
8846 'ee': '\u2147',
8847 'exponentiale': '\u2147',
8848 'Fcy': '\u0424',
8849 'Ffr': '\uD835\uDD09',
8850 'FilledSmallSquare': '\u25FC',
8851 'FilledVerySmallSquare': '\u25AA',
8852 'blacksquare': '\u25AA',
8853 'squarf': '\u25AA',
8854 'squf': '\u25AA',
8855 'Fopf': '\uD835\uDD3D',
8856 'ForAll': '\u2200',
8857 'forall': '\u2200',
8858 'Fouriertrf': '\u2131',
8859 'Fscr': '\u2131',
8860 'GJcy': '\u0403',
8861 'GT': '\u003E',
8862 'gt': '\u003E',
8863 'Gamma': '\u0393',
8864 'Gammad': '\u03DC',
8865 'Gbreve': '\u011E',
8866 'Gcedil': '\u0122',
8867 'Gcirc': '\u011C',
8868 'Gcy': '\u0413',
8869 'Gdot': '\u0120',
8870 'Gfr': '\uD835\uDD0A',
8871 'Gg': '\u22D9',
8872 'ggg': '\u22D9',
8873 'Gopf': '\uD835\uDD3E',
8874 'GreaterEqual': '\u2265',
8875 'ge': '\u2265',
8876 'geq': '\u2265',
8877 'GreaterEqualLess': '\u22DB',
8878 'gel': '\u22DB',
8879 'gtreqless': '\u22DB',
8880 'GreaterFullEqual': '\u2267',
8881 'gE': '\u2267',
8882 'geqq': '\u2267',
8883 'GreaterGreater': '\u2AA2',
8884 'GreaterLess': '\u2277',
8885 'gl': '\u2277',
8886 'gtrless': '\u2277',
8887 'GreaterSlantEqual': '\u2A7E',
8888 'geqslant': '\u2A7E',
8889 'ges': '\u2A7E',
8890 'GreaterTilde': '\u2273',
8891 'gsim': '\u2273',
8892 'gtrsim': '\u2273',
8893 'Gscr': '\uD835\uDCA2',
8894 'Gt': '\u226B',
8895 'NestedGreaterGreater': '\u226B',
8896 'gg': '\u226B',
8897 'HARDcy': '\u042A',
8898 'Hacek': '\u02C7',
8899 'caron': '\u02C7',
8900 'Hat': '\u005E',
8901 'Hcirc': '\u0124',
8902 'Hfr': '\u210C',
8903 'Poincareplane': '\u210C',
8904 'HilbertSpace': '\u210B',
8905 'Hscr': '\u210B',
8906 'hamilt': '\u210B',
8907 'Hopf': '\u210D',
8908 'quaternions': '\u210D',
8909 'HorizontalLine': '\u2500',
8910 'boxh': '\u2500',
8911 'Hstrok': '\u0126',
8912 'HumpEqual': '\u224F',
8913 'bumpe': '\u224F',
8914 'bumpeq': '\u224F',
8915 'IEcy': '\u0415',
8916 'IJlig': '\u0132',
8917 'IOcy': '\u0401',
8918 'Iacute': '\u00CD',
8919 'Icirc': '\u00CE',
8920 'Icy': '\u0418',
8921 'Idot': '\u0130',
8922 'Ifr': '\u2111',
8923 'Im': '\u2111',
8924 'image': '\u2111',
8925 'imagpart': '\u2111',
8926 'Igrave': '\u00CC',
8927 'Imacr': '\u012A',
8928 'ImaginaryI': '\u2148',
8929 'ii': '\u2148',
8930 'Int': '\u222C',
8931 'Integral': '\u222B',
8932 'int': '\u222B',
8933 'Intersection': '\u22C2',
8934 'bigcap': '\u22C2',
8935 'xcap': '\u22C2',
8936 'InvisibleComma': '\u2063',
8937 'ic': '\u2063',
8938 'InvisibleTimes': '\u2062',
8939 'it': '\u2062',
8940 'Iogon': '\u012E',
8941 'Iopf': '\uD835\uDD40',
8942 'Iota': '\u0399',
8943 'Iscr': '\u2110',
8944 'imagline': '\u2110',
8945 'Itilde': '\u0128',
8946 'Iukcy': '\u0406',
8947 'Iuml': '\u00CF',
8948 'Jcirc': '\u0134',
8949 'Jcy': '\u0419',
8950 'Jfr': '\uD835\uDD0D',
8951 'Jopf': '\uD835\uDD41',
8952 'Jscr': '\uD835\uDCA5',
8953 'Jsercy': '\u0408',
8954 'Jukcy': '\u0404',
8955 'KHcy': '\u0425',
8956 'KJcy': '\u040C',
8957 'Kappa': '\u039A',
8958 'Kcedil': '\u0136',
8959 'Kcy': '\u041A',
8960 'Kfr': '\uD835\uDD0E',
8961 'Kopf': '\uD835\uDD42',
8962 'Kscr': '\uD835\uDCA6',
8963 'LJcy': '\u0409',
8964 'LT': '\u003C',
8965 'lt': '\u003C',
8966 'Lacute': '\u0139',
8967 'Lambda': '\u039B',
8968 'Lang': '\u27EA',
8969 'Laplacetrf': '\u2112',
8970 'Lscr': '\u2112',
8971 'lagran': '\u2112',
8972 'Larr': '\u219E',
8973 'twoheadleftarrow': '\u219E',
8974 'Lcaron': '\u013D',
8975 'Lcedil': '\u013B',
8976 'Lcy': '\u041B',
8977 'LeftAngleBracket': '\u27E8',
8978 'lang': '\u27E8',
8979 'langle': '\u27E8',
8980 'LeftArrow': '\u2190',
8981 'ShortLeftArrow': '\u2190',
8982 'larr': '\u2190',
8983 'leftarrow': '\u2190',
8984 'slarr': '\u2190',
8985 'LeftArrowBar': '\u21E4',
8986 'larrb': '\u21E4',
8987 'LeftArrowRightArrow': '\u21C6',
8988 'leftrightarrows': '\u21C6',
8989 'lrarr': '\u21C6',
8990 'LeftCeiling': '\u2308',
8991 'lceil': '\u2308',
8992 'LeftDoubleBracket': '\u27E6',
8993 'lobrk': '\u27E6',
8994 'LeftDownTeeVector': '\u2961',
8995 'LeftDownVector': '\u21C3',
8996 'dharl': '\u21C3',
8997 'downharpoonleft': '\u21C3',
8998 'LeftDownVectorBar': '\u2959',
8999 'LeftFloor': '\u230A',
9000 'lfloor': '\u230A',
9001 'LeftRightArrow': '\u2194',
9002 'harr': '\u2194',
9003 'leftrightarrow': '\u2194',
9004 'LeftRightVector': '\u294E',
9005 'LeftTee': '\u22A3',
9006 'dashv': '\u22A3',
9007 'LeftTeeArrow': '\u21A4',
9008 'mapstoleft': '\u21A4',
9009 'LeftTeeVector': '\u295A',
9010 'LeftTriangle': '\u22B2',
9011 'vartriangleleft': '\u22B2',
9012 'vltri': '\u22B2',
9013 'LeftTriangleBar': '\u29CF',
9014 'LeftTriangleEqual': '\u22B4',
9015 'ltrie': '\u22B4',
9016 'trianglelefteq': '\u22B4',
9017 'LeftUpDownVector': '\u2951',
9018 'LeftUpTeeVector': '\u2960',
9019 'LeftUpVector': '\u21BF',
9020 'uharl': '\u21BF',
9021 'upharpoonleft': '\u21BF',
9022 'LeftUpVectorBar': '\u2958',
9023 'LeftVector': '\u21BC',
9024 'leftharpoonup': '\u21BC',
9025 'lharu': '\u21BC',
9026 'LeftVectorBar': '\u2952',
9027 'LessEqualGreater': '\u22DA',
9028 'leg': '\u22DA',
9029 'lesseqgtr': '\u22DA',
9030 'LessFullEqual': '\u2266',
9031 'lE': '\u2266',
9032 'leqq': '\u2266',
9033 'LessGreater': '\u2276',
9034 'lessgtr': '\u2276',
9035 'lg': '\u2276',
9036 'LessLess': '\u2AA1',
9037 'LessSlantEqual': '\u2A7D',
9038 'leqslant': '\u2A7D',
9039 'les': '\u2A7D',
9040 'LessTilde': '\u2272',
9041 'lesssim': '\u2272',
9042 'lsim': '\u2272',
9043 'Lfr': '\uD835\uDD0F',
9044 'Ll': '\u22D8',
9045 'Lleftarrow': '\u21DA',
9046 'lAarr': '\u21DA',
9047 'Lmidot': '\u013F',
9048 'LongLeftArrow': '\u27F5',
9049 'longleftarrow': '\u27F5',
9050 'xlarr': '\u27F5',
9051 'LongLeftRightArrow': '\u27F7',
9052 'longleftrightarrow': '\u27F7',
9053 'xharr': '\u27F7',
9054 'LongRightArrow': '\u27F6',
9055 'longrightarrow': '\u27F6',
9056 'xrarr': '\u27F6',
9057 'Lopf': '\uD835\uDD43',
9058 'LowerLeftArrow': '\u2199',
9059 'swarr': '\u2199',
9060 'swarrow': '\u2199',
9061 'LowerRightArrow': '\u2198',
9062 'searr': '\u2198',
9063 'searrow': '\u2198',
9064 'Lsh': '\u21B0',
9065 'lsh': '\u21B0',
9066 'Lstrok': '\u0141',
9067 'Lt': '\u226A',
9068 'NestedLessLess': '\u226A',
9069 'll': '\u226A',
9070 'Map': '\u2905',
9071 'Mcy': '\u041C',
9072 'MediumSpace': '\u205F',
9073 'Mellintrf': '\u2133',
9074 'Mscr': '\u2133',
9075 'phmmat': '\u2133',
9076 'Mfr': '\uD835\uDD10',
9077 'MinusPlus': '\u2213',
9078 'mnplus': '\u2213',
9079 'mp': '\u2213',
9080 'Mopf': '\uD835\uDD44',
9081 'Mu': '\u039C',
9082 'NJcy': '\u040A',
9083 'Nacute': '\u0143',
9084 'Ncaron': '\u0147',
9085 'Ncedil': '\u0145',
9086 'Ncy': '\u041D',
9087 'NegativeMediumSpace': '\u200B',
9088 'NegativeThickSpace': '\u200B',
9089 'NegativeThinSpace': '\u200B',
9090 'NegativeVeryThinSpace': '\u200B',
9091 'ZeroWidthSpace': '\u200B',
9092 'NewLine': '\u000A',
9093 'Nfr': '\uD835\uDD11',
9094 'NoBreak': '\u2060',
9095 'NonBreakingSpace': '\u00A0',
9096 'nbsp': '\u00A0',
9097 'Nopf': '\u2115',
9098 'naturals': '\u2115',
9099 'Not': '\u2AEC',
9100 'NotCongruent': '\u2262',
9101 'nequiv': '\u2262',
9102 'NotCupCap': '\u226D',
9103 'NotDoubleVerticalBar': '\u2226',
9104 'npar': '\u2226',
9105 'nparallel': '\u2226',
9106 'nshortparallel': '\u2226',
9107 'nspar': '\u2226',
9108 'NotElement': '\u2209',
9109 'notin': '\u2209',
9110 'notinva': '\u2209',
9111 'NotEqual': '\u2260',
9112 'ne': '\u2260',
9113 'NotEqualTilde': '\u2242\u0338',
9114 'nesim': '\u2242\u0338',
9115 'NotExists': '\u2204',
9116 'nexist': '\u2204',
9117 'nexists': '\u2204',
9118 'NotGreater': '\u226F',
9119 'ngt': '\u226F',
9120 'ngtr': '\u226F',
9121 'NotGreaterEqual': '\u2271',
9122 'nge': '\u2271',
9123 'ngeq': '\u2271',
9124 'NotGreaterFullEqual': '\u2267\u0338',
9125 'ngE': '\u2267\u0338',
9126 'ngeqq': '\u2267\u0338',
9127 'NotGreaterGreater': '\u226B\u0338',
9128 'nGtv': '\u226B\u0338',
9129 'NotGreaterLess': '\u2279',
9130 'ntgl': '\u2279',
9131 'NotGreaterSlantEqual': '\u2A7E\u0338',
9132 'ngeqslant': '\u2A7E\u0338',
9133 'nges': '\u2A7E\u0338',
9134 'NotGreaterTilde': '\u2275',
9135 'ngsim': '\u2275',
9136 'NotHumpDownHump': '\u224E\u0338',
9137 'nbump': '\u224E\u0338',
9138 'NotHumpEqual': '\u224F\u0338',
9139 'nbumpe': '\u224F\u0338',
9140 'NotLeftTriangle': '\u22EA',
9141 'nltri': '\u22EA',
9142 'ntriangleleft': '\u22EA',
9143 'NotLeftTriangleBar': '\u29CF\u0338',
9144 'NotLeftTriangleEqual': '\u22EC',
9145 'nltrie': '\u22EC',
9146 'ntrianglelefteq': '\u22EC',
9147 'NotLess': '\u226E',
9148 'nless': '\u226E',
9149 'nlt': '\u226E',
9150 'NotLessEqual': '\u2270',
9151 'nle': '\u2270',
9152 'nleq': '\u2270',
9153 'NotLessGreater': '\u2278',
9154 'ntlg': '\u2278',
9155 'NotLessLess': '\u226A\u0338',
9156 'nLtv': '\u226A\u0338',
9157 'NotLessSlantEqual': '\u2A7D\u0338',
9158 'nleqslant': '\u2A7D\u0338',
9159 'nles': '\u2A7D\u0338',
9160 'NotLessTilde': '\u2274',
9161 'nlsim': '\u2274',
9162 'NotNestedGreaterGreater': '\u2AA2\u0338',
9163 'NotNestedLessLess': '\u2AA1\u0338',
9164 'NotPrecedes': '\u2280',
9165 'npr': '\u2280',
9166 'nprec': '\u2280',
9167 'NotPrecedesEqual': '\u2AAF\u0338',
9168 'npre': '\u2AAF\u0338',
9169 'npreceq': '\u2AAF\u0338',
9170 'NotPrecedesSlantEqual': '\u22E0',
9171 'nprcue': '\u22E0',
9172 'NotReverseElement': '\u220C',
9173 'notni': '\u220C',
9174 'notniva': '\u220C',
9175 'NotRightTriangle': '\u22EB',
9176 'nrtri': '\u22EB',
9177 'ntriangleright': '\u22EB',
9178 'NotRightTriangleBar': '\u29D0\u0338',
9179 'NotRightTriangleEqual': '\u22ED',
9180 'nrtrie': '\u22ED',
9181 'ntrianglerighteq': '\u22ED',
9182 'NotSquareSubset': '\u228F\u0338',
9183 'NotSquareSubsetEqual': '\u22E2',
9184 'nsqsube': '\u22E2',
9185 'NotSquareSuperset': '\u2290\u0338',
9186 'NotSquareSupersetEqual': '\u22E3',
9187 'nsqsupe': '\u22E3',
9188 'NotSubset': '\u2282\u20D2',
9189 'nsubset': '\u2282\u20D2',
9190 'vnsub': '\u2282\u20D2',
9191 'NotSubsetEqual': '\u2288',
9192 'nsube': '\u2288',
9193 'nsubseteq': '\u2288',
9194 'NotSucceeds': '\u2281',
9195 'nsc': '\u2281',
9196 'nsucc': '\u2281',
9197 'NotSucceedsEqual': '\u2AB0\u0338',
9198 'nsce': '\u2AB0\u0338',
9199 'nsucceq': '\u2AB0\u0338',
9200 'NotSucceedsSlantEqual': '\u22E1',
9201 'nsccue': '\u22E1',
9202 'NotSucceedsTilde': '\u227F\u0338',
9203 'NotSuperset': '\u2283\u20D2',
9204 'nsupset': '\u2283\u20D2',
9205 'vnsup': '\u2283\u20D2',
9206 'NotSupersetEqual': '\u2289',
9207 'nsupe': '\u2289',
9208 'nsupseteq': '\u2289',
9209 'NotTilde': '\u2241',
9210 'nsim': '\u2241',
9211 'NotTildeEqual': '\u2244',
9212 'nsime': '\u2244',
9213 'nsimeq': '\u2244',
9214 'NotTildeFullEqual': '\u2247',
9215 'ncong': '\u2247',
9216 'NotTildeTilde': '\u2249',
9217 'nap': '\u2249',
9218 'napprox': '\u2249',
9219 'NotVerticalBar': '\u2224',
9220 'nmid': '\u2224',
9221 'nshortmid': '\u2224',
9222 'nsmid': '\u2224',
9223 'Nscr': '\uD835\uDCA9',
9224 'Ntilde': '\u00D1',
9225 'Nu': '\u039D',
9226 'OElig': '\u0152',
9227 'Oacute': '\u00D3',
9228 'Ocirc': '\u00D4',
9229 'Ocy': '\u041E',
9230 'Odblac': '\u0150',
9231 'Ofr': '\uD835\uDD12',
9232 'Ograve': '\u00D2',
9233 'Omacr': '\u014C',
9234 'Omega': '\u03A9',
9235 'ohm': '\u03A9',
9236 'Omicron': '\u039F',
9237 'Oopf': '\uD835\uDD46',
9238 'OpenCurlyDoubleQuote': '\u201C',
9239 'ldquo': '\u201C',
9240 'OpenCurlyQuote': '\u2018',
9241 'lsquo': '\u2018',
9242 'Or': '\u2A54',
9243 'Oscr': '\uD835\uDCAA',
9244 'Oslash': '\u00D8',
9245 'Otilde': '\u00D5',
9246 'Otimes': '\u2A37',
9247 'Ouml': '\u00D6',
9248 'OverBar': '\u203E',
9249 'oline': '\u203E',
9250 'OverBrace': '\u23DE',
9251 'OverBracket': '\u23B4',
9252 'tbrk': '\u23B4',
9253 'OverParenthesis': '\u23DC',
9254 'PartialD': '\u2202',
9255 'part': '\u2202',
9256 'Pcy': '\u041F',
9257 'Pfr': '\uD835\uDD13',
9258 'Phi': '\u03A6',
9259 'Pi': '\u03A0',
9260 'PlusMinus': '\u00B1',
9261 'plusmn': '\u00B1',
9262 'pm': '\u00B1',
9263 'Popf': '\u2119',
9264 'primes': '\u2119',
9265 'Pr': '\u2ABB',
9266 'Precedes': '\u227A',
9267 'pr': '\u227A',
9268 'prec': '\u227A',
9269 'PrecedesEqual': '\u2AAF',
9270 'pre': '\u2AAF',
9271 'preceq': '\u2AAF',
9272 'PrecedesSlantEqual': '\u227C',
9273 'prcue': '\u227C',
9274 'preccurlyeq': '\u227C',
9275 'PrecedesTilde': '\u227E',
9276 'precsim': '\u227E',
9277 'prsim': '\u227E',
9278 'Prime': '\u2033',
9279 'Product': '\u220F',
9280 'prod': '\u220F',
9281 'Proportional': '\u221D',
9282 'prop': '\u221D',
9283 'propto': '\u221D',
9284 'varpropto': '\u221D',
9285 'vprop': '\u221D',
9286 'Pscr': '\uD835\uDCAB',
9287 'Psi': '\u03A8',
9288 'QUOT': '\u0022',
9289 'quot': '\u0022',
9290 'Qfr': '\uD835\uDD14',
9291 'Qopf': '\u211A',
9292 'rationals': '\u211A',
9293 'Qscr': '\uD835\uDCAC',
9294 'RBarr': '\u2910',
9295 'drbkarow': '\u2910',
9296 'REG': '\u00AE',
9297 'circledR': '\u00AE',
9298 'reg': '\u00AE',
9299 'Racute': '\u0154',
9300 'Rang': '\u27EB',
9301 'Rarr': '\u21A0',
9302 'twoheadrightarrow': '\u21A0',
9303 'Rarrtl': '\u2916',
9304 'Rcaron': '\u0158',
9305 'Rcedil': '\u0156',
9306 'Rcy': '\u0420',
9307 'Re': '\u211C',
9308 'Rfr': '\u211C',
9309 'real': '\u211C',
9310 'realpart': '\u211C',
9311 'ReverseElement': '\u220B',
9312 'SuchThat': '\u220B',
9313 'ni': '\u220B',
9314 'niv': '\u220B',
9315 'ReverseEquilibrium': '\u21CB',
9316 'leftrightharpoons': '\u21CB',
9317 'lrhar': '\u21CB',
9318 'ReverseUpEquilibrium': '\u296F',
9319 'duhar': '\u296F',
9320 'Rho': '\u03A1',
9321 'RightAngleBracket': '\u27E9',
9322 'rang': '\u27E9',
9323 'rangle': '\u27E9',
9324 'RightArrow': '\u2192',
9325 'ShortRightArrow': '\u2192',
9326 'rarr': '\u2192',
9327 'rightarrow': '\u2192',
9328 'srarr': '\u2192',
9329 'RightArrowBar': '\u21E5',
9330 'rarrb': '\u21E5',
9331 'RightArrowLeftArrow': '\u21C4',
9332 'rightleftarrows': '\u21C4',
9333 'rlarr': '\u21C4',
9334 'RightCeiling': '\u2309',
9335 'rceil': '\u2309',
9336 'RightDoubleBracket': '\u27E7',
9337 'robrk': '\u27E7',
9338 'RightDownTeeVector': '\u295D',
9339 'RightDownVector': '\u21C2',
9340 'dharr': '\u21C2',
9341 'downharpoonright': '\u21C2',
9342 'RightDownVectorBar': '\u2955',
9343 'RightFloor': '\u230B',
9344 'rfloor': '\u230B',
9345 'RightTee': '\u22A2',
9346 'vdash': '\u22A2',
9347 'RightTeeArrow': '\u21A6',
9348 'map': '\u21A6',
9349 'mapsto': '\u21A6',
9350 'RightTeeVector': '\u295B',
9351 'RightTriangle': '\u22B3',
9352 'vartriangleright': '\u22B3',
9353 'vrtri': '\u22B3',
9354 'RightTriangleBar': '\u29D0',
9355 'RightTriangleEqual': '\u22B5',
9356 'rtrie': '\u22B5',
9357 'trianglerighteq': '\u22B5',
9358 'RightUpDownVector': '\u294F',
9359 'RightUpTeeVector': '\u295C',
9360 'RightUpVector': '\u21BE',
9361 'uharr': '\u21BE',
9362 'upharpoonright': '\u21BE',
9363 'RightUpVectorBar': '\u2954',
9364 'RightVector': '\u21C0',
9365 'rharu': '\u21C0',
9366 'rightharpoonup': '\u21C0',
9367 'RightVectorBar': '\u2953',
9368 'Ropf': '\u211D',
9369 'reals': '\u211D',
9370 'RoundImplies': '\u2970',
9371 'Rrightarrow': '\u21DB',
9372 'rAarr': '\u21DB',
9373 'Rscr': '\u211B',
9374 'realine': '\u211B',
9375 'Rsh': '\u21B1',
9376 'rsh': '\u21B1',
9377 'RuleDelayed': '\u29F4',
9378 'SHCHcy': '\u0429',
9379 'SHcy': '\u0428',
9380 'SOFTcy': '\u042C',
9381 'Sacute': '\u015A',
9382 'Sc': '\u2ABC',
9383 'Scaron': '\u0160',
9384 'Scedil': '\u015E',
9385 'Scirc': '\u015C',
9386 'Scy': '\u0421',
9387 'Sfr': '\uD835\uDD16',
9388 'ShortUpArrow': '\u2191',
9389 'UpArrow': '\u2191',
9390 'uarr': '\u2191',
9391 'uparrow': '\u2191',
9392 'Sigma': '\u03A3',
9393 'SmallCircle': '\u2218',
9394 'compfn': '\u2218',
9395 'Sopf': '\uD835\uDD4A',
9396 'Sqrt': '\u221A',
9397 'radic': '\u221A',
9398 'Square': '\u25A1',
9399 'squ': '\u25A1',
9400 'square': '\u25A1',
9401 'SquareIntersection': '\u2293',
9402 'sqcap': '\u2293',
9403 'SquareSubset': '\u228F',
9404 'sqsub': '\u228F',
9405 'sqsubset': '\u228F',
9406 'SquareSubsetEqual': '\u2291',
9407 'sqsube': '\u2291',
9408 'sqsubseteq': '\u2291',
9409 'SquareSuperset': '\u2290',
9410 'sqsup': '\u2290',
9411 'sqsupset': '\u2290',
9412 'SquareSupersetEqual': '\u2292',
9413 'sqsupe': '\u2292',
9414 'sqsupseteq': '\u2292',
9415 'SquareUnion': '\u2294',
9416 'sqcup': '\u2294',
9417 'Sscr': '\uD835\uDCAE',
9418 'Star': '\u22C6',
9419 'sstarf': '\u22C6',
9420 'Sub': '\u22D0',
9421 'Subset': '\u22D0',
9422 'SubsetEqual': '\u2286',
9423 'sube': '\u2286',
9424 'subseteq': '\u2286',
9425 'Succeeds': '\u227B',
9426 'sc': '\u227B',
9427 'succ': '\u227B',
9428 'SucceedsEqual': '\u2AB0',
9429 'sce': '\u2AB0',
9430 'succeq': '\u2AB0',
9431 'SucceedsSlantEqual': '\u227D',
9432 'sccue': '\u227D',
9433 'succcurlyeq': '\u227D',
9434 'SucceedsTilde': '\u227F',
9435 'scsim': '\u227F',
9436 'succsim': '\u227F',
9437 'Sum': '\u2211',
9438 'sum': '\u2211',
9439 'Sup': '\u22D1',
9440 'Supset': '\u22D1',
9441 'Superset': '\u2283',
9442 'sup': '\u2283',
9443 'supset': '\u2283',
9444 'SupersetEqual': '\u2287',
9445 'supe': '\u2287',
9446 'supseteq': '\u2287',
9447 'THORN': '\u00DE',
9448 'TRADE': '\u2122',
9449 'trade': '\u2122',
9450 'TSHcy': '\u040B',
9451 'TScy': '\u0426',
9452 'Tab': '\u0009',
9453 'Tau': '\u03A4',
9454 'Tcaron': '\u0164',
9455 'Tcedil': '\u0162',
9456 'Tcy': '\u0422',
9457 'Tfr': '\uD835\uDD17',
9458 'Therefore': '\u2234',
9459 'there4': '\u2234',
9460 'therefore': '\u2234',
9461 'Theta': '\u0398',
9462 'ThickSpace': '\u205F\u200A',
9463 'ThinSpace': '\u2009',
9464 'thinsp': '\u2009',
9465 'Tilde': '\u223C',
9466 'sim': '\u223C',
9467 'thicksim': '\u223C',
9468 'thksim': '\u223C',
9469 'TildeEqual': '\u2243',
9470 'sime': '\u2243',
9471 'simeq': '\u2243',
9472 'TildeFullEqual': '\u2245',
9473 'cong': '\u2245',
9474 'TildeTilde': '\u2248',
9475 'ap': '\u2248',
9476 'approx': '\u2248',
9477 'asymp': '\u2248',
9478 'thickapprox': '\u2248',
9479 'thkap': '\u2248',
9480 'Topf': '\uD835\uDD4B',
9481 'TripleDot': '\u20DB',
9482 'tdot': '\u20DB',
9483 'Tscr': '\uD835\uDCAF',
9484 'Tstrok': '\u0166',
9485 'Uacute': '\u00DA',
9486 'Uarr': '\u219F',
9487 'Uarrocir': '\u2949',
9488 'Ubrcy': '\u040E',
9489 'Ubreve': '\u016C',
9490 'Ucirc': '\u00DB',
9491 'Ucy': '\u0423',
9492 'Udblac': '\u0170',
9493 'Ufr': '\uD835\uDD18',
9494 'Ugrave': '\u00D9',
9495 'Umacr': '\u016A',
9496 'UnderBar': '\u005F',
9497 'lowbar': '\u005F',
9498 'UnderBrace': '\u23DF',
9499 'UnderBracket': '\u23B5',
9500 'bbrk': '\u23B5',
9501 'UnderParenthesis': '\u23DD',
9502 'Union': '\u22C3',
9503 'bigcup': '\u22C3',
9504 'xcup': '\u22C3',
9505 'UnionPlus': '\u228E',
9506 'uplus': '\u228E',
9507 'Uogon': '\u0172',
9508 'Uopf': '\uD835\uDD4C',
9509 'UpArrowBar': '\u2912',
9510 'UpArrowDownArrow': '\u21C5',
9511 'udarr': '\u21C5',
9512 'UpDownArrow': '\u2195',
9513 'updownarrow': '\u2195',
9514 'varr': '\u2195',
9515 'UpEquilibrium': '\u296E',
9516 'udhar': '\u296E',
9517 'UpTee': '\u22A5',
9518 'bot': '\u22A5',
9519 'bottom': '\u22A5',
9520 'perp': '\u22A5',
9521 'UpTeeArrow': '\u21A5',
9522 'mapstoup': '\u21A5',
9523 'UpperLeftArrow': '\u2196',
9524 'nwarr': '\u2196',
9525 'nwarrow': '\u2196',
9526 'UpperRightArrow': '\u2197',
9527 'nearr': '\u2197',
9528 'nearrow': '\u2197',
9529 'Upsi': '\u03D2',
9530 'upsih': '\u03D2',
9531 'Upsilon': '\u03A5',
9532 'Uring': '\u016E',
9533 'Uscr': '\uD835\uDCB0',
9534 'Utilde': '\u0168',
9535 'Uuml': '\u00DC',
9536 'VDash': '\u22AB',
9537 'Vbar': '\u2AEB',
9538 'Vcy': '\u0412',
9539 'Vdash': '\u22A9',
9540 'Vdashl': '\u2AE6',
9541 'Vee': '\u22C1',
9542 'bigvee': '\u22C1',
9543 'xvee': '\u22C1',
9544 'Verbar': '\u2016',
9545 'Vert': '\u2016',
9546 'VerticalBar': '\u2223',
9547 'mid': '\u2223',
9548 'shortmid': '\u2223',
9549 'smid': '\u2223',
9550 'VerticalLine': '\u007C',
9551 'verbar': '\u007C',
9552 'vert': '\u007C',
9553 'VerticalSeparator': '\u2758',
9554 'VerticalTilde': '\u2240',
9555 'wr': '\u2240',
9556 'wreath': '\u2240',
9557 'VeryThinSpace': '\u200A',
9558 'hairsp': '\u200A',
9559 'Vfr': '\uD835\uDD19',
9560 'Vopf': '\uD835\uDD4D',
9561 'Vscr': '\uD835\uDCB1',
9562 'Vvdash': '\u22AA',
9563 'Wcirc': '\u0174',
9564 'Wedge': '\u22C0',
9565 'bigwedge': '\u22C0',
9566 'xwedge': '\u22C0',
9567 'Wfr': '\uD835\uDD1A',
9568 'Wopf': '\uD835\uDD4E',
9569 'Wscr': '\uD835\uDCB2',
9570 'Xfr': '\uD835\uDD1B',
9571 'Xi': '\u039E',
9572 'Xopf': '\uD835\uDD4F',
9573 'Xscr': '\uD835\uDCB3',
9574 'YAcy': '\u042F',
9575 'YIcy': '\u0407',
9576 'YUcy': '\u042E',
9577 'Yacute': '\u00DD',
9578 'Ycirc': '\u0176',
9579 'Ycy': '\u042B',
9580 'Yfr': '\uD835\uDD1C',
9581 'Yopf': '\uD835\uDD50',
9582 'Yscr': '\uD835\uDCB4',
9583 'Yuml': '\u0178',
9584 'ZHcy': '\u0416',
9585 'Zacute': '\u0179',
9586 'Zcaron': '\u017D',
9587 'Zcy': '\u0417',
9588 'Zdot': '\u017B',
9589 'Zeta': '\u0396',
9590 'Zfr': '\u2128',
9591 'zeetrf': '\u2128',
9592 'Zopf': '\u2124',
9593 'integers': '\u2124',
9594 'Zscr': '\uD835\uDCB5',
9595 'aacute': '\u00E1',
9596 'abreve': '\u0103',
9597 'ac': '\u223E',
9598 'mstpos': '\u223E',
9599 'acE': '\u223E\u0333',
9600 'acd': '\u223F',
9601 'acirc': '\u00E2',
9602 'acy': '\u0430',
9603 'aelig': '\u00E6',
9604 'afr': '\uD835\uDD1E',
9605 'agrave': '\u00E0',
9606 'alefsym': '\u2135',
9607 'aleph': '\u2135',
9608 'alpha': '\u03B1',
9609 'amacr': '\u0101',
9610 'amalg': '\u2A3F',
9611 'and': '\u2227',
9612 'wedge': '\u2227',
9613 'andand': '\u2A55',
9614 'andd': '\u2A5C',
9615 'andslope': '\u2A58',
9616 'andv': '\u2A5A',
9617 'ang': '\u2220',
9618 'angle': '\u2220',
9619 'ange': '\u29A4',
9620 'angmsd': '\u2221',
9621 'measuredangle': '\u2221',
9622 'angmsdaa': '\u29A8',
9623 'angmsdab': '\u29A9',
9624 'angmsdac': '\u29AA',
9625 'angmsdad': '\u29AB',
9626 'angmsdae': '\u29AC',
9627 'angmsdaf': '\u29AD',
9628 'angmsdag': '\u29AE',
9629 'angmsdah': '\u29AF',
9630 'angrt': '\u221F',
9631 'angrtvb': '\u22BE',
9632 'angrtvbd': '\u299D',
9633 'angsph': '\u2222',
9634 'angzarr': '\u237C',
9635 'aogon': '\u0105',
9636 'aopf': '\uD835\uDD52',
9637 'apE': '\u2A70',
9638 'apacir': '\u2A6F',
9639 'ape': '\u224A',
9640 'approxeq': '\u224A',
9641 'apid': '\u224B',
9642 'apos': '\u0027',
9643 'aring': '\u00E5',
9644 'ascr': '\uD835\uDCB6',
9645 'ast': '\u002A',
9646 'midast': '\u002A',
9647 'atilde': '\u00E3',
9648 'auml': '\u00E4',
9649 'awint': '\u2A11',
9650 'bNot': '\u2AED',
9651 'backcong': '\u224C',
9652 'bcong': '\u224C',
9653 'backepsilon': '\u03F6',
9654 'bepsi': '\u03F6',
9655 'backprime': '\u2035',
9656 'bprime': '\u2035',
9657 'backsim': '\u223D',
9658 'bsim': '\u223D',
9659 'backsimeq': '\u22CD',
9660 'bsime': '\u22CD',
9661 'barvee': '\u22BD',
9662 'barwed': '\u2305',
9663 'barwedge': '\u2305',
9664 'bbrktbrk': '\u23B6',
9665 'bcy': '\u0431',
9666 'bdquo': '\u201E',
9667 'ldquor': '\u201E',
9668 'bemptyv': '\u29B0',
9669 'beta': '\u03B2',
9670 'beth': '\u2136',
9671 'between': '\u226C',
9672 'twixt': '\u226C',
9673 'bfr': '\uD835\uDD1F',
9674 'bigcirc': '\u25EF',
9675 'xcirc': '\u25EF',
9676 'bigodot': '\u2A00',
9677 'xodot': '\u2A00',
9678 'bigoplus': '\u2A01',
9679 'xoplus': '\u2A01',
9680 'bigotimes': '\u2A02',
9681 'xotime': '\u2A02',
9682 'bigsqcup': '\u2A06',
9683 'xsqcup': '\u2A06',
9684 'bigstar': '\u2605',
9685 'starf': '\u2605',
9686 'bigtriangledown': '\u25BD',
9687 'xdtri': '\u25BD',
9688 'bigtriangleup': '\u25B3',
9689 'xutri': '\u25B3',
9690 'biguplus': '\u2A04',
9691 'xuplus': '\u2A04',
9692 'bkarow': '\u290D',
9693 'rbarr': '\u290D',
9694 'blacklozenge': '\u29EB',
9695 'lozf': '\u29EB',
9696 'blacktriangle': '\u25B4',
9697 'utrif': '\u25B4',
9698 'blacktriangledown': '\u25BE',
9699 'dtrif': '\u25BE',
9700 'blacktriangleleft': '\u25C2',
9701 'ltrif': '\u25C2',
9702 'blacktriangleright': '\u25B8',
9703 'rtrif': '\u25B8',
9704 'blank': '\u2423',
9705 'blk12': '\u2592',
9706 'blk14': '\u2591',
9707 'blk34': '\u2593',
9708 'block': '\u2588',
9709 'bne': '\u003D\u20E5',
9710 'bnequiv': '\u2261\u20E5',
9711 'bnot': '\u2310',
9712 'bopf': '\uD835\uDD53',
9713 'bowtie': '\u22C8',
9714 'boxDL': '\u2557',
9715 'boxDR': '\u2554',
9716 'boxDl': '\u2556',
9717 'boxDr': '\u2553',
9718 'boxH': '\u2550',
9719 'boxHD': '\u2566',
9720 'boxHU': '\u2569',
9721 'boxHd': '\u2564',
9722 'boxHu': '\u2567',
9723 'boxUL': '\u255D',
9724 'boxUR': '\u255A',
9725 'boxUl': '\u255C',
9726 'boxUr': '\u2559',
9727 'boxV': '\u2551',
9728 'boxVH': '\u256C',
9729 'boxVL': '\u2563',
9730 'boxVR': '\u2560',
9731 'boxVh': '\u256B',
9732 'boxVl': '\u2562',
9733 'boxVr': '\u255F',
9734 'boxbox': '\u29C9',
9735 'boxdL': '\u2555',
9736 'boxdR': '\u2552',
9737 'boxdl': '\u2510',
9738 'boxdr': '\u250C',
9739 'boxhD': '\u2565',
9740 'boxhU': '\u2568',
9741 'boxhd': '\u252C',
9742 'boxhu': '\u2534',
9743 'boxminus': '\u229F',
9744 'minusb': '\u229F',
9745 'boxplus': '\u229E',
9746 'plusb': '\u229E',
9747 'boxtimes': '\u22A0',
9748 'timesb': '\u22A0',
9749 'boxuL': '\u255B',
9750 'boxuR': '\u2558',
9751 'boxul': '\u2518',
9752 'boxur': '\u2514',
9753 'boxv': '\u2502',
9754 'boxvH': '\u256A',
9755 'boxvL': '\u2561',
9756 'boxvR': '\u255E',
9757 'boxvh': '\u253C',
9758 'boxvl': '\u2524',
9759 'boxvr': '\u251C',
9760 'brvbar': '\u00A6',
9761 'bscr': '\uD835\uDCB7',
9762 'bsemi': '\u204F',
9763 'bsol': '\u005C',
9764 'bsolb': '\u29C5',
9765 'bsolhsub': '\u27C8',
9766 'bull': '\u2022',
9767 'bullet': '\u2022',
9768 'bumpE': '\u2AAE',
9769 'cacute': '\u0107',
9770 'cap': '\u2229',
9771 'capand': '\u2A44',
9772 'capbrcup': '\u2A49',
9773 'capcap': '\u2A4B',
9774 'capcup': '\u2A47',
9775 'capdot': '\u2A40',
9776 'caps': '\u2229\uFE00',
9777 'caret': '\u2041',
9778 'ccaps': '\u2A4D',
9779 'ccaron': '\u010D',
9780 'ccedil': '\u00E7',
9781 'ccirc': '\u0109',
9782 'ccups': '\u2A4C',
9783 'ccupssm': '\u2A50',
9784 'cdot': '\u010B',
9785 'cemptyv': '\u29B2',
9786 'cent': '\u00A2',
9787 'cfr': '\uD835\uDD20',
9788 'chcy': '\u0447',
9789 'check': '\u2713',
9790 'checkmark': '\u2713',
9791 'chi': '\u03C7',
9792 'cir': '\u25CB',
9793 'cirE': '\u29C3',
9794 'circ': '\u02C6',
9795 'circeq': '\u2257',
9796 'cire': '\u2257',
9797 'circlearrowleft': '\u21BA',
9798 'olarr': '\u21BA',
9799 'circlearrowright': '\u21BB',
9800 'orarr': '\u21BB',
9801 'circledS': '\u24C8',
9802 'oS': '\u24C8',
9803 'circledast': '\u229B',
9804 'oast': '\u229B',
9805 'circledcirc': '\u229A',
9806 'ocir': '\u229A',
9807 'circleddash': '\u229D',
9808 'odash': '\u229D',
9809 'cirfnint': '\u2A10',
9810 'cirmid': '\u2AEF',
9811 'cirscir': '\u29C2',
9812 'clubs': '\u2663',
9813 'clubsuit': '\u2663',
9814 'colon': '\u003A',
9815 'comma': '\u002C',
9816 'commat': '\u0040',
9817 'comp': '\u2201',
9818 'complement': '\u2201',
9819 'congdot': '\u2A6D',
9820 'copf': '\uD835\uDD54',
9821 'copysr': '\u2117',
9822 'crarr': '\u21B5',
9823 'cross': '\u2717',
9824 'cscr': '\uD835\uDCB8',
9825 'csub': '\u2ACF',
9826 'csube': '\u2AD1',
9827 'csup': '\u2AD0',
9828 'csupe': '\u2AD2',
9829 'ctdot': '\u22EF',
9830 'cudarrl': '\u2938',
9831 'cudarrr': '\u2935',
9832 'cuepr': '\u22DE',
9833 'curlyeqprec': '\u22DE',
9834 'cuesc': '\u22DF',
9835 'curlyeqsucc': '\u22DF',
9836 'cularr': '\u21B6',
9837 'curvearrowleft': '\u21B6',
9838 'cularrp': '\u293D',
9839 'cup': '\u222A',
9840 'cupbrcap': '\u2A48',
9841 'cupcap': '\u2A46',
9842 'cupcup': '\u2A4A',
9843 'cupdot': '\u228D',
9844 'cupor': '\u2A45',
9845 'cups': '\u222A\uFE00',
9846 'curarr': '\u21B7',
9847 'curvearrowright': '\u21B7',
9848 'curarrm': '\u293C',
9849 'curlyvee': '\u22CE',
9850 'cuvee': '\u22CE',
9851 'curlywedge': '\u22CF',
9852 'cuwed': '\u22CF',
9853 'curren': '\u00A4',
9854 'cwint': '\u2231',
9855 'cylcty': '\u232D',
9856 'dHar': '\u2965',
9857 'dagger': '\u2020',
9858 'daleth': '\u2138',
9859 'dash': '\u2010',
9860 'hyphen': '\u2010',
9861 'dbkarow': '\u290F',
9862 'rBarr': '\u290F',
9863 'dcaron': '\u010F',
9864 'dcy': '\u0434',
9865 'ddarr': '\u21CA',
9866 'downdownarrows': '\u21CA',
9867 'ddotseq': '\u2A77',
9868 'eDDot': '\u2A77',
9869 'deg': '\u00B0',
9870 'delta': '\u03B4',
9871 'demptyv': '\u29B1',
9872 'dfisht': '\u297F',
9873 'dfr': '\uD835\uDD21',
9874 'diamondsuit': '\u2666',
9875 'diams': '\u2666',
9876 'digamma': '\u03DD',
9877 'gammad': '\u03DD',
9878 'disin': '\u22F2',
9879 'div': '\u00F7',
9880 'divide': '\u00F7',
9881 'divideontimes': '\u22C7',
9882 'divonx': '\u22C7',
9883 'djcy': '\u0452',
9884 'dlcorn': '\u231E',
9885 'llcorner': '\u231E',
9886 'dlcrop': '\u230D',
9887 'dollar': '\u0024',
9888 'dopf': '\uD835\uDD55',
9889 'doteqdot': '\u2251',
9890 'eDot': '\u2251',
9891 'dotminus': '\u2238',
9892 'minusd': '\u2238',
9893 'dotplus': '\u2214',
9894 'plusdo': '\u2214',
9895 'dotsquare': '\u22A1',
9896 'sdotb': '\u22A1',
9897 'drcorn': '\u231F',
9898 'lrcorner': '\u231F',
9899 'drcrop': '\u230C',
9900 'dscr': '\uD835\uDCB9',
9901 'dscy': '\u0455',
9902 'dsol': '\u29F6',
9903 'dstrok': '\u0111',
9904 'dtdot': '\u22F1',
9905 'dtri': '\u25BF',
9906 'triangledown': '\u25BF',
9907 'dwangle': '\u29A6',
9908 'dzcy': '\u045F',
9909 'dzigrarr': '\u27FF',
9910 'eacute': '\u00E9',
9911 'easter': '\u2A6E',
9912 'ecaron': '\u011B',
9913 'ecir': '\u2256',
9914 'eqcirc': '\u2256',
9915 'ecirc': '\u00EA',
9916 'ecolon': '\u2255',
9917 'eqcolon': '\u2255',
9918 'ecy': '\u044D',
9919 'edot': '\u0117',
9920 'efDot': '\u2252',
9921 'fallingdotseq': '\u2252',
9922 'efr': '\uD835\uDD22',
9923 'eg': '\u2A9A',
9924 'egrave': '\u00E8',
9925 'egs': '\u2A96',
9926 'eqslantgtr': '\u2A96',
9927 'egsdot': '\u2A98',
9928 'el': '\u2A99',
9929 'elinters': '\u23E7',
9930 'ell': '\u2113',
9931 'els': '\u2A95',
9932 'eqslantless': '\u2A95',
9933 'elsdot': '\u2A97',
9934 'emacr': '\u0113',
9935 'empty': '\u2205',
9936 'emptyset': '\u2205',
9937 'emptyv': '\u2205',
9938 'varnothing': '\u2205',
9939 'emsp13': '\u2004',
9940 'emsp14': '\u2005',
9941 'emsp': '\u2003',
9942 'eng': '\u014B',
9943 'ensp': '\u2002',
9944 'eogon': '\u0119',
9945 'eopf': '\uD835\uDD56',
9946 'epar': '\u22D5',
9947 'eparsl': '\u29E3',
9948 'eplus': '\u2A71',
9949 'epsi': '\u03B5',
9950 'epsilon': '\u03B5',
9951 'epsiv': '\u03F5',
9952 'straightepsilon': '\u03F5',
9953 'varepsilon': '\u03F5',
9954 'equals': '\u003D',
9955 'equest': '\u225F',
9956 'questeq': '\u225F',
9957 'equivDD': '\u2A78',
9958 'eqvparsl': '\u29E5',
9959 'erDot': '\u2253',
9960 'risingdotseq': '\u2253',
9961 'erarr': '\u2971',
9962 'escr': '\u212F',
9963 'eta': '\u03B7',
9964 'eth': '\u00F0',
9965 'euml': '\u00EB',
9966 'euro': '\u20AC',
9967 'excl': '\u0021',
9968 'fcy': '\u0444',
9969 'female': '\u2640',
9970 'ffilig': '\uFB03',
9971 'fflig': '\uFB00',
9972 'ffllig': '\uFB04',
9973 'ffr': '\uD835\uDD23',
9974 'filig': '\uFB01',
9975 'fjlig': '\u0066\u006A',
9976 'flat': '\u266D',
9977 'fllig': '\uFB02',
9978 'fltns': '\u25B1',
9979 'fnof': '\u0192',
9980 'fopf': '\uD835\uDD57',
9981 'fork': '\u22D4',
9982 'pitchfork': '\u22D4',
9983 'forkv': '\u2AD9',
9984 'fpartint': '\u2A0D',
9985 'frac12': '\u00BD',
9986 'half': '\u00BD',
9987 'frac13': '\u2153',
9988 'frac14': '\u00BC',
9989 'frac15': '\u2155',
9990 'frac16': '\u2159',
9991 'frac18': '\u215B',
9992 'frac23': '\u2154',
9993 'frac25': '\u2156',
9994 'frac34': '\u00BE',
9995 'frac35': '\u2157',
9996 'frac38': '\u215C',
9997 'frac45': '\u2158',
9998 'frac56': '\u215A',
9999 'frac58': '\u215D',
10000 'frac78': '\u215E',
10001 'frasl': '\u2044',
10002 'frown': '\u2322',
10003 'sfrown': '\u2322',
10004 'fscr': '\uD835\uDCBB',
10005 'gEl': '\u2A8C',
10006 'gtreqqless': '\u2A8C',
10007 'gacute': '\u01F5',
10008 'gamma': '\u03B3',
10009 'gap': '\u2A86',
10010 'gtrapprox': '\u2A86',
10011 'gbreve': '\u011F',
10012 'gcirc': '\u011D',
10013 'gcy': '\u0433',
10014 'gdot': '\u0121',
10015 'gescc': '\u2AA9',
10016 'gesdot': '\u2A80',
10017 'gesdoto': '\u2A82',
10018 'gesdotol': '\u2A84',
10019 'gesl': '\u22DB\uFE00',
10020 'gesles': '\u2A94',
10021 'gfr': '\uD835\uDD24',
10022 'gimel': '\u2137',
10023 'gjcy': '\u0453',
10024 'glE': '\u2A92',
10025 'gla': '\u2AA5',
10026 'glj': '\u2AA4',
10027 'gnE': '\u2269',
10028 'gneqq': '\u2269',
10029 'gnap': '\u2A8A',
10030 'gnapprox': '\u2A8A',
10031 'gne': '\u2A88',
10032 'gneq': '\u2A88',
10033 'gnsim': '\u22E7',
10034 'gopf': '\uD835\uDD58',
10035 'gscr': '\u210A',
10036 'gsime': '\u2A8E',
10037 'gsiml': '\u2A90',
10038 'gtcc': '\u2AA7',
10039 'gtcir': '\u2A7A',
10040 'gtdot': '\u22D7',
10041 'gtrdot': '\u22D7',
10042 'gtlPar': '\u2995',
10043 'gtquest': '\u2A7C',
10044 'gtrarr': '\u2978',
10045 'gvertneqq': '\u2269\uFE00',
10046 'gvnE': '\u2269\uFE00',
10047 'hardcy': '\u044A',
10048 'harrcir': '\u2948',
10049 'harrw': '\u21AD',
10050 'leftrightsquigarrow': '\u21AD',
10051 'hbar': '\u210F',
10052 'hslash': '\u210F',
10053 'planck': '\u210F',
10054 'plankv': '\u210F',
10055 'hcirc': '\u0125',
10056 'hearts': '\u2665',
10057 'heartsuit': '\u2665',
10058 'hellip': '\u2026',
10059 'mldr': '\u2026',
10060 'hercon': '\u22B9',
10061 'hfr': '\uD835\uDD25',
10062 'hksearow': '\u2925',
10063 'searhk': '\u2925',
10064 'hkswarow': '\u2926',
10065 'swarhk': '\u2926',
10066 'hoarr': '\u21FF',
10067 'homtht': '\u223B',
10068 'hookleftarrow': '\u21A9',
10069 'larrhk': '\u21A9',
10070 'hookrightarrow': '\u21AA',
10071 'rarrhk': '\u21AA',
10072 'hopf': '\uD835\uDD59',
10073 'horbar': '\u2015',
10074 'hscr': '\uD835\uDCBD',
10075 'hstrok': '\u0127',
10076 'hybull': '\u2043',
10077 'iacute': '\u00ED',
10078 'icirc': '\u00EE',
10079 'icy': '\u0438',
10080 'iecy': '\u0435',
10081 'iexcl': '\u00A1',
10082 'ifr': '\uD835\uDD26',
10083 'igrave': '\u00EC',
10084 'iiiint': '\u2A0C',
10085 'qint': '\u2A0C',
10086 'iiint': '\u222D',
10087 'tint': '\u222D',
10088 'iinfin': '\u29DC',
10089 'iiota': '\u2129',
10090 'ijlig': '\u0133',
10091 'imacr': '\u012B',
10092 'imath': '\u0131',
10093 'inodot': '\u0131',
10094 'imof': '\u22B7',
10095 'imped': '\u01B5',
10096 'incare': '\u2105',
10097 'infin': '\u221E',
10098 'infintie': '\u29DD',
10099 'intcal': '\u22BA',
10100 'intercal': '\u22BA',
10101 'intlarhk': '\u2A17',
10102 'intprod': '\u2A3C',
10103 'iprod': '\u2A3C',
10104 'iocy': '\u0451',
10105 'iogon': '\u012F',
10106 'iopf': '\uD835\uDD5A',
10107 'iota': '\u03B9',
10108 'iquest': '\u00BF',
10109 'iscr': '\uD835\uDCBE',
10110 'isinE': '\u22F9',
10111 'isindot': '\u22F5',
10112 'isins': '\u22F4',
10113 'isinsv': '\u22F3',
10114 'itilde': '\u0129',
10115 'iukcy': '\u0456',
10116 'iuml': '\u00EF',
10117 'jcirc': '\u0135',
10118 'jcy': '\u0439',
10119 'jfr': '\uD835\uDD27',
10120 'jmath': '\u0237',
10121 'jopf': '\uD835\uDD5B',
10122 'jscr': '\uD835\uDCBF',
10123 'jsercy': '\u0458',
10124 'jukcy': '\u0454',
10125 'kappa': '\u03BA',
10126 'kappav': '\u03F0',
10127 'varkappa': '\u03F0',
10128 'kcedil': '\u0137',
10129 'kcy': '\u043A',
10130 'kfr': '\uD835\uDD28',
10131 'kgreen': '\u0138',
10132 'khcy': '\u0445',
10133 'kjcy': '\u045C',
10134 'kopf': '\uD835\uDD5C',
10135 'kscr': '\uD835\uDCC0',
10136 'lAtail': '\u291B',
10137 'lBarr': '\u290E',
10138 'lEg': '\u2A8B',
10139 'lesseqqgtr': '\u2A8B',
10140 'lHar': '\u2962',
10141 'lacute': '\u013A',
10142 'laemptyv': '\u29B4',
10143 'lambda': '\u03BB',
10144 'langd': '\u2991',
10145 'lap': '\u2A85',
10146 'lessapprox': '\u2A85',
10147 'laquo': '\u00AB',
10148 'larrbfs': '\u291F',
10149 'larrfs': '\u291D',
10150 'larrlp': '\u21AB',
10151 'looparrowleft': '\u21AB',
10152 'larrpl': '\u2939',
10153 'larrsim': '\u2973',
10154 'larrtl': '\u21A2',
10155 'leftarrowtail': '\u21A2',
10156 'lat': '\u2AAB',
10157 'latail': '\u2919',
10158 'late': '\u2AAD',
10159 'lates': '\u2AAD\uFE00',
10160 'lbarr': '\u290C',
10161 'lbbrk': '\u2772',
10162 'lbrace': '\u007B',
10163 'lcub': '\u007B',
10164 'lbrack': '\u005B',
10165 'lsqb': '\u005B',
10166 'lbrke': '\u298B',
10167 'lbrksld': '\u298F',
10168 'lbrkslu': '\u298D',
10169 'lcaron': '\u013E',
10170 'lcedil': '\u013C',
10171 'lcy': '\u043B',
10172 'ldca': '\u2936',
10173 'ldrdhar': '\u2967',
10174 'ldrushar': '\u294B',
10175 'ldsh': '\u21B2',
10176 'le': '\u2264',
10177 'leq': '\u2264',
10178 'leftleftarrows': '\u21C7',
10179 'llarr': '\u21C7',
10180 'leftthreetimes': '\u22CB',
10181 'lthree': '\u22CB',
10182 'lescc': '\u2AA8',
10183 'lesdot': '\u2A7F',
10184 'lesdoto': '\u2A81',
10185 'lesdotor': '\u2A83',
10186 'lesg': '\u22DA\uFE00',
10187 'lesges': '\u2A93',
10188 'lessdot': '\u22D6',
10189 'ltdot': '\u22D6',
10190 'lfisht': '\u297C',
10191 'lfr': '\uD835\uDD29',
10192 'lgE': '\u2A91',
10193 'lharul': '\u296A',
10194 'lhblk': '\u2584',
10195 'ljcy': '\u0459',
10196 'llhard': '\u296B',
10197 'lltri': '\u25FA',
10198 'lmidot': '\u0140',
10199 'lmoust': '\u23B0',
10200 'lmoustache': '\u23B0',
10201 'lnE': '\u2268',
10202 'lneqq': '\u2268',
10203 'lnap': '\u2A89',
10204 'lnapprox': '\u2A89',
10205 'lne': '\u2A87',
10206 'lneq': '\u2A87',
10207 'lnsim': '\u22E6',
10208 'loang': '\u27EC',
10209 'loarr': '\u21FD',
10210 'longmapsto': '\u27FC',
10211 'xmap': '\u27FC',
10212 'looparrowright': '\u21AC',
10213 'rarrlp': '\u21AC',
10214 'lopar': '\u2985',
10215 'lopf': '\uD835\uDD5D',
10216 'loplus': '\u2A2D',
10217 'lotimes': '\u2A34',
10218 'lowast': '\u2217',
10219 'loz': '\u25CA',
10220 'lozenge': '\u25CA',
10221 'lpar': '\u0028',
10222 'lparlt': '\u2993',
10223 'lrhard': '\u296D',
10224 'lrm': '\u200E',
10225 'lrtri': '\u22BF',
10226 'lsaquo': '\u2039',
10227 'lscr': '\uD835\uDCC1',
10228 'lsime': '\u2A8D',
10229 'lsimg': '\u2A8F',
10230 'lsquor': '\u201A',
10231 'sbquo': '\u201A',
10232 'lstrok': '\u0142',
10233 'ltcc': '\u2AA6',
10234 'ltcir': '\u2A79',
10235 'ltimes': '\u22C9',
10236 'ltlarr': '\u2976',
10237 'ltquest': '\u2A7B',
10238 'ltrPar': '\u2996',
10239 'ltri': '\u25C3',
10240 'triangleleft': '\u25C3',
10241 'lurdshar': '\u294A',
10242 'luruhar': '\u2966',
10243 'lvertneqq': '\u2268\uFE00',
10244 'lvnE': '\u2268\uFE00',
10245 'mDDot': '\u223A',
10246 'macr': '\u00AF',
10247 'strns': '\u00AF',
10248 'male': '\u2642',
10249 'malt': '\u2720',
10250 'maltese': '\u2720',
10251 'marker': '\u25AE',
10252 'mcomma': '\u2A29',
10253 'mcy': '\u043C',
10254 'mdash': '\u2014',
10255 'mfr': '\uD835\uDD2A',
10256 'mho': '\u2127',
10257 'micro': '\u00B5',
10258 'midcir': '\u2AF0',
10259 'minus': '\u2212',
10260 'minusdu': '\u2A2A',
10261 'mlcp': '\u2ADB',
10262 'models': '\u22A7',
10263 'mopf': '\uD835\uDD5E',
10264 'mscr': '\uD835\uDCC2',
10265 'mu': '\u03BC',
10266 'multimap': '\u22B8',
10267 'mumap': '\u22B8',
10268 'nGg': '\u22D9\u0338',
10269 'nGt': '\u226B\u20D2',
10270 'nLeftarrow': '\u21CD',
10271 'nlArr': '\u21CD',
10272 'nLeftrightarrow': '\u21CE',
10273 'nhArr': '\u21CE',
10274 'nLl': '\u22D8\u0338',
10275 'nLt': '\u226A\u20D2',
10276 'nRightarrow': '\u21CF',
10277 'nrArr': '\u21CF',
10278 'nVDash': '\u22AF',
10279 'nVdash': '\u22AE',
10280 'nacute': '\u0144',
10281 'nang': '\u2220\u20D2',
10282 'napE': '\u2A70\u0338',
10283 'napid': '\u224B\u0338',
10284 'napos': '\u0149',
10285 'natur': '\u266E',
10286 'natural': '\u266E',
10287 'ncap': '\u2A43',
10288 'ncaron': '\u0148',
10289 'ncedil': '\u0146',
10290 'ncongdot': '\u2A6D\u0338',
10291 'ncup': '\u2A42',
10292 'ncy': '\u043D',
10293 'ndash': '\u2013',
10294 'neArr': '\u21D7',
10295 'nearhk': '\u2924',
10296 'nedot': '\u2250\u0338',
10297 'nesear': '\u2928',
10298 'toea': '\u2928',
10299 'nfr': '\uD835\uDD2B',
10300 'nharr': '\u21AE',
10301 'nleftrightarrow': '\u21AE',
10302 'nhpar': '\u2AF2',
10303 'nis': '\u22FC',
10304 'nisd': '\u22FA',
10305 'njcy': '\u045A',
10306 'nlE': '\u2266\u0338',
10307 'nleqq': '\u2266\u0338',
10308 'nlarr': '\u219A',
10309 'nleftarrow': '\u219A',
10310 'nldr': '\u2025',
10311 'nopf': '\uD835\uDD5F',
10312 'not': '\u00AC',
10313 'notinE': '\u22F9\u0338',
10314 'notindot': '\u22F5\u0338',
10315 'notinvb': '\u22F7',
10316 'notinvc': '\u22F6',
10317 'notnivb': '\u22FE',
10318 'notnivc': '\u22FD',
10319 'nparsl': '\u2AFD\u20E5',
10320 'npart': '\u2202\u0338',
10321 'npolint': '\u2A14',
10322 'nrarr': '\u219B',
10323 'nrightarrow': '\u219B',
10324 'nrarrc': '\u2933\u0338',
10325 'nrarrw': '\u219D\u0338',
10326 'nscr': '\uD835\uDCC3',
10327 'nsub': '\u2284',
10328 'nsubE': '\u2AC5\u0338',
10329 'nsubseteqq': '\u2AC5\u0338',
10330 'nsup': '\u2285',
10331 'nsupE': '\u2AC6\u0338',
10332 'nsupseteqq': '\u2AC6\u0338',
10333 'ntilde': '\u00F1',
10334 'nu': '\u03BD',
10335 'num': '\u0023',
10336 'numero': '\u2116',
10337 'numsp': '\u2007',
10338 'nvDash': '\u22AD',
10339 'nvHarr': '\u2904',
10340 'nvap': '\u224D\u20D2',
10341 'nvdash': '\u22AC',
10342 'nvge': '\u2265\u20D2',
10343 'nvgt': '\u003E\u20D2',
10344 'nvinfin': '\u29DE',
10345 'nvlArr': '\u2902',
10346 'nvle': '\u2264\u20D2',
10347 'nvlt': '\u003C\u20D2',
10348 'nvltrie': '\u22B4\u20D2',
10349 'nvrArr': '\u2903',
10350 'nvrtrie': '\u22B5\u20D2',
10351 'nvsim': '\u223C\u20D2',
10352 'nwArr': '\u21D6',
10353 'nwarhk': '\u2923',
10354 'nwnear': '\u2927',
10355 'oacute': '\u00F3',
10356 'ocirc': '\u00F4',
10357 'ocy': '\u043E',
10358 'odblac': '\u0151',
10359 'odiv': '\u2A38',
10360 'odsold': '\u29BC',
10361 'oelig': '\u0153',
10362 'ofcir': '\u29BF',
10363 'ofr': '\uD835\uDD2C',
10364 'ogon': '\u02DB',
10365 'ograve': '\u00F2',
10366 'ogt': '\u29C1',
10367 'ohbar': '\u29B5',
10368 'olcir': '\u29BE',
10369 'olcross': '\u29BB',
10370 'olt': '\u29C0',
10371 'omacr': '\u014D',
10372 'omega': '\u03C9',
10373 'omicron': '\u03BF',
10374 'omid': '\u29B6',
10375 'oopf': '\uD835\uDD60',
10376 'opar': '\u29B7',
10377 'operp': '\u29B9',
10378 'or': '\u2228',
10379 'vee': '\u2228',
10380 'ord': '\u2A5D',
10381 'order': '\u2134',
10382 'orderof': '\u2134',
10383 'oscr': '\u2134',
10384 'ordf': '\u00AA',
10385 'ordm': '\u00BA',
10386 'origof': '\u22B6',
10387 'oror': '\u2A56',
10388 'orslope': '\u2A57',
10389 'orv': '\u2A5B',
10390 'oslash': '\u00F8',
10391 'osol': '\u2298',
10392 'otilde': '\u00F5',
10393 'otimesas': '\u2A36',
10394 'ouml': '\u00F6',
10395 'ovbar': '\u233D',
10396 'para': '\u00B6',
10397 'parsim': '\u2AF3',
10398 'parsl': '\u2AFD',
10399 'pcy': '\u043F',
10400 'percnt': '\u0025',
10401 'period': '\u002E',
10402 'permil': '\u2030',
10403 'pertenk': '\u2031',
10404 'pfr': '\uD835\uDD2D',
10405 'phi': '\u03C6',
10406 'phiv': '\u03D5',
10407 'straightphi': '\u03D5',
10408 'varphi': '\u03D5',
10409 'phone': '\u260E',
10410 'pi': '\u03C0',
10411 'piv': '\u03D6',
10412 'varpi': '\u03D6',
10413 'planckh': '\u210E',
10414 'plus': '\u002B',
10415 'plusacir': '\u2A23',
10416 'pluscir': '\u2A22',
10417 'plusdu': '\u2A25',
10418 'pluse': '\u2A72',
10419 'plussim': '\u2A26',
10420 'plustwo': '\u2A27',
10421 'pointint': '\u2A15',
10422 'popf': '\uD835\uDD61',
10423 'pound': '\u00A3',
10424 'prE': '\u2AB3',
10425 'prap': '\u2AB7',
10426 'precapprox': '\u2AB7',
10427 'precnapprox': '\u2AB9',
10428 'prnap': '\u2AB9',
10429 'precneqq': '\u2AB5',
10430 'prnE': '\u2AB5',
10431 'precnsim': '\u22E8',
10432 'prnsim': '\u22E8',
10433 'prime': '\u2032',
10434 'profalar': '\u232E',
10435 'profline': '\u2312',
10436 'profsurf': '\u2313',
10437 'prurel': '\u22B0',
10438 'pscr': '\uD835\uDCC5',
10439 'psi': '\u03C8',
10440 'puncsp': '\u2008',
10441 'qfr': '\uD835\uDD2E',
10442 'qopf': '\uD835\uDD62',
10443 'qprime': '\u2057',
10444 'qscr': '\uD835\uDCC6',
10445 'quatint': '\u2A16',
10446 'quest': '\u003F',
10447 'rAtail': '\u291C',
10448 'rHar': '\u2964',
10449 'race': '\u223D\u0331',
10450 'racute': '\u0155',
10451 'raemptyv': '\u29B3',
10452 'rangd': '\u2992',
10453 'range': '\u29A5',
10454 'raquo': '\u00BB',
10455 'rarrap': '\u2975',
10456 'rarrbfs': '\u2920',
10457 'rarrc': '\u2933',
10458 'rarrfs': '\u291E',
10459 'rarrpl': '\u2945',
10460 'rarrsim': '\u2974',
10461 'rarrtl': '\u21A3',
10462 'rightarrowtail': '\u21A3',
10463 'rarrw': '\u219D',
10464 'rightsquigarrow': '\u219D',
10465 'ratail': '\u291A',
10466 'ratio': '\u2236',
10467 'rbbrk': '\u2773',
10468 'rbrace': '\u007D',
10469 'rcub': '\u007D',
10470 'rbrack': '\u005D',
10471 'rsqb': '\u005D',
10472 'rbrke': '\u298C',
10473 'rbrksld': '\u298E',
10474 'rbrkslu': '\u2990',
10475 'rcaron': '\u0159',
10476 'rcedil': '\u0157',
10477 'rcy': '\u0440',
10478 'rdca': '\u2937',
10479 'rdldhar': '\u2969',
10480 'rdsh': '\u21B3',
10481 'rect': '\u25AD',
10482 'rfisht': '\u297D',
10483 'rfr': '\uD835\uDD2F',
10484 'rharul': '\u296C',
10485 'rho': '\u03C1',
10486 'rhov': '\u03F1',
10487 'varrho': '\u03F1',
10488 'rightrightarrows': '\u21C9',
10489 'rrarr': '\u21C9',
10490 'rightthreetimes': '\u22CC',
10491 'rthree': '\u22CC',
10492 'ring': '\u02DA',
10493 'rlm': '\u200F',
10494 'rmoust': '\u23B1',
10495 'rmoustache': '\u23B1',
10496 'rnmid': '\u2AEE',
10497 'roang': '\u27ED',
10498 'roarr': '\u21FE',
10499 'ropar': '\u2986',
10500 'ropf': '\uD835\uDD63',
10501 'roplus': '\u2A2E',
10502 'rotimes': '\u2A35',
10503 'rpar': '\u0029',
10504 'rpargt': '\u2994',
10505 'rppolint': '\u2A12',
10506 'rsaquo': '\u203A',
10507 'rscr': '\uD835\uDCC7',
10508 'rtimes': '\u22CA',
10509 'rtri': '\u25B9',
10510 'triangleright': '\u25B9',
10511 'rtriltri': '\u29CE',
10512 'ruluhar': '\u2968',
10513 'rx': '\u211E',
10514 'sacute': '\u015B',
10515 'scE': '\u2AB4',
10516 'scap': '\u2AB8',
10517 'succapprox': '\u2AB8',
10518 'scaron': '\u0161',
10519 'scedil': '\u015F',
10520 'scirc': '\u015D',
10521 'scnE': '\u2AB6',
10522 'succneqq': '\u2AB6',
10523 'scnap': '\u2ABA',
10524 'succnapprox': '\u2ABA',
10525 'scnsim': '\u22E9',
10526 'succnsim': '\u22E9',
10527 'scpolint': '\u2A13',
10528 'scy': '\u0441',
10529 'sdot': '\u22C5',
10530 'sdote': '\u2A66',
10531 'seArr': '\u21D8',
10532 'sect': '\u00A7',
10533 'semi': '\u003B',
10534 'seswar': '\u2929',
10535 'tosa': '\u2929',
10536 'sext': '\u2736',
10537 'sfr': '\uD835\uDD30',
10538 'sharp': '\u266F',
10539 'shchcy': '\u0449',
10540 'shcy': '\u0448',
10541 'shy': '\u00AD',
10542 'sigma': '\u03C3',
10543 'sigmaf': '\u03C2',
10544 'sigmav': '\u03C2',
10545 'varsigma': '\u03C2',
10546 'simdot': '\u2A6A',
10547 'simg': '\u2A9E',
10548 'simgE': '\u2AA0',
10549 'siml': '\u2A9D',
10550 'simlE': '\u2A9F',
10551 'simne': '\u2246',
10552 'simplus': '\u2A24',
10553 'simrarr': '\u2972',
10554 'smashp': '\u2A33',
10555 'smeparsl': '\u29E4',
10556 'smile': '\u2323',
10557 'ssmile': '\u2323',
10558 'smt': '\u2AAA',
10559 'smte': '\u2AAC',
10560 'smtes': '\u2AAC\uFE00',
10561 'softcy': '\u044C',
10562 'sol': '\u002F',
10563 'solb': '\u29C4',
10564 'solbar': '\u233F',
10565 'sopf': '\uD835\uDD64',
10566 'spades': '\u2660',
10567 'spadesuit': '\u2660',
10568 'sqcaps': '\u2293\uFE00',
10569 'sqcups': '\u2294\uFE00',
10570 'sscr': '\uD835\uDCC8',
10571 'star': '\u2606',
10572 'sub': '\u2282',
10573 'subset': '\u2282',
10574 'subE': '\u2AC5',
10575 'subseteqq': '\u2AC5',
10576 'subdot': '\u2ABD',
10577 'subedot': '\u2AC3',
10578 'submult': '\u2AC1',
10579 'subnE': '\u2ACB',
10580 'subsetneqq': '\u2ACB',
10581 'subne': '\u228A',
10582 'subsetneq': '\u228A',
10583 'subplus': '\u2ABF',
10584 'subrarr': '\u2979',
10585 'subsim': '\u2AC7',
10586 'subsub': '\u2AD5',
10587 'subsup': '\u2AD3',
10588 'sung': '\u266A',
10589 'sup1': '\u00B9',
10590 'sup2': '\u00B2',
10591 'sup3': '\u00B3',
10592 'supE': '\u2AC6',
10593 'supseteqq': '\u2AC6',
10594 'supdot': '\u2ABE',
10595 'supdsub': '\u2AD8',
10596 'supedot': '\u2AC4',
10597 'suphsol': '\u27C9',
10598 'suphsub': '\u2AD7',
10599 'suplarr': '\u297B',
10600 'supmult': '\u2AC2',
10601 'supnE': '\u2ACC',
10602 'supsetneqq': '\u2ACC',
10603 'supne': '\u228B',
10604 'supsetneq': '\u228B',
10605 'supplus': '\u2AC0',
10606 'supsim': '\u2AC8',
10607 'supsub': '\u2AD4',
10608 'supsup': '\u2AD6',
10609 'swArr': '\u21D9',
10610 'swnwar': '\u292A',
10611 'szlig': '\u00DF',
10612 'target': '\u2316',
10613 'tau': '\u03C4',
10614 'tcaron': '\u0165',
10615 'tcedil': '\u0163',
10616 'tcy': '\u0442',
10617 'telrec': '\u2315',
10618 'tfr': '\uD835\uDD31',
10619 'theta': '\u03B8',
10620 'thetasym': '\u03D1',
10621 'thetav': '\u03D1',
10622 'vartheta': '\u03D1',
10623 'thorn': '\u00FE',
10624 'times': '\u00D7',
10625 'timesbar': '\u2A31',
10626 'timesd': '\u2A30',
10627 'topbot': '\u2336',
10628 'topcir': '\u2AF1',
10629 'topf': '\uD835\uDD65',
10630 'topfork': '\u2ADA',
10631 'tprime': '\u2034',
10632 'triangle': '\u25B5',
10633 'utri': '\u25B5',
10634 'triangleq': '\u225C',
10635 'trie': '\u225C',
10636 'tridot': '\u25EC',
10637 'triminus': '\u2A3A',
10638 'triplus': '\u2A39',
10639 'trisb': '\u29CD',
10640 'tritime': '\u2A3B',
10641 'trpezium': '\u23E2',
10642 'tscr': '\uD835\uDCC9',
10643 'tscy': '\u0446',
10644 'tshcy': '\u045B',
10645 'tstrok': '\u0167',
10646 'uHar': '\u2963',
10647 'uacute': '\u00FA',
10648 'ubrcy': '\u045E',
10649 'ubreve': '\u016D',
10650 'ucirc': '\u00FB',
10651 'ucy': '\u0443',
10652 'udblac': '\u0171',
10653 'ufisht': '\u297E',
10654 'ufr': '\uD835\uDD32',
10655 'ugrave': '\u00F9',
10656 'uhblk': '\u2580',
10657 'ulcorn': '\u231C',
10658 'ulcorner': '\u231C',
10659 'ulcrop': '\u230F',
10660 'ultri': '\u25F8',
10661 'umacr': '\u016B',
10662 'uogon': '\u0173',
10663 'uopf': '\uD835\uDD66',
10664 'upsi': '\u03C5',
10665 'upsilon': '\u03C5',
10666 'upuparrows': '\u21C8',
10667 'uuarr': '\u21C8',
10668 'urcorn': '\u231D',
10669 'urcorner': '\u231D',
10670 'urcrop': '\u230E',
10671 'uring': '\u016F',
10672 'urtri': '\u25F9',
10673 'uscr': '\uD835\uDCCA',
10674 'utdot': '\u22F0',
10675 'utilde': '\u0169',
10676 'uuml': '\u00FC',
10677 'uwangle': '\u29A7',
10678 'vBar': '\u2AE8',
10679 'vBarv': '\u2AE9',
10680 'vangrt': '\u299C',
10681 'varsubsetneq': '\u228A\uFE00',
10682 'vsubne': '\u228A\uFE00',
10683 'varsubsetneqq': '\u2ACB\uFE00',
10684 'vsubnE': '\u2ACB\uFE00',
10685 'varsupsetneq': '\u228B\uFE00',
10686 'vsupne': '\u228B\uFE00',
10687 'varsupsetneqq': '\u2ACC\uFE00',
10688 'vsupnE': '\u2ACC\uFE00',
10689 'vcy': '\u0432',
10690 'veebar': '\u22BB',
10691 'veeeq': '\u225A',
10692 'vellip': '\u22EE',
10693 'vfr': '\uD835\uDD33',
10694 'vopf': '\uD835\uDD67',
10695 'vscr': '\uD835\uDCCB',
10696 'vzigzag': '\u299A',
10697 'wcirc': '\u0175',
10698 'wedbar': '\u2A5F',
10699 'wedgeq': '\u2259',
10700 'weierp': '\u2118',
10701 'wp': '\u2118',
10702 'wfr': '\uD835\uDD34',
10703 'wopf': '\uD835\uDD68',
10704 'wscr': '\uD835\uDCCC',
10705 'xfr': '\uD835\uDD35',
10706 'xi': '\u03BE',
10707 'xnis': '\u22FB',
10708 'xopf': '\uD835\uDD69',
10709 'xscr': '\uD835\uDCCD',
10710 'yacute': '\u00FD',
10711 'yacy': '\u044F',
10712 'ycirc': '\u0177',
10713 'ycy': '\u044B',
10714 'yen': '\u00A5',
10715 'yfr': '\uD835\uDD36',
10716 'yicy': '\u0457',
10717 'yopf': '\uD835\uDD6A',
10718 'yscr': '\uD835\uDCCE',
10719 'yucy': '\u044E',
10720 'yuml': '\u00FF',
10721 'zacute': '\u017A',
10722 'zcaron': '\u017E',
10723 'zcy': '\u0437',
10724 'zdot': '\u017C',
10725 'zeta': '\u03B6',
10726 'zfr': '\uD835\uDD37',
10727 'zhcy': '\u0436',
10728 'zigrarr': '\u21DD',
10729 'zopf': '\uD835\uDD6B',
10730 'zscr': '\uD835\uDCCF',
10731 'zwj': '\u200D',
10732 'zwnj': '\u200C'
10733 };
10734 // The &ngsp; pseudo-entity is denoting a space. see:
10735 // https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart
10736 const NGSP_UNICODE = '\uE500';
10737 NAMED_ENTITIES['ngsp'] = NGSP_UNICODE;
10738
10739 /**
10740 * @license
10741 * Copyright Google LLC All Rights Reserved.
10742 *
10743 * Use of this source code is governed by an MIT-style license that can be
10744 * found in the LICENSE file at https://angular.io/license
10745 */
10746 class TokenError extends ParseError {
10747 constructor(errorMsg, tokenType, span) {
10748 super(span, errorMsg);
10749 this.tokenType = tokenType;
10750 }
10751 }
10752 class TokenizeResult {
10753 constructor(tokens, errors, nonNormalizedIcuExpressions) {
10754 this.tokens = tokens;
10755 this.errors = errors;
10756 this.nonNormalizedIcuExpressions = nonNormalizedIcuExpressions;
10757 }
10758 }
10759 function tokenize(source, url, getTagDefinition, options = {}) {
10760 const tokenizer = new _Tokenizer(new ParseSourceFile(source, url), getTagDefinition, options);
10761 tokenizer.tokenize();
10762 return new TokenizeResult(mergeTextTokens(tokenizer.tokens), tokenizer.errors, tokenizer.nonNormalizedIcuExpressions);
10763 }
10764 const _CR_OR_CRLF_REGEXP = /\r\n?/g;
10765 function _unexpectedCharacterErrorMsg(charCode) {
10766 const char = charCode === $EOF ? 'EOF' : String.fromCharCode(charCode);
10767 return `Unexpected character "${char}"`;
10768 }
10769 function _unknownEntityErrorMsg(entitySrc) {
10770 return `Unknown entity "${entitySrc}" - use the "&#<decimal>;" or "&#x<hex>;" syntax`;
10771 }
10772 function _unparsableEntityErrorMsg(type, entityStr) {
10773 return `Unable to parse entity "${entityStr}" - ${type} character reference entities must end with ";"`;
10774 }
10775 var CharacterReferenceType;
10776 (function (CharacterReferenceType) {
10777 CharacterReferenceType["HEX"] = "hexadecimal";
10778 CharacterReferenceType["DEC"] = "decimal";
10779 })(CharacterReferenceType || (CharacterReferenceType = {}));
10780 class _ControlFlowError {
10781 constructor(error) {
10782 this.error = error;
10783 }
10784 }
10785 // See https://www.w3.org/TR/html51/syntax.html#writing-html-documents
10786 class _Tokenizer {
10787 /**
10788 * @param _file The html source file being tokenized.
10789 * @param _getTagDefinition A function that will retrieve a tag definition for a given tag name.
10790 * @param options Configuration of the tokenization.
10791 */
10792 constructor(_file, _getTagDefinition, options) {
10793 this._getTagDefinition = _getTagDefinition;
10794 this._currentTokenStart = null;
10795 this._currentTokenType = null;
10796 this._expansionCaseStack = [];
10797 this._inInterpolation = false;
10798 this.tokens = [];
10799 this.errors = [];
10800 this.nonNormalizedIcuExpressions = [];
10801 this._tokenizeIcu = options.tokenizeExpansionForms || false;
10802 this._interpolationConfig = options.interpolationConfig || DEFAULT_INTERPOLATION_CONFIG;
10803 this._leadingTriviaCodePoints =
10804 options.leadingTriviaChars && options.leadingTriviaChars.map(c => c.codePointAt(0) || 0);
10805 const range = options.range || { endPos: _file.content.length, startPos: 0, startLine: 0, startCol: 0 };
10806 this._cursor = options.escapedString ? new EscapedCharacterCursor(_file, range) :
10807 new PlainCharacterCursor(_file, range);
10808 this._preserveLineEndings = options.preserveLineEndings || false;
10809 this._escapedString = options.escapedString || false;
10810 this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
10811 try {
10812 this._cursor.init();
10813 }
10814 catch (e) {
10815 this.handleError(e);
10816 }
10817 }
10818 _processCarriageReturns(content) {
10819 if (this._preserveLineEndings) {
10820 return content;
10821 }
10822 // https://www.w3.org/TR/html51/syntax.html#preprocessing-the-input-stream
10823 // In order to keep the original position in the source, we can not
10824 // pre-process it.
10825 // Instead CRs are processed right before instantiating the tokens.
10826 return content.replace(_CR_OR_CRLF_REGEXP, '\n');
10827 }
10828 tokenize() {
10829 while (this._cursor.peek() !== $EOF) {
10830 const start = this._cursor.clone();
10831 try {
10832 if (this._attemptCharCode($LT)) {
10833 if (this._attemptCharCode($BANG)) {
10834 if (this._attemptCharCode($LBRACKET)) {
10835 this._consumeCdata(start);
10836 }
10837 else if (this._attemptCharCode($MINUS)) {
10838 this._consumeComment(start);
10839 }
10840 else {
10841 this._consumeDocType(start);
10842 }
10843 }
10844 else if (this._attemptCharCode($SLASH)) {
10845 this._consumeTagClose(start);
10846 }
10847 else {
10848 this._consumeTagOpen(start);
10849 }
10850 }
10851 else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {
10852 // In (possibly interpolated) text the end of the text is given by `isTextEnd()`, while
10853 // the premature end of an interpolation is given by the start of a new HTML element.
10854 this._consumeWithInterpolation(5 /* TEXT */, 8 /* INTERPOLATION */, () => this._isTextEnd(), () => this._isTagStart());
10855 }
10856 }
10857 catch (e) {
10858 this.handleError(e);
10859 }
10860 }
10861 this._beginToken(24 /* EOF */);
10862 this._endToken([]);
10863 }
10864 /**
10865 * @returns whether an ICU token has been created
10866 * @internal
10867 */
10868 _tokenizeExpansionForm() {
10869 if (this.isExpansionFormStart()) {
10870 this._consumeExpansionFormStart();
10871 return true;
10872 }
10873 if (isExpansionCaseStart(this._cursor.peek()) && this._isInExpansionForm()) {
10874 this._consumeExpansionCaseStart();
10875 return true;
10876 }
10877 if (this._cursor.peek() === $RBRACE) {
10878 if (this._isInExpansionCase()) {
10879 this._consumeExpansionCaseEnd();
10880 return true;
10881 }
10882 if (this._isInExpansionForm()) {
10883 this._consumeExpansionFormEnd();
10884 return true;
10885 }
10886 }
10887 return false;
10888 }
10889 _beginToken(type, start = this._cursor.clone()) {
10890 this._currentTokenStart = start;
10891 this._currentTokenType = type;
10892 }
10893 _endToken(parts, end) {
10894 if (this._currentTokenStart === null) {
10895 throw new TokenError('Programming error - attempted to end a token when there was no start to the token', this._currentTokenType, this._cursor.getSpan(end));
10896 }
10897 if (this._currentTokenType === null) {
10898 throw new TokenError('Programming error - attempted to end a token which has no token type', null, this._cursor.getSpan(this._currentTokenStart));
10899 }
10900 const token = {
10901 type: this._currentTokenType,
10902 parts,
10903 sourceSpan: (end ?? this._cursor).getSpan(this._currentTokenStart, this._leadingTriviaCodePoints),
10904 };
10905 this.tokens.push(token);
10906 this._currentTokenStart = null;
10907 this._currentTokenType = null;
10908 return token;
10909 }
10910 _createError(msg, span) {
10911 if (this._isInExpansionForm()) {
10912 msg += ` (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)`;
10913 }
10914 const error = new TokenError(msg, this._currentTokenType, span);
10915 this._currentTokenStart = null;
10916 this._currentTokenType = null;
10917 return new _ControlFlowError(error);
10918 }
10919 handleError(e) {
10920 if (e instanceof CursorError) {
10921 e = this._createError(e.msg, this._cursor.getSpan(e.cursor));
10922 }
10923 if (e instanceof _ControlFlowError) {
10924 this.errors.push(e.error);
10925 }
10926 else {
10927 throw e;
10928 }
10929 }
10930 _attemptCharCode(charCode) {
10931 if (this._cursor.peek() === charCode) {
10932 this._cursor.advance();
10933 return true;
10934 }
10935 return false;
10936 }
10937 _attemptCharCodeCaseInsensitive(charCode) {
10938 if (compareCharCodeCaseInsensitive(this._cursor.peek(), charCode)) {
10939 this._cursor.advance();
10940 return true;
10941 }
10942 return false;
10943 }
10944 _requireCharCode(charCode) {
10945 const location = this._cursor.clone();
10946 if (!this._attemptCharCode(charCode)) {
10947 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
10948 }
10949 }
10950 _attemptStr(chars) {
10951 const len = chars.length;
10952 if (this._cursor.charsLeft() < len) {
10953 return false;
10954 }
10955 const initialPosition = this._cursor.clone();
10956 for (let i = 0; i < len; i++) {
10957 if (!this._attemptCharCode(chars.charCodeAt(i))) {
10958 // If attempting to parse the string fails, we want to reset the parser
10959 // to where it was before the attempt
10960 this._cursor = initialPosition;
10961 return false;
10962 }
10963 }
10964 return true;
10965 }
10966 _attemptStrCaseInsensitive(chars) {
10967 for (let i = 0; i < chars.length; i++) {
10968 if (!this._attemptCharCodeCaseInsensitive(chars.charCodeAt(i))) {
10969 return false;
10970 }
10971 }
10972 return true;
10973 }
10974 _requireStr(chars) {
10975 const location = this._cursor.clone();
10976 if (!this._attemptStr(chars)) {
10977 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
10978 }
10979 }
10980 _attemptCharCodeUntilFn(predicate) {
10981 while (!predicate(this._cursor.peek())) {
10982 this._cursor.advance();
10983 }
10984 }
10985 _requireCharCodeUntilFn(predicate, len) {
10986 const start = this._cursor.clone();
10987 this._attemptCharCodeUntilFn(predicate);
10988 if (this._cursor.diff(start) < len) {
10989 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
10990 }
10991 }
10992 _attemptUntilChar(char) {
10993 while (this._cursor.peek() !== char) {
10994 this._cursor.advance();
10995 }
10996 }
10997 _readChar() {
10998 // Don't rely upon reading directly from `_input` as the actual char value
10999 // may have been generated from an escape sequence.
11000 const char = String.fromCodePoint(this._cursor.peek());
11001 this._cursor.advance();
11002 return char;
11003 }
11004 _consumeEntity(textTokenType) {
11005 this._beginToken(9 /* ENCODED_ENTITY */);
11006 const start = this._cursor.clone();
11007 this._cursor.advance();
11008 if (this._attemptCharCode($HASH)) {
11009 const isHex = this._attemptCharCode($x) || this._attemptCharCode($X);
11010 const codeStart = this._cursor.clone();
11011 this._attemptCharCodeUntilFn(isDigitEntityEnd);
11012 if (this._cursor.peek() != $SEMICOLON) {
11013 // Advance cursor to include the peeked character in the string provided to the error
11014 // message.
11015 this._cursor.advance();
11016 const entityType = isHex ? CharacterReferenceType.HEX : CharacterReferenceType.DEC;
11017 throw this._createError(_unparsableEntityErrorMsg(entityType, this._cursor.getChars(start)), this._cursor.getSpan());
11018 }
11019 const strNum = this._cursor.getChars(codeStart);
11020 this._cursor.advance();
11021 try {
11022 const charCode = parseInt(strNum, isHex ? 16 : 10);
11023 this._endToken([String.fromCharCode(charCode), this._cursor.getChars(start)]);
11024 }
11025 catch {
11026 throw this._createError(_unknownEntityErrorMsg(this._cursor.getChars(start)), this._cursor.getSpan());
11027 }
11028 }
11029 else {
11030 const nameStart = this._cursor.clone();
11031 this._attemptCharCodeUntilFn(isNamedEntityEnd);
11032 if (this._cursor.peek() != $SEMICOLON) {
11033 // No semicolon was found so abort the encoded entity token that was in progress, and treat
11034 // this as a text token
11035 this._beginToken(textTokenType, start);
11036 this._cursor = nameStart;
11037 this._endToken(['&']);
11038 }
11039 else {
11040 const name = this._cursor.getChars(nameStart);
11041 this._cursor.advance();
11042 const char = NAMED_ENTITIES[name];
11043 if (!char) {
11044 throw this._createError(_unknownEntityErrorMsg(name), this._cursor.getSpan(start));
11045 }
11046 this._endToken([char, `&${name};`]);
11047 }
11048 }
11049 }
11050 _consumeRawText(consumeEntities, endMarkerPredicate) {
11051 this._beginToken(consumeEntities ? 6 /* ESCAPABLE_RAW_TEXT */ : 7 /* RAW_TEXT */);
11052 const parts = [];
11053 while (true) {
11054 const tagCloseStart = this._cursor.clone();
11055 const foundEndMarker = endMarkerPredicate();
11056 this._cursor = tagCloseStart;
11057 if (foundEndMarker) {
11058 break;
11059 }
11060 if (consumeEntities && this._cursor.peek() === $AMPERSAND) {
11061 this._endToken([this._processCarriageReturns(parts.join(''))]);
11062 parts.length = 0;
11063 this._consumeEntity(6 /* ESCAPABLE_RAW_TEXT */);
11064 this._beginToken(6 /* ESCAPABLE_RAW_TEXT */);
11065 }
11066 else {
11067 parts.push(this._readChar());
11068 }
11069 }
11070 this._endToken([this._processCarriageReturns(parts.join(''))]);
11071 }
11072 _consumeComment(start) {
11073 this._beginToken(10 /* COMMENT_START */, start);
11074 this._requireCharCode($MINUS);
11075 this._endToken([]);
11076 this._consumeRawText(false, () => this._attemptStr('-->'));
11077 this._beginToken(11 /* COMMENT_END */);
11078 this._requireStr('-->');
11079 this._endToken([]);
11080 }
11081 _consumeCdata(start) {
11082 this._beginToken(12 /* CDATA_START */, start);
11083 this._requireStr('CDATA[');
11084 this._endToken([]);
11085 this._consumeRawText(false, () => this._attemptStr(']]>'));
11086 this._beginToken(13 /* CDATA_END */);
11087 this._requireStr(']]>');
11088 this._endToken([]);
11089 }
11090 _consumeDocType(start) {
11091 this._beginToken(18 /* DOC_TYPE */, start);
11092 const contentStart = this._cursor.clone();
11093 this._attemptUntilChar($GT);
11094 const content = this._cursor.getChars(contentStart);
11095 this._cursor.advance();
11096 this._endToken([content]);
11097 }
11098 _consumePrefixAndName() {
11099 const nameOrPrefixStart = this._cursor.clone();
11100 let prefix = '';
11101 while (this._cursor.peek() !== $COLON && !isPrefixEnd(this._cursor.peek())) {
11102 this._cursor.advance();
11103 }
11104 let nameStart;
11105 if (this._cursor.peek() === $COLON) {
11106 prefix = this._cursor.getChars(nameOrPrefixStart);
11107 this._cursor.advance();
11108 nameStart = this._cursor.clone();
11109 }
11110 else {
11111 nameStart = nameOrPrefixStart;
11112 }
11113 this._requireCharCodeUntilFn(isNameEnd, prefix === '' ? 0 : 1);
11114 const name = this._cursor.getChars(nameStart);
11115 return [prefix, name];
11116 }
11117 _consumeTagOpen(start) {
11118 let tagName;
11119 let prefix;
11120 let openTagToken;
11121 try {
11122 if (!isAsciiLetter(this._cursor.peek())) {
11123 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
11124 }
11125 openTagToken = this._consumeTagOpenStart(start);
11126 prefix = openTagToken.parts[0];
11127 tagName = openTagToken.parts[1];
11128 this._attemptCharCodeUntilFn(isNotWhitespace);
11129 while (this._cursor.peek() !== $SLASH && this._cursor.peek() !== $GT &&
11130 this._cursor.peek() !== $LT && this._cursor.peek() !== $EOF) {
11131 this._consumeAttributeName();
11132 this._attemptCharCodeUntilFn(isNotWhitespace);
11133 if (this._attemptCharCode($EQ)) {
11134 this._attemptCharCodeUntilFn(isNotWhitespace);
11135 this._consumeAttributeValue();
11136 }
11137 this._attemptCharCodeUntilFn(isNotWhitespace);
11138 }
11139 this._consumeTagOpenEnd();
11140 }
11141 catch (e) {
11142 if (e instanceof _ControlFlowError) {
11143 if (openTagToken) {
11144 // We errored before we could close the opening tag, so it is incomplete.
11145 openTagToken.type = 4 /* INCOMPLETE_TAG_OPEN */;
11146 }
11147 else {
11148 // When the start tag is invalid, assume we want a "<" as text.
11149 // Back to back text tokens are merged at the end.
11150 this._beginToken(5 /* TEXT */, start);
11151 this._endToken(['<']);
11152 }
11153 return;
11154 }
11155 throw e;
11156 }
11157 const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix);
11158 if (contentTokenType === TagContentType.RAW_TEXT) {
11159 this._consumeRawTextWithTagClose(prefix, tagName, false);
11160 }
11161 else if (contentTokenType === TagContentType.ESCAPABLE_RAW_TEXT) {
11162 this._consumeRawTextWithTagClose(prefix, tagName, true);
11163 }
11164 }
11165 _consumeRawTextWithTagClose(prefix, tagName, consumeEntities) {
11166 this._consumeRawText(consumeEntities, () => {
11167 if (!this._attemptCharCode($LT))
11168 return false;
11169 if (!this._attemptCharCode($SLASH))
11170 return false;
11171 this._attemptCharCodeUntilFn(isNotWhitespace);
11172 if (!this._attemptStrCaseInsensitive(tagName))
11173 return false;
11174 this._attemptCharCodeUntilFn(isNotWhitespace);
11175 return this._attemptCharCode($GT);
11176 });
11177 this._beginToken(3 /* TAG_CLOSE */);
11178 this._requireCharCodeUntilFn(code => code === $GT, 3);
11179 this._cursor.advance(); // Consume the `>`
11180 this._endToken([prefix, tagName]);
11181 }
11182 _consumeTagOpenStart(start) {
11183 this._beginToken(0 /* TAG_OPEN_START */, start);
11184 const parts = this._consumePrefixAndName();
11185 return this._endToken(parts);
11186 }
11187 _consumeAttributeName() {
11188 const attrNameStart = this._cursor.peek();
11189 if (attrNameStart === $SQ || attrNameStart === $DQ) {
11190 throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());
11191 }
11192 this._beginToken(14 /* ATTR_NAME */);
11193 const prefixAndName = this._consumePrefixAndName();
11194 this._endToken(prefixAndName);
11195 }
11196 _consumeAttributeValue() {
11197 if (this._cursor.peek() === $SQ || this._cursor.peek() === $DQ) {
11198 const quoteChar = this._cursor.peek();
11199 this._consumeQuote(quoteChar);
11200 // In an attribute then end of the attribute value and the premature end to an interpolation
11201 // are both triggered by the `quoteChar`.
11202 const endPredicate = () => this._cursor.peek() === quoteChar;
11203 this._consumeWithInterpolation(16 /* ATTR_VALUE_TEXT */, 17 /* ATTR_VALUE_INTERPOLATION */, endPredicate, endPredicate);
11204 this._consumeQuote(quoteChar);
11205 }
11206 else {
11207 const endPredicate = () => isNameEnd(this._cursor.peek());
11208 this._consumeWithInterpolation(16 /* ATTR_VALUE_TEXT */, 17 /* ATTR_VALUE_INTERPOLATION */, endPredicate, endPredicate);
11209 }
11210 }
11211 _consumeQuote(quoteChar) {
11212 this._beginToken(15 /* ATTR_QUOTE */);
11213 this._requireCharCode(quoteChar);
11214 this._endToken([String.fromCodePoint(quoteChar)]);
11215 }
11216 _consumeTagOpenEnd() {
11217 const tokenType = this._attemptCharCode($SLASH) ? 2 /* TAG_OPEN_END_VOID */ : 1 /* TAG_OPEN_END */;
11218 this._beginToken(tokenType);
11219 this._requireCharCode($GT);
11220 this._endToken([]);
11221 }
11222 _consumeTagClose(start) {
11223 this._beginToken(3 /* TAG_CLOSE */, start);
11224 this._attemptCharCodeUntilFn(isNotWhitespace);
11225 const prefixAndName = this._consumePrefixAndName();
11226 this._attemptCharCodeUntilFn(isNotWhitespace);
11227 this._requireCharCode($GT);
11228 this._endToken(prefixAndName);
11229 }
11230 _consumeExpansionFormStart() {
11231 this._beginToken(19 /* EXPANSION_FORM_START */);
11232 this._requireCharCode($LBRACE);
11233 this._endToken([]);
11234 this._expansionCaseStack.push(19 /* EXPANSION_FORM_START */);
11235 this._beginToken(7 /* RAW_TEXT */);
11236 const condition = this._readUntil($COMMA);
11237 const normalizedCondition = this._processCarriageReturns(condition);
11238 if (this._i18nNormalizeLineEndingsInICUs) {
11239 // We explicitly want to normalize line endings for this text.
11240 this._endToken([normalizedCondition]);
11241 }
11242 else {
11243 // We are not normalizing line endings.
11244 const conditionToken = this._endToken([condition]);
11245 if (normalizedCondition !== condition) {
11246 this.nonNormalizedIcuExpressions.push(conditionToken);
11247 }
11248 }
11249 this._requireCharCode($COMMA);
11250 this._attemptCharCodeUntilFn(isNotWhitespace);
11251 this._beginToken(7 /* RAW_TEXT */);
11252 const type = this._readUntil($COMMA);
11253 this._endToken([type]);
11254 this._requireCharCode($COMMA);
11255 this._attemptCharCodeUntilFn(isNotWhitespace);
11256 }
11257 _consumeExpansionCaseStart() {
11258 this._beginToken(20 /* EXPANSION_CASE_VALUE */);
11259 const value = this._readUntil($LBRACE).trim();
11260 this._endToken([value]);
11261 this._attemptCharCodeUntilFn(isNotWhitespace);
11262 this._beginToken(21 /* EXPANSION_CASE_EXP_START */);
11263 this._requireCharCode($LBRACE);
11264 this._endToken([]);
11265 this._attemptCharCodeUntilFn(isNotWhitespace);
11266 this._expansionCaseStack.push(21 /* EXPANSION_CASE_EXP_START */);
11267 }
11268 _consumeExpansionCaseEnd() {
11269 this._beginToken(22 /* EXPANSION_CASE_EXP_END */);
11270 this._requireCharCode($RBRACE);
11271 this._endToken([]);
11272 this._attemptCharCodeUntilFn(isNotWhitespace);
11273 this._expansionCaseStack.pop();
11274 }
11275 _consumeExpansionFormEnd() {
11276 this._beginToken(23 /* EXPANSION_FORM_END */);
11277 this._requireCharCode($RBRACE);
11278 this._endToken([]);
11279 this._expansionCaseStack.pop();
11280 }
11281 /**
11282 * Consume a string that may contain interpolation expressions.
11283 *
11284 * The first token consumed will be of `tokenType` and then there will be alternating
11285 * `interpolationTokenType` and `tokenType` tokens until the `endPredicate()` returns true.
11286 *
11287 * If an interpolation token ends prematurely it will have no end marker in its `parts` array.
11288 *
11289 * @param textTokenType the kind of tokens to interleave around interpolation tokens.
11290 * @param interpolationTokenType the kind of tokens that contain interpolation.
11291 * @param endPredicate a function that should return true when we should stop consuming.
11292 * @param endInterpolation a function that should return true if there is a premature end to an
11293 * interpolation expression - i.e. before we get to the normal interpolation closing marker.
11294 */
11295 _consumeWithInterpolation(textTokenType, interpolationTokenType, endPredicate, endInterpolation) {
11296 this._beginToken(textTokenType);
11297 const parts = [];
11298 while (!endPredicate()) {
11299 const current = this._cursor.clone();
11300 if (this._interpolationConfig && this._attemptStr(this._interpolationConfig.start)) {
11301 this._endToken([this._processCarriageReturns(parts.join(''))], current);
11302 parts.length = 0;
11303 this._consumeInterpolation(interpolationTokenType, current, endInterpolation);
11304 this._beginToken(textTokenType);
11305 }
11306 else if (this._cursor.peek() === $AMPERSAND) {
11307 this._endToken([this._processCarriageReturns(parts.join(''))]);
11308 parts.length = 0;
11309 this._consumeEntity(textTokenType);
11310 this._beginToken(textTokenType);
11311 }
11312 else {
11313 parts.push(this._readChar());
11314 }
11315 }
11316 // It is possible that an interpolation was started but not ended inside this text token.
11317 // Make sure that we reset the state of the lexer correctly.
11318 this._inInterpolation = false;
11319 this._endToken([this._processCarriageReturns(parts.join(''))]);
11320 }
11321 /**
11322 * Consume a block of text that has been interpreted as an Angular interpolation.
11323 *
11324 * @param interpolationTokenType the type of the interpolation token to generate.
11325 * @param interpolationStart a cursor that points to the start of this interpolation.
11326 * @param prematureEndPredicate a function that should return true if the next characters indicate
11327 * an end to the interpolation before its normal closing marker.
11328 */
11329 _consumeInterpolation(interpolationTokenType, interpolationStart, prematureEndPredicate) {
11330 const parts = [];
11331 this._beginToken(interpolationTokenType, interpolationStart);
11332 parts.push(this._interpolationConfig.start);
11333 // Find the end of the interpolation, ignoring content inside quotes.
11334 const expressionStart = this._cursor.clone();
11335 let inQuote = null;
11336 let inComment = false;
11337 while (this._cursor.peek() !== $EOF &&
11338 (prematureEndPredicate === null || !prematureEndPredicate())) {
11339 const current = this._cursor.clone();
11340 if (this._isTagStart()) {
11341 // We are starting what looks like an HTML element in the middle of this interpolation.
11342 // Reset the cursor to before the `<` character and end the interpolation token.
11343 // (This is actually wrong but here for backward compatibility).
11344 this._cursor = current;
11345 parts.push(this._getProcessedChars(expressionStart, current));
11346 this._endToken(parts);
11347 return;
11348 }
11349 if (inQuote === null) {
11350 if (this._attemptStr(this._interpolationConfig.end)) {
11351 // We are not in a string, and we hit the end interpolation marker
11352 parts.push(this._getProcessedChars(expressionStart, current));
11353 parts.push(this._interpolationConfig.end);
11354 this._endToken(parts);
11355 return;
11356 }
11357 else if (this._attemptStr('//')) {
11358 // Once we are in a comment we ignore any quotes
11359 inComment = true;
11360 }
11361 }
11362 const char = this._cursor.peek();
11363 this._cursor.advance();
11364 if (char === $BACKSLASH) {
11365 // Skip the next character because it was escaped.
11366 this._cursor.advance();
11367 }
11368 else if (char === inQuote) {
11369 // Exiting the current quoted string
11370 inQuote = null;
11371 }
11372 else if (!inComment && inQuote === null && isQuote(char)) {
11373 // Entering a new quoted string
11374 inQuote = char;
11375 }
11376 }
11377 // We hit EOF without finding a closing interpolation marker
11378 parts.push(this._getProcessedChars(expressionStart, this._cursor));
11379 this._endToken(parts);
11380 }
11381 _getProcessedChars(start, end) {
11382 return this._processCarriageReturns(end.getChars(start));
11383 }
11384 _isTextEnd() {
11385 if (this._isTagStart() || this._cursor.peek() === $EOF) {
11386 return true;
11387 }
11388 if (this._tokenizeIcu && !this._inInterpolation) {
11389 if (this.isExpansionFormStart()) {
11390 // start of an expansion form
11391 return true;
11392 }
11393 if (this._cursor.peek() === $RBRACE && this._isInExpansionCase()) {
11394 // end of and expansion case
11395 return true;
11396 }
11397 }
11398 return false;
11399 }
11400 /**
11401 * Returns true if the current cursor is pointing to the start of a tag
11402 * (opening/closing/comments/cdata/etc).
11403 */
11404 _isTagStart() {
11405 if (this._cursor.peek() === $LT) {
11406 // We assume that `<` followed by whitespace is not the start of an HTML element.
11407 const tmp = this._cursor.clone();
11408 tmp.advance();
11409 // If the next character is alphabetic, ! nor / then it is a tag start
11410 const code = tmp.peek();
11411 if (($a <= code && code <= $z) || ($A <= code && code <= $Z) ||
11412 code === $SLASH || code === $BANG) {
11413 return true;
11414 }
11415 }
11416 return false;
11417 }
11418 _readUntil(char) {
11419 const start = this._cursor.clone();
11420 this._attemptUntilChar(char);
11421 return this._cursor.getChars(start);
11422 }
11423 _isInExpansionCase() {
11424 return this._expansionCaseStack.length > 0 &&
11425 this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
11426 21 /* EXPANSION_CASE_EXP_START */;
11427 }
11428 _isInExpansionForm() {
11429 return this._expansionCaseStack.length > 0 &&
11430 this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
11431 19 /* EXPANSION_FORM_START */;
11432 }
11433 isExpansionFormStart() {
11434 if (this._cursor.peek() !== $LBRACE) {
11435 return false;
11436 }
11437 if (this._interpolationConfig) {
11438 const start = this._cursor.clone();
11439 const isInterpolation = this._attemptStr(this._interpolationConfig.start);
11440 this._cursor = start;
11441 return !isInterpolation;
11442 }
11443 return true;
11444 }
11445 }
11446 function isNotWhitespace(code) {
11447 return !isWhitespace(code) || code === $EOF;
11448 }
11449 function isNameEnd(code) {
11450 return isWhitespace(code) || code === $GT || code === $LT ||
11451 code === $SLASH || code === $SQ || code === $DQ || code === $EQ ||
11452 code === $EOF;
11453 }
11454 function isPrefixEnd(code) {
11455 return (code < $a || $z < code) && (code < $A || $Z < code) &&
11456 (code < $0 || code > $9);
11457 }
11458 function isDigitEntityEnd(code) {
11459 return code === $SEMICOLON || code === $EOF || !isAsciiHexDigit(code);
11460 }
11461 function isNamedEntityEnd(code) {
11462 return code === $SEMICOLON || code === $EOF || !isAsciiLetter(code);
11463 }
11464 function isExpansionCaseStart(peek) {
11465 return peek !== $RBRACE;
11466 }
11467 function compareCharCodeCaseInsensitive(code1, code2) {
11468 return toUpperCaseCharCode(code1) === toUpperCaseCharCode(code2);
11469 }
11470 function toUpperCaseCharCode(code) {
11471 return code >= $a && code <= $z ? code - $a + $A : code;
11472 }
11473 function mergeTextTokens(srcTokens) {
11474 const dstTokens = [];
11475 let lastDstToken = undefined;
11476 for (let i = 0; i < srcTokens.length; i++) {
11477 const token = srcTokens[i];
11478 if ((lastDstToken && lastDstToken.type === 5 /* TEXT */ && token.type === 5 /* TEXT */) ||
11479 (lastDstToken && lastDstToken.type === 16 /* ATTR_VALUE_TEXT */ &&
11480 token.type === 16 /* ATTR_VALUE_TEXT */)) {
11481 lastDstToken.parts[0] += token.parts[0];
11482 lastDstToken.sourceSpan.end = token.sourceSpan.end;
11483 }
11484 else {
11485 lastDstToken = token;
11486 dstTokens.push(lastDstToken);
11487 }
11488 }
11489 return dstTokens;
11490 }
11491 class PlainCharacterCursor {
11492 constructor(fileOrCursor, range) {
11493 if (fileOrCursor instanceof PlainCharacterCursor) {
11494 this.file = fileOrCursor.file;
11495 this.input = fileOrCursor.input;
11496 this.end = fileOrCursor.end;
11497 const state = fileOrCursor.state;
11498 // Note: avoid using `{...fileOrCursor.state}` here as that has a severe performance penalty.
11499 // In ES5 bundles the object spread operator is translated into the `__assign` helper, which
11500 // is not optimized by VMs as efficiently as a raw object literal. Since this constructor is
11501 // called in tight loops, this difference matters.
11502 this.state = {
11503 peek: state.peek,
11504 offset: state.offset,
11505 line: state.line,
11506 column: state.column,
11507 };
11508 }
11509 else {
11510 if (!range) {
11511 throw new Error('Programming error: the range argument must be provided with a file argument.');
11512 }
11513 this.file = fileOrCursor;
11514 this.input = fileOrCursor.content;
11515 this.end = range.endPos;
11516 this.state = {
11517 peek: -1,
11518 offset: range.startPos,
11519 line: range.startLine,
11520 column: range.startCol,
11521 };
11522 }
11523 }
11524 clone() {
11525 return new PlainCharacterCursor(this);
11526 }
11527 peek() {
11528 return this.state.peek;
11529 }
11530 charsLeft() {
11531 return this.end - this.state.offset;
11532 }
11533 diff(other) {
11534 return this.state.offset - other.state.offset;
11535 }
11536 advance() {
11537 this.advanceState(this.state);
11538 }
11539 init() {
11540 this.updatePeek(this.state);
11541 }
11542 getSpan(start, leadingTriviaCodePoints) {
11543 start = start || this;
11544 let fullStart = start;
11545 if (leadingTriviaCodePoints) {
11546 while (this.diff(start) > 0 && leadingTriviaCodePoints.indexOf(start.peek()) !== -1) {
11547 if (fullStart === start) {
11548 start = start.clone();
11549 }
11550 start.advance();
11551 }
11552 }
11553 const startLocation = this.locationFromCursor(start);
11554 const endLocation = this.locationFromCursor(this);
11555 const fullStartLocation = fullStart !== start ? this.locationFromCursor(fullStart) : startLocation;
11556 return new ParseSourceSpan(startLocation, endLocation, fullStartLocation);
11557 }
11558 getChars(start) {
11559 return this.input.substring(start.state.offset, this.state.offset);
11560 }
11561 charAt(pos) {
11562 return this.input.charCodeAt(pos);
11563 }
11564 advanceState(state) {
11565 if (state.offset >= this.end) {
11566 this.state = state;
11567 throw new CursorError('Unexpected character "EOF"', this);
11568 }
11569 const currentChar = this.charAt(state.offset);
11570 if (currentChar === $LF) {
11571 state.line++;
11572 state.column = 0;
11573 }
11574 else if (!isNewLine(currentChar)) {
11575 state.column++;
11576 }
11577 state.offset++;
11578 this.updatePeek(state);
11579 }
11580 updatePeek(state) {
11581 state.peek = state.offset >= this.end ? $EOF : this.charAt(state.offset);
11582 }
11583 locationFromCursor(cursor) {
11584 return new ParseLocation(cursor.file, cursor.state.offset, cursor.state.line, cursor.state.column);
11585 }
11586 }
11587 class EscapedCharacterCursor extends PlainCharacterCursor {
11588 constructor(fileOrCursor, range) {
11589 if (fileOrCursor instanceof EscapedCharacterCursor) {
11590 super(fileOrCursor);
11591 this.internalState = { ...fileOrCursor.internalState };
11592 }
11593 else {
11594 super(fileOrCursor, range);
11595 this.internalState = this.state;
11596 }
11597 }
11598 advance() {
11599 this.state = this.internalState;
11600 super.advance();
11601 this.processEscapeSequence();
11602 }
11603 init() {
11604 super.init();
11605 this.processEscapeSequence();
11606 }
11607 clone() {
11608 return new EscapedCharacterCursor(this);
11609 }
11610 getChars(start) {
11611 const cursor = start.clone();
11612 let chars = '';
11613 while (cursor.internalState.offset < this.internalState.offset) {
11614 chars += String.fromCodePoint(cursor.peek());
11615 cursor.advance();
11616 }
11617 return chars;
11618 }
11619 /**
11620 * Process the escape sequence that starts at the current position in the text.
11621 *
11622 * This method is called to ensure that `peek` has the unescaped value of escape sequences.
11623 */
11624 processEscapeSequence() {
11625 const peek = () => this.internalState.peek;
11626 if (peek() === $BACKSLASH) {
11627 // We have hit an escape sequence so we need the internal state to become independent
11628 // of the external state.
11629 this.internalState = { ...this.state };
11630 // Move past the backslash
11631 this.advanceState(this.internalState);
11632 // First check for standard control char sequences
11633 if (peek() === $n) {
11634 this.state.peek = $LF;
11635 }
11636 else if (peek() === $r) {
11637 this.state.peek = $CR;
11638 }
11639 else if (peek() === $v) {
11640 this.state.peek = $VTAB;
11641 }
11642 else if (peek() === $t) {
11643 this.state.peek = $TAB;
11644 }
11645 else if (peek() === $b) {
11646 this.state.peek = $BSPACE;
11647 }
11648 else if (peek() === $f) {
11649 this.state.peek = $FF;
11650 }
11651 // Now consider more complex sequences
11652 else if (peek() === $u) {
11653 // Unicode code-point sequence
11654 this.advanceState(this.internalState); // advance past the `u` char
11655 if (peek() === $LBRACE) {
11656 // Variable length Unicode, e.g. `\x{123}`
11657 this.advanceState(this.internalState); // advance past the `{` char
11658 // Advance past the variable number of hex digits until we hit a `}` char
11659 const digitStart = this.clone();
11660 let length = 0;
11661 while (peek() !== $RBRACE) {
11662 this.advanceState(this.internalState);
11663 length++;
11664 }
11665 this.state.peek = this.decodeHexDigits(digitStart, length);
11666 }
11667 else {
11668 // Fixed length Unicode, e.g. `\u1234`
11669 const digitStart = this.clone();
11670 this.advanceState(this.internalState);
11671 this.advanceState(this.internalState);
11672 this.advanceState(this.internalState);
11673 this.state.peek = this.decodeHexDigits(digitStart, 4);
11674 }
11675 }
11676 else if (peek() === $x) {
11677 // Hex char code, e.g. `\x2F`
11678 this.advanceState(this.internalState); // advance past the `x` char
11679 const digitStart = this.clone();
11680 this.advanceState(this.internalState);
11681 this.state.peek = this.decodeHexDigits(digitStart, 2);
11682 }
11683 else if (isOctalDigit(peek())) {
11684 // Octal char code, e.g. `\012`,
11685 let octal = '';
11686 let length = 0;
11687 let previous = this.clone();
11688 while (isOctalDigit(peek()) && length < 3) {
11689 previous = this.clone();
11690 octal += String.fromCodePoint(peek());
11691 this.advanceState(this.internalState);
11692 length++;
11693 }
11694 this.state.peek = parseInt(octal, 8);
11695 // Backup one char
11696 this.internalState = previous.internalState;
11697 }
11698 else if (isNewLine(this.internalState.peek)) {
11699 // Line continuation `\` followed by a new line
11700 this.advanceState(this.internalState); // advance over the newline
11701 this.state = this.internalState;
11702 }
11703 else {
11704 // If none of the `if` blocks were executed then we just have an escaped normal character.
11705 // In that case we just, effectively, skip the backslash from the character.
11706 this.state.peek = this.internalState.peek;
11707 }
11708 }
11709 }
11710 decodeHexDigits(start, length) {
11711 const hex = this.input.substr(start.internalState.offset, length);
11712 const charCode = parseInt(hex, 16);
11713 if (!isNaN(charCode)) {
11714 return charCode;
11715 }
11716 else {
11717 start.state = start.internalState;
11718 throw new CursorError('Invalid hexadecimal escape sequence', start);
11719 }
11720 }
11721 }
11722 class CursorError {
11723 constructor(msg, cursor) {
11724 this.msg = msg;
11725 this.cursor = cursor;
11726 }
11727 }
11728
11729 /**
11730 * @license
11731 * Copyright Google LLC All Rights Reserved.
11732 *
11733 * Use of this source code is governed by an MIT-style license that can be
11734 * found in the LICENSE file at https://angular.io/license
11735 */
11736 class TreeError extends ParseError {
11737 constructor(elementName, span, msg) {
11738 super(span, msg);
11739 this.elementName = elementName;
11740 }
11741 static create(elementName, span, msg) {
11742 return new TreeError(elementName, span, msg);
11743 }
11744 }
11745 class ParseTreeResult {
11746 constructor(rootNodes, errors) {
11747 this.rootNodes = rootNodes;
11748 this.errors = errors;
11749 }
11750 }
11751 class Parser$1 {
11752 constructor(getTagDefinition) {
11753 this.getTagDefinition = getTagDefinition;
11754 }
11755 parse(source, url, options) {
11756 const tokenizeResult = tokenize(source, url, this.getTagDefinition, options);
11757 const parser = new _TreeBuilder(tokenizeResult.tokens, this.getTagDefinition);
11758 parser.build();
11759 return new ParseTreeResult(parser.rootNodes, tokenizeResult.errors.concat(parser.errors));
11760 }
11761 }
11762 class _TreeBuilder {
11763 constructor(tokens, getTagDefinition) {
11764 this.tokens = tokens;
11765 this.getTagDefinition = getTagDefinition;
11766 this._index = -1;
11767 this._elementStack = [];
11768 this.rootNodes = [];
11769 this.errors = [];
11770 this._advance();
11771 }
11772 build() {
11773 while (this._peek.type !== 24 /* EOF */) {
11774 if (this._peek.type === 0 /* TAG_OPEN_START */ ||
11775 this._peek.type === 4 /* INCOMPLETE_TAG_OPEN */) {
11776 this._consumeStartTag(this._advance());
11777 }
11778 else if (this._peek.type === 3 /* TAG_CLOSE */) {
11779 this._consumeEndTag(this._advance());
11780 }
11781 else if (this._peek.type === 12 /* CDATA_START */) {
11782 this._closeVoidElement();
11783 this._consumeCdata(this._advance());
11784 }
11785 else if (this._peek.type === 10 /* COMMENT_START */) {
11786 this._closeVoidElement();
11787 this._consumeComment(this._advance());
11788 }
11789 else if (this._peek.type === 5 /* TEXT */ || this._peek.type === 7 /* RAW_TEXT */ ||
11790 this._peek.type === 6 /* ESCAPABLE_RAW_TEXT */) {
11791 this._closeVoidElement();
11792 this._consumeText(this._advance());
11793 }
11794 else if (this._peek.type === 19 /* EXPANSION_FORM_START */) {
11795 this._consumeExpansion(this._advance());
11796 }
11797 else {
11798 // Skip all other tokens...
11799 this._advance();
11800 }
11801 }
11802 }
11803 _advance() {
11804 const prev = this._peek;
11805 if (this._index < this.tokens.length - 1) {
11806 // Note: there is always an EOF token at the end
11807 this._index++;
11808 }
11809 this._peek = this.tokens[this._index];
11810 return prev;
11811 }
11812 _advanceIf(type) {
11813 if (this._peek.type === type) {
11814 return this._advance();
11815 }
11816 return null;
11817 }
11818 _consumeCdata(_startToken) {
11819 this._consumeText(this._advance());
11820 this._advanceIf(13 /* CDATA_END */);
11821 }
11822 _consumeComment(token) {
11823 const text = this._advanceIf(7 /* RAW_TEXT */);
11824 this._advanceIf(11 /* COMMENT_END */);
11825 const value = text != null ? text.parts[0].trim() : null;
11826 this._addToParent(new Comment(value, token.sourceSpan));
11827 }
11828 _consumeExpansion(token) {
11829 const switchValue = this._advance();
11830 const type = this._advance();
11831 const cases = [];
11832 // read =
11833 while (this._peek.type === 20 /* EXPANSION_CASE_VALUE */) {
11834 const expCase = this._parseExpansionCase();
11835 if (!expCase)
11836 return; // error
11837 cases.push(expCase);
11838 }
11839 // read the final }
11840 if (this._peek.type !== 23 /* EXPANSION_FORM_END */) {
11841 this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '}'.`));
11842 return;
11843 }
11844 const sourceSpan = new ParseSourceSpan(token.sourceSpan.start, this._peek.sourceSpan.end, token.sourceSpan.fullStart);
11845 this._addToParent(new Expansion(switchValue.parts[0], type.parts[0], cases, sourceSpan, switchValue.sourceSpan));
11846 this._advance();
11847 }
11848 _parseExpansionCase() {
11849 const value = this._advance();
11850 // read {
11851 if (this._peek.type !== 21 /* EXPANSION_CASE_EXP_START */) {
11852 this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '{'.`));
11853 return null;
11854 }
11855 // read until }
11856 const start = this._advance();
11857 const exp = this._collectExpansionExpTokens(start);
11858 if (!exp)
11859 return null;
11860 const end = this._advance();
11861 exp.push({ type: 24 /* EOF */, parts: [], sourceSpan: end.sourceSpan });
11862 // parse everything in between { and }
11863 const expansionCaseParser = new _TreeBuilder(exp, this.getTagDefinition);
11864 expansionCaseParser.build();
11865 if (expansionCaseParser.errors.length > 0) {
11866 this.errors = this.errors.concat(expansionCaseParser.errors);
11867 return null;
11868 }
11869 const sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end, value.sourceSpan.fullStart);
11870 const expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end, start.sourceSpan.fullStart);
11871 return new ExpansionCase(value.parts[0], expansionCaseParser.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan);
11872 }
11873 _collectExpansionExpTokens(start) {
11874 const exp = [];
11875 const expansionFormStack = [21 /* EXPANSION_CASE_EXP_START */];
11876 while (true) {
11877 if (this._peek.type === 19 /* EXPANSION_FORM_START */ ||
11878 this._peek.type === 21 /* EXPANSION_CASE_EXP_START */) {
11879 expansionFormStack.push(this._peek.type);
11880 }
11881 if (this._peek.type === 22 /* EXPANSION_CASE_EXP_END */) {
11882 if (lastOnStack(expansionFormStack, 21 /* EXPANSION_CASE_EXP_START */)) {
11883 expansionFormStack.pop();
11884 if (expansionFormStack.length === 0)
11885 return exp;
11886 }
11887 else {
11888 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
11889 return null;
11890 }
11891 }
11892 if (this._peek.type === 23 /* EXPANSION_FORM_END */) {
11893 if (lastOnStack(expansionFormStack, 19 /* EXPANSION_FORM_START */)) {
11894 expansionFormStack.pop();
11895 }
11896 else {
11897 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
11898 return null;
11899 }
11900 }
11901 if (this._peek.type === 24 /* EOF */) {
11902 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
11903 return null;
11904 }
11905 exp.push(this._advance());
11906 }
11907 }
11908 _consumeText(token) {
11909 const tokens = [token];
11910 const startSpan = token.sourceSpan;
11911 let text = token.parts[0];
11912 if (text.length > 0 && text[0] === '\n') {
11913 const parent = this._getParentElement();
11914 if (parent != null && parent.children.length === 0 &&
11915 this.getTagDefinition(parent.name).ignoreFirstLf) {
11916 text = text.substring(1);
11917 tokens[0] = { type: token.type, sourceSpan: token.sourceSpan, parts: [text] };
11918 }
11919 }
11920 while (this._peek.type === 8 /* INTERPOLATION */ || this._peek.type === 5 /* TEXT */ ||
11921 this._peek.type === 9 /* ENCODED_ENTITY */) {
11922 token = this._advance();
11923 tokens.push(token);
11924 if (token.type === 8 /* INTERPOLATION */) {
11925 // For backward compatibility we decode HTML entities that appear in interpolation
11926 // expressions. This is arguably a bug, but it could be a considerable breaking change to
11927 // fix it. It should be addressed in a larger project to refactor the entire parser/lexer
11928 // chain after View Engine has been removed.
11929 text += token.parts.join('').replace(/&([^;]+);/g, decodeEntity);
11930 }
11931 else if (token.type === 9 /* ENCODED_ENTITY */) {
11932 text += token.parts[0];
11933 }
11934 else {
11935 text += token.parts.join('');
11936 }
11937 }
11938 if (text.length > 0) {
11939 const endSpan = token.sourceSpan;
11940 this._addToParent(new Text(text, new ParseSourceSpan(startSpan.start, endSpan.end, startSpan.fullStart, startSpan.details), tokens));
11941 }
11942 }
11943 _closeVoidElement() {
11944 const el = this._getParentElement();
11945 if (el && this.getTagDefinition(el.name).isVoid) {
11946 this._elementStack.pop();
11947 }
11948 }
11949 _consumeStartTag(startTagToken) {
11950 const [prefix, name] = startTagToken.parts;
11951 const attrs = [];
11952 while (this._peek.type === 14 /* ATTR_NAME */) {
11953 attrs.push(this._consumeAttr(this._advance()));
11954 }
11955 const fullName = this._getElementFullName(prefix, name, this._getParentElement());
11956 let selfClosing = false;
11957 // Note: There could have been a tokenizer error
11958 // so that we don't get a token for the end tag...
11959 if (this._peek.type === 2 /* TAG_OPEN_END_VOID */) {
11960 this._advance();
11961 selfClosing = true;
11962 const tagDef = this.getTagDefinition(fullName);
11963 if (!(tagDef.canSelfClose || getNsPrefix(fullName) !== null || tagDef.isVoid)) {
11964 this.errors.push(TreeError.create(fullName, startTagToken.sourceSpan, `Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`));
11965 }
11966 }
11967 else if (this._peek.type === 1 /* TAG_OPEN_END */) {
11968 this._advance();
11969 selfClosing = false;
11970 }
11971 const end = this._peek.sourceSpan.fullStart;
11972 const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
11973 // Create a separate `startSpan` because `span` will be modified when there is an `end` span.
11974 const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
11975 const el = new Element(fullName, attrs, [], span, startSpan, undefined);
11976 this._pushElement(el);
11977 if (selfClosing) {
11978 // Elements that are self-closed have their `endSourceSpan` set to the full span, as the
11979 // element start tag also represents the end tag.
11980 this._popElement(fullName, span);
11981 }
11982 else if (startTagToken.type === 4 /* INCOMPLETE_TAG_OPEN */) {
11983 // We already know the opening tag is not complete, so it is unlikely it has a corresponding
11984 // close tag. Let's optimistically parse it as a full element and emit an error.
11985 this._popElement(fullName, null);
11986 this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
11987 }
11988 }
11989 _pushElement(el) {
11990 const parentEl = this._getParentElement();
11991 if (parentEl && this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) {
11992 this._elementStack.pop();
11993 }
11994 this._addToParent(el);
11995 this._elementStack.push(el);
11996 }
11997 _consumeEndTag(endTagToken) {
11998 const fullName = this._getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
11999 if (this.getTagDefinition(fullName).isVoid) {
12000 this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`));
12001 }
12002 else if (!this._popElement(fullName, endTagToken.sourceSpan)) {
12003 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`;
12004 this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg));
12005 }
12006 }
12007 /**
12008 * Closes the nearest element with the tag name `fullName` in the parse tree.
12009 * `endSourceSpan` is the span of the closing tag, or null if the element does
12010 * not have a closing tag (for example, this happens when an incomplete
12011 * opening tag is recovered).
12012 */
12013 _popElement(fullName, endSourceSpan) {
12014 let unexpectedCloseTagDetected = false;
12015 for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) {
12016 const el = this._elementStack[stackIndex];
12017 if (el.name === fullName) {
12018 // Record the parse span with the element that is being closed. Any elements that are
12019 // removed from the element stack at this point are closed implicitly, so they won't get
12020 // an end source span (as there is no explicit closing element).
12021 el.endSourceSpan = endSourceSpan;
12022 el.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : el.sourceSpan.end;
12023 this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex);
12024 return !unexpectedCloseTagDetected;
12025 }
12026 if (!this.getTagDefinition(el.name).closedByParent) {
12027 // Note that we encountered an unexpected close tag but continue processing the element
12028 // stack so we can assign an `endSourceSpan` if there is a corresponding start tag for this
12029 // end tag in the stack.
12030 unexpectedCloseTagDetected = true;
12031 }
12032 }
12033 return false;
12034 }
12035 _consumeAttr(attrName) {
12036 const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
12037 let attrEnd = attrName.sourceSpan.end;
12038 // Consume any quote
12039 if (this._peek.type === 15 /* ATTR_QUOTE */) {
12040 this._advance();
12041 }
12042 // Consume the attribute value
12043 let value = '';
12044 const valueTokens = [];
12045 let valueStartSpan = undefined;
12046 let valueEnd = undefined;
12047 // NOTE: We need to use a new variable `nextTokenType` here to hide the actual type of
12048 // `_peek.type` from TS. Otherwise TS will narrow the type of `_peek.type` preventing it from
12049 // being able to consider `ATTR_VALUE_INTERPOLATION` as an option. This is because TS is not
12050 // able to see that `_advance()` will actually mutate `_peek`.
12051 const nextTokenType = this._peek.type;
12052 if (nextTokenType === 16 /* ATTR_VALUE_TEXT */) {
12053 valueStartSpan = this._peek.sourceSpan;
12054 valueEnd = this._peek.sourceSpan.end;
12055 while (this._peek.type === 16 /* ATTR_VALUE_TEXT */ ||
12056 this._peek.type === 17 /* ATTR_VALUE_INTERPOLATION */ ||
12057 this._peek.type === 9 /* ENCODED_ENTITY */) {
12058 const valueToken = this._advance();
12059 valueTokens.push(valueToken);
12060 if (valueToken.type === 17 /* ATTR_VALUE_INTERPOLATION */) {
12061 // For backward compatibility we decode HTML entities that appear in interpolation
12062 // expressions. This is arguably a bug, but it could be a considerable breaking change to
12063 // fix it. It should be addressed in a larger project to refactor the entire parser/lexer
12064 // chain after View Engine has been removed.
12065 value += valueToken.parts.join('').replace(/&([^;]+);/g, decodeEntity);
12066 }
12067 else if (valueToken.type === 9 /* ENCODED_ENTITY */) {
12068 value += valueToken.parts[0];
12069 }
12070 else {
12071 value += valueToken.parts.join('');
12072 }
12073 valueEnd = attrEnd = valueToken.sourceSpan.end;
12074 }
12075 }
12076 // Consume any quote
12077 if (this._peek.type === 15 /* ATTR_QUOTE */) {
12078 const quoteToken = this._advance();
12079 attrEnd = quoteToken.sourceSpan.end;
12080 }
12081 const valueSpan = valueStartSpan && valueEnd &&
12082 new ParseSourceSpan(valueStartSpan.start, valueEnd, valueStartSpan.fullStart);
12083 return new Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, attrEnd, attrName.sourceSpan.fullStart), attrName.sourceSpan, valueSpan, valueTokens.length > 0 ? valueTokens : undefined, undefined);
12084 }
12085 _getParentElement() {
12086 return this._elementStack.length > 0 ? this._elementStack[this._elementStack.length - 1] : null;
12087 }
12088 _addToParent(node) {
12089 const parent = this._getParentElement();
12090 if (parent != null) {
12091 parent.children.push(node);
12092 }
12093 else {
12094 this.rootNodes.push(node);
12095 }
12096 }
12097 _getElementFullName(prefix, localName, parentElement) {
12098 if (prefix === '') {
12099 prefix = this.getTagDefinition(localName).implicitNamespacePrefix || '';
12100 if (prefix === '' && parentElement != null) {
12101 const parentTagName = splitNsName(parentElement.name)[1];
12102 const parentTagDefinition = this.getTagDefinition(parentTagName);
12103 if (!parentTagDefinition.preventNamespaceInheritance) {
12104 prefix = getNsPrefix(parentElement.name);
12105 }
12106 }
12107 }
12108 return mergeNsAndName(prefix, localName);
12109 }
12110 }
12111 function lastOnStack(stack, element) {
12112 return stack.length > 0 && stack[stack.length - 1] === element;
12113 }
12114 /**
12115 * Decode the `entity` string, which we believe is the contents of an HTML entity.
12116 *
12117 * If the string is not actually a valid/known entity then just return the original `match` string.
12118 */
12119 function decodeEntity(match, entity) {
12120 if (NAMED_ENTITIES[entity] !== undefined) {
12121 return NAMED_ENTITIES[entity] || match;
12122 }
12123 if (/^#x[a-f0-9]+$/i.test(entity)) {
12124 return String.fromCodePoint(parseInt(entity.slice(2), 16));
12125 }
12126 if (/^#\d+$/.test(entity)) {
12127 return String.fromCodePoint(parseInt(entity.slice(1), 10));
12128 }
12129 return match;
12130 }
12131
12132 /**
12133 * @license
12134 * Copyright Google LLC All Rights Reserved.
12135 *
12136 * Use of this source code is governed by an MIT-style license that can be
12137 * found in the LICENSE file at https://angular.io/license
12138 */
12139 const PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces';
12140 const SKIP_WS_TRIM_TAGS = new Set(['pre', 'template', 'textarea', 'script', 'style']);
12141 // Equivalent to \s with \u00a0 (non-breaking space) excluded.
12142 // Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
12143 const WS_CHARS = ' \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff';
12144 const NO_WS_REGEXP = new RegExp(`[^${WS_CHARS}]`);
12145 const WS_REPLACE_REGEXP = new RegExp(`[${WS_CHARS}]{2,}`, 'g');
12146 function hasPreserveWhitespacesAttr(attrs) {
12147 return attrs.some((attr) => attr.name === PRESERVE_WS_ATTR_NAME);
12148 }
12149 /**
12150 * Angular Dart introduced &ngsp; as a placeholder for non-removable space, see:
12151 * https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32
12152 * In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character
12153 * and later on replaced by a space. We are re-implementing the same idea here.
12154 */
12155 function replaceNgsp(value) {
12156 // lexer is replacing the &ngsp; pseudo-entity with NGSP_UNICODE
12157 return value.replace(new RegExp(NGSP_UNICODE, 'g'), ' ');
12158 }
12159 /**
12160 * This visitor can walk HTML parse tree and remove / trim text nodes using the following rules:
12161 * - consider spaces, tabs and new lines as whitespace characters;
12162 * - drop text nodes consisting of whitespace characters only;
12163 * - for all other text nodes replace consecutive whitespace characters with one space;
12164 * - convert &ngsp; pseudo-entity to a single space;
12165 *
12166 * Removal and trimming of whitespaces have positive performance impact (less code to generate
12167 * while compiling templates, faster view creation). At the same time it can be "destructive"
12168 * in some cases (whitespaces can influence layout). Because of the potential of breaking layout
12169 * this visitor is not activated by default in Angular 5 and people need to explicitly opt-in for
12170 * whitespace removal. The default option for whitespace removal will be revisited in Angular 6
12171 * and might be changed to "on" by default.
12172 */
12173 class WhitespaceVisitor {
12174 visitElement(element, context) {
12175 if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
12176 // don't descent into elements where we need to preserve whitespaces
12177 // but still visit all attributes to eliminate one used as a market to preserve WS
12178 return new Element(element.name, visitAll(this, element.attrs), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
12179 }
12180 return new Element(element.name, element.attrs, visitAllWithSiblings(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
12181 }
12182 visitAttribute(attribute, context) {
12183 return attribute.name !== PRESERVE_WS_ATTR_NAME ? attribute : null;
12184 }
12185 visitText(text, context) {
12186 const isNotBlank = text.value.match(NO_WS_REGEXP);
12187 const hasExpansionSibling = context &&
12188 (context.prev instanceof Expansion || context.next instanceof Expansion);
12189 if (isNotBlank || hasExpansionSibling) {
12190 // Process the whitespace in the tokens of this Text node
12191 const tokens = text.tokens.map(token => token.type === 5 /* TEXT */ ? createWhitespaceProcessedTextToken(token) : token);
12192 // Process the whitespace of the value of this Text node
12193 const value = processWhitespace(text.value);
12194 return new Text(value, text.sourceSpan, tokens, text.i18n);
12195 }
12196 return null;
12197 }
12198 visitComment(comment, context) {
12199 return comment;
12200 }
12201 visitExpansion(expansion, context) {
12202 return expansion;
12203 }
12204 visitExpansionCase(expansionCase, context) {
12205 return expansionCase;
12206 }
12207 }
12208 function createWhitespaceProcessedTextToken({ type, parts, sourceSpan }) {
12209 return { type, parts: [processWhitespace(parts[0])], sourceSpan };
12210 }
12211 function processWhitespace(text) {
12212 return replaceNgsp(text).replace(WS_REPLACE_REGEXP, ' ');
12213 }
12214 function visitAllWithSiblings(visitor, nodes) {
12215 const result = [];
12216 nodes.forEach((ast, i) => {
12217 const context = { prev: nodes[i - 1], next: nodes[i + 1] };
12218 const astResult = ast.visit(visitor, context);
12219 if (astResult) {
12220 result.push(astResult);
12221 }
12222 });
12223 return result;
12224 }
12225
12226 /**
12227 * @license
12228 * Copyright Google LLC All Rights Reserved.
12229 *
12230 * Use of this source code is governed by an MIT-style license that can be
12231 * found in the LICENSE file at https://angular.io/license
12232 */
12233 var ProviderAstType;
12234 (function (ProviderAstType) {
12235 ProviderAstType[ProviderAstType["PublicService"] = 0] = "PublicService";
12236 ProviderAstType[ProviderAstType["PrivateService"] = 1] = "PrivateService";
12237 ProviderAstType[ProviderAstType["Component"] = 2] = "Component";
12238 ProviderAstType[ProviderAstType["Directive"] = 3] = "Directive";
12239 ProviderAstType[ProviderAstType["Builtin"] = 4] = "Builtin";
12240 })(ProviderAstType || (ProviderAstType = {}));
12241
12242 /**
12243 * @license
12244 * Copyright Google LLC All Rights Reserved.
12245 *
12246 * Use of this source code is governed by an MIT-style license that can be
12247 * found in the LICENSE file at https://angular.io/license
12248 */
12249 function isStyleUrlResolvable(url) {
12250 if (url == null || url.length === 0 || url[0] == '/')
12251 return false;
12252 const schemeMatch = url.match(URL_WITH_SCHEMA_REGEXP);
12253 return schemeMatch === null || schemeMatch[1] == 'package' || schemeMatch[1] == 'asset';
12254 }
12255 const URL_WITH_SCHEMA_REGEXP = /^([^:/?#]+):/;
12256
12257 /**
12258 * @license
12259 * Copyright Google LLC All Rights Reserved.
12260 *
12261 * Use of this source code is governed by an MIT-style license that can be
12262 * found in the LICENSE file at https://angular.io/license
12263 */
12264 const PROPERTY_PARTS_SEPARATOR = '.';
12265 const ATTRIBUTE_PREFIX = 'attr';
12266 const CLASS_PREFIX = 'class';
12267 const STYLE_PREFIX = 'style';
12268 const TEMPLATE_ATTR_PREFIX$1 = '*';
12269 const ANIMATE_PROP_PREFIX = 'animate-';
12270 /**
12271 * Parses bindings in templates and in the directive host area.
12272 */
12273 class BindingParser {
12274 constructor(_exprParser, _interpolationConfig, _schemaRegistry, pipes, errors) {
12275 this._exprParser = _exprParser;
12276 this._interpolationConfig = _interpolationConfig;
12277 this._schemaRegistry = _schemaRegistry;
12278 this.errors = errors;
12279 this.pipesByName = null;
12280 this._usedPipes = new Map();
12281 // When the `pipes` parameter is `null`, do not check for used pipes
12282 // This is used in IVY when we might not know the available pipes at compile time
12283 if (pipes) {
12284 const pipesByName = new Map();
12285 pipes.forEach(pipe => pipesByName.set(pipe.name, pipe));
12286 this.pipesByName = pipesByName;
12287 }
12288 }
12289 get interpolationConfig() {
12290 return this._interpolationConfig;
12291 }
12292 getUsedPipes() {
12293 return Array.from(this._usedPipes.values());
12294 }
12295 createBoundHostProperties(dirMeta, sourceSpan) {
12296 if (dirMeta.hostProperties) {
12297 const boundProps = [];
12298 Object.keys(dirMeta.hostProperties).forEach(propName => {
12299 const expression = dirMeta.hostProperties[propName];
12300 if (typeof expression === 'string') {
12301 this.parsePropertyBinding(propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [],
12302 // Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the
12303 // sourceSpan, as it represents the sourceSpan of the host itself rather than the
12304 // source of the host binding (which doesn't exist in the template). Regardless,
12305 // neither of these values are used in Ivy but are only here to satisfy the function
12306 // signature. This should likely be refactored in the future so that `sourceSpan`
12307 // isn't being used inaccurately.
12308 boundProps, sourceSpan);
12309 }
12310 else {
12311 this._reportError(`Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
12312 }
12313 });
12314 return boundProps;
12315 }
12316 return null;
12317 }
12318 createDirectiveHostPropertyAsts(dirMeta, elementSelector, sourceSpan) {
12319 const boundProps = this.createBoundHostProperties(dirMeta, sourceSpan);
12320 return boundProps &&
12321 boundProps.map((prop) => this.createBoundElementProperty(elementSelector, prop));
12322 }
12323 createDirectiveHostEventAsts(dirMeta, sourceSpan) {
12324 if (dirMeta.hostListeners) {
12325 const targetEvents = [];
12326 Object.keys(dirMeta.hostListeners).forEach(propName => {
12327 const expression = dirMeta.hostListeners[propName];
12328 if (typeof expression === 'string') {
12329 // Use the `sourceSpan` for `keySpan` and `handlerSpan`. This isn't really accurate, but
12330 // neither is the `sourceSpan`, as it represents the `sourceSpan` of the host itself
12331 // rather than the source of the host binding (which doesn't exist in the template).
12332 // Regardless, neither of these values are used in Ivy but are only here to satisfy the
12333 // function signature. This should likely be refactored in the future so that `sourceSpan`
12334 // isn't being used inaccurately.
12335 this.parseEvent(propName, expression, sourceSpan, sourceSpan, [], targetEvents, sourceSpan);
12336 }
12337 else {
12338 this._reportError(`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
12339 }
12340 });
12341 return targetEvents;
12342 }
12343 return null;
12344 }
12345 parseInterpolation(value, sourceSpan) {
12346 const sourceInfo = sourceSpan.start.toString();
12347 const absoluteOffset = sourceSpan.fullStart.offset;
12348 try {
12349 const ast = this._exprParser.parseInterpolation(value, sourceInfo, absoluteOffset, this._interpolationConfig);
12350 if (ast)
12351 this._reportExpressionParserErrors(ast.errors, sourceSpan);
12352 this._checkPipes(ast, sourceSpan);
12353 return ast;
12354 }
12355 catch (e) {
12356 this._reportError(`${e}`, sourceSpan);
12357 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
12358 }
12359 }
12360 /**
12361 * Similar to `parseInterpolation`, but treats the provided string as a single expression
12362 * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).
12363 * This is used for parsing the switch expression in ICUs.
12364 */
12365 parseInterpolationExpression(expression, sourceSpan) {
12366 const sourceInfo = sourceSpan.start.toString();
12367 const absoluteOffset = sourceSpan.start.offset;
12368 try {
12369 const ast = this._exprParser.parseInterpolationExpression(expression, sourceInfo, absoluteOffset);
12370 if (ast)
12371 this._reportExpressionParserErrors(ast.errors, sourceSpan);
12372 this._checkPipes(ast, sourceSpan);
12373 return ast;
12374 }
12375 catch (e) {
12376 this._reportError(`${e}`, sourceSpan);
12377 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
12378 }
12379 }
12380 /**
12381 * Parses the bindings in a microsyntax expression, and converts them to
12382 * `ParsedProperty` or `ParsedVariable`.
12383 *
12384 * @param tplKey template binding name
12385 * @param tplValue template binding value
12386 * @param sourceSpan span of template binding relative to entire the template
12387 * @param absoluteValueOffset start of the tplValue relative to the entire template
12388 * @param targetMatchableAttrs potential attributes to match in the template
12389 * @param targetProps target property bindings in the template
12390 * @param targetVars target variables in the template
12391 */
12392 parseInlineTemplateBinding(tplKey, tplValue, sourceSpan, absoluteValueOffset, targetMatchableAttrs, targetProps, targetVars, isIvyAst) {
12393 const absoluteKeyOffset = sourceSpan.start.offset + TEMPLATE_ATTR_PREFIX$1.length;
12394 const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset);
12395 for (const binding of bindings) {
12396 // sourceSpan is for the entire HTML attribute. bindingSpan is for a particular
12397 // binding within the microsyntax expression so it's more narrow than sourceSpan.
12398 const bindingSpan = moveParseSourceSpan(sourceSpan, binding.sourceSpan);
12399 const key = binding.key.source;
12400 const keySpan = moveParseSourceSpan(sourceSpan, binding.key.span);
12401 if (binding instanceof VariableBinding) {
12402 const value = binding.value ? binding.value.source : '$implicit';
12403 const valueSpan = binding.value ? moveParseSourceSpan(sourceSpan, binding.value.span) : undefined;
12404 targetVars.push(new ParsedVariable(key, value, bindingSpan, keySpan, valueSpan));
12405 }
12406 else if (binding.value) {
12407 const srcSpan = isIvyAst ? bindingSpan : sourceSpan;
12408 const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);
12409 this._parsePropertyAst(key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
12410 }
12411 else {
12412 targetMatchableAttrs.push([key, '' /* value */]);
12413 // Since this is a literal attribute with no RHS, source span should be
12414 // just the key span.
12415 this.parseLiteralAttr(key, null /* value */, keySpan, absoluteValueOffset, undefined /* valueSpan */, targetMatchableAttrs, targetProps, keySpan);
12416 }
12417 }
12418 }
12419 /**
12420 * Parses the bindings in a microsyntax expression, e.g.
12421 * ```
12422 * <tag *tplKey="let value1 = prop; let value2 = localVar">
12423 * ```
12424 *
12425 * @param tplKey template binding name
12426 * @param tplValue template binding value
12427 * @param sourceSpan span of template binding relative to entire the template
12428 * @param absoluteKeyOffset start of the `tplKey`
12429 * @param absoluteValueOffset start of the `tplValue`
12430 */
12431 _parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset) {
12432 const sourceInfo = sourceSpan.start.toString();
12433 try {
12434 const bindingsResult = this._exprParser.parseTemplateBindings(tplKey, tplValue, sourceInfo, absoluteKeyOffset, absoluteValueOffset);
12435 this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
12436 bindingsResult.templateBindings.forEach((binding) => {
12437 if (binding.value instanceof ASTWithSource) {
12438 this._checkPipes(binding.value, sourceSpan);
12439 }
12440 });
12441 bindingsResult.warnings.forEach((warning) => {
12442 this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING);
12443 });
12444 return bindingsResult.templateBindings;
12445 }
12446 catch (e) {
12447 this._reportError(`${e}`, sourceSpan);
12448 return [];
12449 }
12450 }
12451 parseLiteralAttr(name, value, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs,
12452 // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
12453 // have to change This should be required when VE is removed.
12454 targetProps, keySpan) {
12455 if (isAnimationLabel(name)) {
12456 name = name.substring(1);
12457 if (keySpan !== undefined) {
12458 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan$1(keySpan.start.offset + 1, keySpan.end.offset));
12459 }
12460 if (value) {
12461 this._reportError(`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
12462 ` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`, sourceSpan, ParseErrorLevel.ERROR);
12463 }
12464 this._parseAnimation(name, value, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
12465 }
12466 else {
12467 targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));
12468 }
12469 }
12470 parsePropertyBinding(name, expression, isHost, sourceSpan, absoluteOffset, valueSpan,
12471 // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
12472 // have to change This should be required when VE is removed.
12473 targetMatchableAttrs, targetProps, keySpan) {
12474 if (name.length === 0) {
12475 this._reportError(`Property name is missing in binding`, sourceSpan);
12476 }
12477 let isAnimationProp = false;
12478 if (name.startsWith(ANIMATE_PROP_PREFIX)) {
12479 isAnimationProp = true;
12480 name = name.substring(ANIMATE_PROP_PREFIX.length);
12481 if (keySpan !== undefined) {
12482 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan$1(keySpan.start.offset + ANIMATE_PROP_PREFIX.length, keySpan.end.offset));
12483 }
12484 }
12485 else if (isAnimationLabel(name)) {
12486 isAnimationProp = true;
12487 name = name.substring(1);
12488 if (keySpan !== undefined) {
12489 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan$1(keySpan.start.offset + 1, keySpan.end.offset));
12490 }
12491 }
12492 if (isAnimationProp) {
12493 this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
12494 }
12495 else {
12496 this._parsePropertyAst(name, this._parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
12497 }
12498 }
12499 parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs,
12500 // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
12501 // have to change This should be required when VE is removed.
12502 targetProps, keySpan) {
12503 const expr = this.parseInterpolation(value, valueSpan || sourceSpan);
12504 if (expr) {
12505 this._parsePropertyAst(name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
12506 return true;
12507 }
12508 return false;
12509 }
12510 _parsePropertyAst(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
12511 targetMatchableAttrs.push([name, ast.source]);
12512 targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
12513 }
12514 _parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
12515 if (name.length === 0) {
12516 this._reportError('Animation trigger is missing', sourceSpan);
12517 }
12518 // This will occur when a @trigger is not paired with an expression.
12519 // For animations it is valid to not have an expression since */void
12520 // states will be applied by angular when the element is attached/detached
12521 const ast = this._parseBinding(expression || 'undefined', false, valueSpan || sourceSpan, absoluteOffset);
12522 targetMatchableAttrs.push([name, ast.source]);
12523 targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan, keySpan, valueSpan));
12524 }
12525 _parseBinding(value, isHostBinding, sourceSpan, absoluteOffset) {
12526 const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown)').toString();
12527 try {
12528 const ast = isHostBinding ?
12529 this._exprParser.parseSimpleBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig) :
12530 this._exprParser.parseBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig);
12531 if (ast)
12532 this._reportExpressionParserErrors(ast.errors, sourceSpan);
12533 this._checkPipes(ast, sourceSpan);
12534 return ast;
12535 }
12536 catch (e) {
12537 this._reportError(`${e}`, sourceSpan);
12538 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
12539 }
12540 }
12541 createBoundElementProperty(elementSelector, boundProp, skipValidation = false, mapPropertyName = true) {
12542 if (boundProp.isAnimation) {
12543 return new BoundElementProperty(boundProp.name, 4 /* Animation */, SecurityContext.NONE, boundProp.expression, null, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
12544 }
12545 let unit = null;
12546 let bindingType = undefined;
12547 let boundPropertyName = null;
12548 const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR);
12549 let securityContexts = undefined;
12550 // Check for special cases (prefix style, attr, class)
12551 if (parts.length > 1) {
12552 if (parts[0] == ATTRIBUTE_PREFIX) {
12553 boundPropertyName = parts.slice(1).join(PROPERTY_PARTS_SEPARATOR);
12554 if (!skipValidation) {
12555 this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, true);
12556 }
12557 securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, boundPropertyName, true);
12558 const nsSeparatorIdx = boundPropertyName.indexOf(':');
12559 if (nsSeparatorIdx > -1) {
12560 const ns = boundPropertyName.substring(0, nsSeparatorIdx);
12561 const name = boundPropertyName.substring(nsSeparatorIdx + 1);
12562 boundPropertyName = mergeNsAndName(ns, name);
12563 }
12564 bindingType = 1 /* Attribute */;
12565 }
12566 else if (parts[0] == CLASS_PREFIX) {
12567 boundPropertyName = parts[1];
12568 bindingType = 2 /* Class */;
12569 securityContexts = [SecurityContext.NONE];
12570 }
12571 else if (parts[0] == STYLE_PREFIX) {
12572 unit = parts.length > 2 ? parts[2] : null;
12573 boundPropertyName = parts[1];
12574 bindingType = 3 /* Style */;
12575 securityContexts = [SecurityContext.STYLE];
12576 }
12577 }
12578 // If not a special case, use the full property name
12579 if (boundPropertyName === null) {
12580 const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);
12581 boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;
12582 securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);
12583 bindingType = 0 /* Property */;
12584 if (!skipValidation) {
12585 this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);
12586 }
12587 }
12588 return new BoundElementProperty(boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
12589 }
12590 // TODO: keySpan should be required but was made optional to avoid changing VE parser.
12591 parseEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
12592 if (name.length === 0) {
12593 this._reportError(`Event name is missing in binding`, sourceSpan);
12594 }
12595 if (isAnimationLabel(name)) {
12596 name = name.substr(1);
12597 if (keySpan !== undefined) {
12598 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan$1(keySpan.start.offset + 1, keySpan.end.offset));
12599 }
12600 this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
12601 }
12602 else {
12603 this._parseRegularEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);
12604 }
12605 }
12606 calcPossibleSecurityContexts(selector, propName, isAttribute) {
12607 const prop = this._schemaRegistry.getMappedPropName(propName);
12608 return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);
12609 }
12610 _parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) {
12611 const matches = splitAtPeriod(name, [name, '']);
12612 const eventName = matches[0];
12613 const phase = matches[1].toLowerCase();
12614 const ast = this._parseAction(expression, handlerSpan);
12615 targetEvents.push(new ParsedEvent(eventName, phase, 1 /* Animation */, ast, sourceSpan, handlerSpan, keySpan));
12616 if (eventName.length === 0) {
12617 this._reportError(`Animation event name is missing in binding`, sourceSpan);
12618 }
12619 if (phase) {
12620 if (phase !== 'start' && phase !== 'done') {
12621 this._reportError(`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, sourceSpan);
12622 }
12623 }
12624 else {
12625 this._reportError(`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, sourceSpan);
12626 }
12627 }
12628 _parseRegularEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
12629 // long format: 'target: eventName'
12630 const [target, eventName] = splitAtColon(name, [null, name]);
12631 const ast = this._parseAction(expression, handlerSpan);
12632 targetMatchableAttrs.push([name, ast.source]);
12633 targetEvents.push(new ParsedEvent(eventName, target, 0 /* Regular */, ast, sourceSpan, handlerSpan, keySpan));
12634 // Don't detect directives for event names for now,
12635 // so don't add the event name to the matchableAttrs
12636 }
12637 _parseAction(value, sourceSpan) {
12638 const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString();
12639 const absoluteOffset = (sourceSpan && sourceSpan.start) ? sourceSpan.start.offset : 0;
12640 try {
12641 const ast = this._exprParser.parseAction(value, sourceInfo, absoluteOffset, this._interpolationConfig);
12642 if (ast) {
12643 this._reportExpressionParserErrors(ast.errors, sourceSpan);
12644 }
12645 if (!ast || ast.ast instanceof EmptyExpr) {
12646 this._reportError(`Empty expressions are not allowed`, sourceSpan);
12647 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
12648 }
12649 this._checkPipes(ast, sourceSpan);
12650 return ast;
12651 }
12652 catch (e) {
12653 this._reportError(`${e}`, sourceSpan);
12654 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
12655 }
12656 }
12657 _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
12658 this.errors.push(new ParseError(sourceSpan, message, level));
12659 }
12660 _reportExpressionParserErrors(errors, sourceSpan) {
12661 for (const error of errors) {
12662 this._reportError(error.message, sourceSpan);
12663 }
12664 }
12665 // Make sure all the used pipes are known in `this.pipesByName`
12666 _checkPipes(ast, sourceSpan) {
12667 if (ast && this.pipesByName) {
12668 const collector = new PipeCollector();
12669 ast.visit(collector);
12670 collector.pipes.forEach((ast, pipeName) => {
12671 const pipeMeta = this.pipesByName.get(pipeName);
12672 if (!pipeMeta) {
12673 this._reportError(`The pipe '${pipeName}' could not be found`, new ParseSourceSpan(sourceSpan.start.moveBy(ast.span.start), sourceSpan.start.moveBy(ast.span.end)));
12674 }
12675 else {
12676 this._usedPipes.set(pipeName, pipeMeta);
12677 }
12678 });
12679 }
12680 }
12681 /**
12682 * @param propName the name of the property / attribute
12683 * @param sourceSpan
12684 * @param isAttr true when binding to an attribute
12685 */
12686 _validatePropertyOrAttributeName(propName, sourceSpan, isAttr) {
12687 const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
12688 this._schemaRegistry.validateProperty(propName);
12689 if (report.error) {
12690 this._reportError(report.msg, sourceSpan, ParseErrorLevel.ERROR);
12691 }
12692 }
12693 }
12694 class PipeCollector extends RecursiveAstVisitor {
12695 constructor() {
12696 super(...arguments);
12697 this.pipes = new Map();
12698 }
12699 visitPipe(ast, context) {
12700 this.pipes.set(ast.name, ast);
12701 ast.exp.visit(this);
12702 this.visitAll(ast.args, context);
12703 return null;
12704 }
12705 }
12706 function isAnimationLabel(name) {
12707 return name[0] == '@';
12708 }
12709 function calcPossibleSecurityContexts(registry, selector, propName, isAttribute) {
12710 const ctxs = [];
12711 CssSelector.parse(selector).forEach((selector) => {
12712 const elementNames = selector.element ? [selector.element] : registry.allKnownElementNames();
12713 const notElementNames = new Set(selector.notSelectors.filter(selector => selector.isElementSelector())
12714 .map((selector) => selector.element));
12715 const possibleElementNames = elementNames.filter(elementName => !notElementNames.has(elementName));
12716 ctxs.push(...possibleElementNames.map(elementName => registry.securityContext(elementName, propName, isAttribute)));
12717 });
12718 return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();
12719 }
12720 /**
12721 * Compute a new ParseSourceSpan based off an original `sourceSpan` by using
12722 * absolute offsets from the specified `absoluteSpan`.
12723 *
12724 * @param sourceSpan original source span
12725 * @param absoluteSpan absolute source span to move to
12726 */
12727 function moveParseSourceSpan(sourceSpan, absoluteSpan) {
12728 // The difference of two absolute offsets provide the relative offset
12729 const startDiff = absoluteSpan.start - sourceSpan.start.offset;
12730 const endDiff = absoluteSpan.end - sourceSpan.end.offset;
12731 return new ParseSourceSpan(sourceSpan.start.moveBy(startDiff), sourceSpan.end.moveBy(endDiff), sourceSpan.fullStart.moveBy(startDiff), sourceSpan.details);
12732 }
12733
12734 /**
12735 * @license
12736 * Copyright Google LLC All Rights Reserved.
12737 *
12738 * Use of this source code is governed by an MIT-style license that can be
12739 * found in the LICENSE file at https://angular.io/license
12740 */
12741 const NG_CONTENT_SELECT_ATTR$1 = 'select';
12742 const LINK_ELEMENT = 'link';
12743 const LINK_STYLE_REL_ATTR = 'rel';
12744 const LINK_STYLE_HREF_ATTR = 'href';
12745 const LINK_STYLE_REL_VALUE = 'stylesheet';
12746 const STYLE_ELEMENT = 'style';
12747 const SCRIPT_ELEMENT = 'script';
12748 const NG_NON_BINDABLE_ATTR = 'ngNonBindable';
12749 const NG_PROJECT_AS = 'ngProjectAs';
12750 function preparseElement(ast) {
12751 let selectAttr = null;
12752 let hrefAttr = null;
12753 let relAttr = null;
12754 let nonBindable = false;
12755 let projectAs = '';
12756 ast.attrs.forEach(attr => {
12757 const lcAttrName = attr.name.toLowerCase();
12758 if (lcAttrName == NG_CONTENT_SELECT_ATTR$1) {
12759 selectAttr = attr.value;
12760 }
12761 else if (lcAttrName == LINK_STYLE_HREF_ATTR) {
12762 hrefAttr = attr.value;
12763 }
12764 else if (lcAttrName == LINK_STYLE_REL_ATTR) {
12765 relAttr = attr.value;
12766 }
12767 else if (attr.name == NG_NON_BINDABLE_ATTR) {
12768 nonBindable = true;
12769 }
12770 else if (attr.name == NG_PROJECT_AS) {
12771 if (attr.value.length > 0) {
12772 projectAs = attr.value;
12773 }
12774 }
12775 });
12776 selectAttr = normalizeNgContentSelect(selectAttr);
12777 const nodeName = ast.name.toLowerCase();
12778 let type = PreparsedElementType.OTHER;
12779 if (isNgContent(nodeName)) {
12780 type = PreparsedElementType.NG_CONTENT;
12781 }
12782 else if (nodeName == STYLE_ELEMENT) {
12783 type = PreparsedElementType.STYLE;
12784 }
12785 else if (nodeName == SCRIPT_ELEMENT) {
12786 type = PreparsedElementType.SCRIPT;
12787 }
12788 else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
12789 type = PreparsedElementType.STYLESHEET;
12790 }
12791 return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable, projectAs);
12792 }
12793 var PreparsedElementType;
12794 (function (PreparsedElementType) {
12795 PreparsedElementType[PreparsedElementType["NG_CONTENT"] = 0] = "NG_CONTENT";
12796 PreparsedElementType[PreparsedElementType["STYLE"] = 1] = "STYLE";
12797 PreparsedElementType[PreparsedElementType["STYLESHEET"] = 2] = "STYLESHEET";
12798 PreparsedElementType[PreparsedElementType["SCRIPT"] = 3] = "SCRIPT";
12799 PreparsedElementType[PreparsedElementType["OTHER"] = 4] = "OTHER";
12800 })(PreparsedElementType || (PreparsedElementType = {}));
12801 class PreparsedElement {
12802 constructor(type, selectAttr, hrefAttr, nonBindable, projectAs) {
12803 this.type = type;
12804 this.selectAttr = selectAttr;
12805 this.hrefAttr = hrefAttr;
12806 this.nonBindable = nonBindable;
12807 this.projectAs = projectAs;
12808 }
12809 }
12810 function normalizeNgContentSelect(selectAttr) {
12811 if (selectAttr === null || selectAttr.length === 0) {
12812 return '*';
12813 }
12814 return selectAttr;
12815 }
12816
12817 /**
12818 * @license
12819 * Copyright Google LLC All Rights Reserved.
12820 *
12821 * Use of this source code is governed by an MIT-style license that can be
12822 * found in the LICENSE file at https://angular.io/license
12823 */
12824 class ElementContext {
12825 constructor(isTemplateElement, _ngContentIndexMatcher, _wildcardNgContentIndex, providerContext) {
12826 this.isTemplateElement = isTemplateElement;
12827 this._ngContentIndexMatcher = _ngContentIndexMatcher;
12828 this._wildcardNgContentIndex = _wildcardNgContentIndex;
12829 this.providerContext = providerContext;
12830 }
12831 static create(isTemplateElement, directives, providerContext) {
12832 const matcher = new SelectorMatcher();
12833 let wildcardNgContentIndex = null;
12834 const component = directives.find(directive => directive.directive.isComponent);
12835 if (component) {
12836 const ngContentSelectors = component.directive.template.ngContentSelectors;
12837 for (let i = 0; i < ngContentSelectors.length; i++) {
12838 const selector = ngContentSelectors[i];
12839 if (selector === '*') {
12840 wildcardNgContentIndex = i;
12841 }
12842 else {
12843 matcher.addSelectables(CssSelector.parse(ngContentSelectors[i]), i);
12844 }
12845 }
12846 }
12847 return new ElementContext(isTemplateElement, matcher, wildcardNgContentIndex, providerContext);
12848 }
12849 findNgContentIndex(selector) {
12850 const ngContentIndices = [];
12851 this._ngContentIndexMatcher.match(selector, (selector, ngContentIndex) => {
12852 ngContentIndices.push(ngContentIndex);
12853 });
12854 ngContentIndices.sort();
12855 if (this._wildcardNgContentIndex != null) {
12856 ngContentIndices.push(this._wildcardNgContentIndex);
12857 }
12858 return ngContentIndices.length > 0 ? ngContentIndices[0] : null;
12859 }
12860 }
12861 new ElementContext(true, new SelectorMatcher(), null, null);
12862 function isEmptyExpression(ast) {
12863 if (ast instanceof ASTWithSource) {
12864 ast = ast.ast;
12865 }
12866 return ast instanceof EmptyExpr;
12867 }
12868
12869 /**
12870 * @license
12871 * Copyright Google LLC All Rights Reserved.
12872 *
12873 * Use of this source code is governed by an MIT-style license that can be
12874 * found in the LICENSE file at https://angular.io/license
12875 */
12876 /**
12877 * Parses string representation of a style and converts it into object literal.
12878 *
12879 * @param value string representation of style as used in the `style` attribute in HTML.
12880 * Example: `color: red; height: auto`.
12881 * @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
12882 * 'auto']`
12883 */
12884 function parse(value) {
12885 // we use a string array here instead of a string map
12886 // because a string-map is not guaranteed to retain the
12887 // order of the entries whereas a string array can be
12888 // constructed in a [key, value, key, value] format.
12889 const styles = [];
12890 let i = 0;
12891 let parenDepth = 0;
12892 let quote = 0 /* QuoteNone */;
12893 let valueStart = 0;
12894 let propStart = 0;
12895 let currentProp = null;
12896 let valueHasQuotes = false;
12897 while (i < value.length) {
12898 const token = value.charCodeAt(i++);
12899 switch (token) {
12900 case 40 /* OpenParen */:
12901 parenDepth++;
12902 break;
12903 case 41 /* CloseParen */:
12904 parenDepth--;
12905 break;
12906 case 39 /* QuoteSingle */:
12907 // valueStart needs to be there since prop values don't
12908 // have quotes in CSS
12909 valueHasQuotes = valueHasQuotes || valueStart > 0;
12910 if (quote === 0 /* QuoteNone */) {
12911 quote = 39 /* QuoteSingle */;
12912 }
12913 else if (quote === 39 /* QuoteSingle */ && value.charCodeAt(i - 1) !== 92 /* BackSlash */) {
12914 quote = 0 /* QuoteNone */;
12915 }
12916 break;
12917 case 34 /* QuoteDouble */:
12918 // same logic as above
12919 valueHasQuotes = valueHasQuotes || valueStart > 0;
12920 if (quote === 0 /* QuoteNone */) {
12921 quote = 34 /* QuoteDouble */;
12922 }
12923 else if (quote === 34 /* QuoteDouble */ && value.charCodeAt(i - 1) !== 92 /* BackSlash */) {
12924 quote = 0 /* QuoteNone */;
12925 }
12926 break;
12927 case 58 /* Colon */:
12928 if (!currentProp && parenDepth === 0 && quote === 0 /* QuoteNone */) {
12929 currentProp = hyphenate(value.substring(propStart, i - 1).trim());
12930 valueStart = i;
12931 }
12932 break;
12933 case 59 /* Semicolon */:
12934 if (currentProp && valueStart > 0 && parenDepth === 0 && quote === 0 /* QuoteNone */) {
12935 const styleVal = value.substring(valueStart, i - 1).trim();
12936 styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
12937 propStart = i;
12938 valueStart = 0;
12939 currentProp = null;
12940 valueHasQuotes = false;
12941 }
12942 break;
12943 }
12944 }
12945 if (currentProp && valueStart) {
12946 const styleVal = value.substr(valueStart).trim();
12947 styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
12948 }
12949 return styles;
12950 }
12951 function stripUnnecessaryQuotes(value) {
12952 const qS = value.charCodeAt(0);
12953 const qE = value.charCodeAt(value.length - 1);
12954 if (qS == qE && (qS == 39 /* QuoteSingle */ || qS == 34 /* QuoteDouble */)) {
12955 const tempValue = value.substring(1, value.length - 1);
12956 // special case to avoid using a multi-quoted string that was just chomped
12957 // (e.g. `font-family: "Verdana", "sans-serif"`)
12958 if (tempValue.indexOf('\'') == -1 && tempValue.indexOf('"') == -1) {
12959 value = tempValue;
12960 }
12961 }
12962 return value;
12963 }
12964 function hyphenate(value) {
12965 return value
12966 .replace(/[a-z][A-Z]/g, v => {
12967 return v.charAt(0) + '-' + v.charAt(1);
12968 })
12969 .toLowerCase();
12970 }
12971
12972 const IMPORTANT_FLAG = '!important';
12973 /**
12974 * Minimum amount of binding slots required in the runtime for style/class bindings.
12975 *
12976 * Styling in Angular uses up two slots in the runtime LView/TData data structures to
12977 * record binding data, property information and metadata.
12978 *
12979 * When a binding is registered it will place the following information in the `LView`:
12980 *
12981 * slot 1) binding value
12982 * slot 2) cached value (all other values collected before it in string form)
12983 *
12984 * When a binding is registered it will place the following information in the `TData`:
12985 *
12986 * slot 1) prop name
12987 * slot 2) binding index that points to the previous style/class binding (and some extra config
12988 * values)
12989 *
12990 * Let's imagine we have a binding that looks like so:
12991 *
12992 * ```
12993 * <div [style.width]="x" [style.height]="y">
12994 * ```
12995 *
12996 * Our `LView` and `TData` data-structures look like so:
12997 *
12998 * ```typescript
12999 * LView = [
13000 * // ...
13001 * x, // value of x
13002 * "width: x",
13003 *
13004 * y, // value of y
13005 * "width: x; height: y",
13006 * // ...
13007 * ];
13008 *
13009 * TData = [
13010 * // ...
13011 * "width", // binding slot 20
13012 * 0,
13013 *
13014 * "height",
13015 * 20,
13016 * // ...
13017 * ];
13018 * ```
13019 *
13020 * */
13021 const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2;
13022 /**
13023 * Produces creation/update instructions for all styling bindings (class and style)
13024 *
13025 * It also produces the creation instruction to register all initial styling values
13026 * (which are all the static class="..." and style="..." attribute values that exist
13027 * on an element within a template).
13028 *
13029 * The builder class below handles producing instructions for the following cases:
13030 *
13031 * - Static style/class attributes (style="..." and class="...")
13032 * - Dynamic style/class map bindings ([style]="map" and [class]="map|string")
13033 * - Dynamic style/class property bindings ([style.prop]="exp" and [class.name]="exp")
13034 *
13035 * Due to the complex relationship of all of these cases, the instructions generated
13036 * for these attributes/properties/bindings must be done so in the correct order. The
13037 * order which these must be generated is as follows:
13038 *
13039 * if (createMode) {
13040 * styling(...)
13041 * }
13042 * if (updateMode) {
13043 * styleMap(...)
13044 * classMap(...)
13045 * styleProp(...)
13046 * classProp(...)
13047 * }
13048 *
13049 * The creation/update methods within the builder class produce these instructions.
13050 */
13051 class StylingBuilder {
13052 constructor(_directiveExpr) {
13053 this._directiveExpr = _directiveExpr;
13054 /** Whether or not there are any static styling values present */
13055 this._hasInitialValues = false;
13056 /**
13057 * Whether or not there are any styling bindings present
13058 * (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
13059 */
13060 this.hasBindings = false;
13061 this.hasBindingsWithPipes = false;
13062 /** the input for [class] (if it exists) */
13063 this._classMapInput = null;
13064 /** the input for [style] (if it exists) */
13065 this._styleMapInput = null;
13066 /** an array of each [style.prop] input */
13067 this._singleStyleInputs = null;
13068 /** an array of each [class.name] input */
13069 this._singleClassInputs = null;
13070 this._lastStylingInput = null;
13071 this._firstStylingInput = null;
13072 // maps are used instead of hash maps because a Map will
13073 // retain the ordering of the keys
13074 /**
13075 * Represents the location of each style binding in the template
13076 * (e.g. `<div [style.width]="w" [style.height]="h">` implies
13077 * that `width=0` and `height=1`)
13078 */
13079 this._stylesIndex = new Map();
13080 /**
13081 * Represents the location of each class binding in the template
13082 * (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
13083 * that `big=0` and `hidden=1`)
13084 */
13085 this._classesIndex = new Map();
13086 this._initialStyleValues = [];
13087 this._initialClassValues = [];
13088 }
13089 /**
13090 * Registers a given input to the styling builder to be later used when producing AOT code.
13091 *
13092 * The code below will only accept the input if it is somehow tied to styling (whether it be
13093 * style/class bindings or static style/class attributes).
13094 */
13095 registerBoundInput(input) {
13096 // [attr.style] or [attr.class] are skipped in the code below,
13097 // they should not be treated as styling-based bindings since
13098 // they are intended to be written directly to the attr and
13099 // will therefore skip all style/class resolution that is present
13100 // with style="", [style]="" and [style.prop]="", class="",
13101 // [class.prop]="". [class]="" assignments
13102 let binding = null;
13103 let name = input.name;
13104 switch (input.type) {
13105 case 0 /* Property */:
13106 binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan);
13107 break;
13108 case 3 /* Style */:
13109 binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit);
13110 break;
13111 case 2 /* Class */:
13112 binding = this.registerClassInput(name, false, input.value, input.sourceSpan);
13113 break;
13114 }
13115 return binding ? true : false;
13116 }
13117 registerInputBasedOnName(name, expression, sourceSpan) {
13118 let binding = null;
13119 const prefix = name.substring(0, 6);
13120 const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!';
13121 const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');
13122 if (isStyle || isClass) {
13123 const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no
13124 const property = name.substr(isMapBased ? 5 : 6); // the dot explains why there's a +1
13125 if (isStyle) {
13126 binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan);
13127 }
13128 else {
13129 binding = this.registerClassInput(property, isMapBased, expression, sourceSpan);
13130 }
13131 }
13132 return binding;
13133 }
13134 registerStyleInput(name, isMapBased, value, sourceSpan, suffix) {
13135 if (isEmptyExpression(value)) {
13136 return null;
13137 }
13138 // CSS custom properties are case-sensitive so we shouldn't normalize them.
13139 // See: https://www.w3.org/TR/css-variables-1/#defining-variables
13140 if (!isCssCustomProperty(name)) {
13141 name = hyphenate(name);
13142 }
13143 const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
13144 suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
13145 const entry = { name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag };
13146 if (isMapBased) {
13147 this._styleMapInput = entry;
13148 }
13149 else {
13150 (this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
13151 registerIntoMap(this._stylesIndex, property);
13152 }
13153 this._lastStylingInput = entry;
13154 this._firstStylingInput = this._firstStylingInput || entry;
13155 this._checkForPipes(value);
13156 this.hasBindings = true;
13157 return entry;
13158 }
13159 registerClassInput(name, isMapBased, value, sourceSpan) {
13160 if (isEmptyExpression(value)) {
13161 return null;
13162 }
13163 const { property, hasOverrideFlag } = parseProperty(name);
13164 const entry = { name: property, value, sourceSpan, hasOverrideFlag, suffix: null };
13165 if (isMapBased) {
13166 this._classMapInput = entry;
13167 }
13168 else {
13169 (this._singleClassInputs = this._singleClassInputs || []).push(entry);
13170 registerIntoMap(this._classesIndex, property);
13171 }
13172 this._lastStylingInput = entry;
13173 this._firstStylingInput = this._firstStylingInput || entry;
13174 this._checkForPipes(value);
13175 this.hasBindings = true;
13176 return entry;
13177 }
13178 _checkForPipes(value) {
13179 if ((value instanceof ASTWithSource) && (value.ast instanceof BindingPipe)) {
13180 this.hasBindingsWithPipes = true;
13181 }
13182 }
13183 /**
13184 * Registers the element's static style string value to the builder.
13185 *
13186 * @param value the style string (e.g. `width:100px; height:200px;`)
13187 */
13188 registerStyleAttr(value) {
13189 this._initialStyleValues = parse(value);
13190 this._hasInitialValues = true;
13191 }
13192 /**
13193 * Registers the element's static class string value to the builder.
13194 *
13195 * @param value the className string (e.g. `disabled gold zoom`)
13196 */
13197 registerClassAttr(value) {
13198 this._initialClassValues = value.trim().split(/\s+/g);
13199 this._hasInitialValues = true;
13200 }
13201 /**
13202 * Appends all styling-related expressions to the provided attrs array.
13203 *
13204 * @param attrs an existing array where each of the styling expressions
13205 * will be inserted into.
13206 */
13207 populateInitialStylingAttrs(attrs) {
13208 // [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
13209 if (this._initialClassValues.length) {
13210 attrs.push(literal$1(1 /* Classes */));
13211 for (let i = 0; i < this._initialClassValues.length; i++) {
13212 attrs.push(literal$1(this._initialClassValues[i]));
13213 }
13214 }
13215 // [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]
13216 if (this._initialStyleValues.length) {
13217 attrs.push(literal$1(2 /* Styles */));
13218 for (let i = 0; i < this._initialStyleValues.length; i += 2) {
13219 attrs.push(literal$1(this._initialStyleValues[i]), literal$1(this._initialStyleValues[i + 1]));
13220 }
13221 }
13222 }
13223 /**
13224 * Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
13225 *
13226 * The instruction generation code below is used for producing the AOT statement code which is
13227 * responsible for registering initial styles (within a directive hostBindings' creation block),
13228 * as well as any of the provided attribute values, to the directive host element.
13229 */
13230 assignHostAttrs(attrs, definitionMap) {
13231 if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
13232 this.populateInitialStylingAttrs(attrs);
13233 definitionMap.set('hostAttrs', literalArr(attrs));
13234 }
13235 }
13236 /**
13237 * Builds an instruction with all the expressions and parameters for `classMap`.
13238 *
13239 * The instruction data will contain all expressions for `classMap` to function
13240 * which includes the `[class]` expression params.
13241 */
13242 buildClassMapInstruction(valueConverter) {
13243 if (this._classMapInput) {
13244 return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
13245 }
13246 return null;
13247 }
13248 /**
13249 * Builds an instruction with all the expressions and parameters for `styleMap`.
13250 *
13251 * The instruction data will contain all expressions for `styleMap` to function
13252 * which includes the `[style]` expression params.
13253 */
13254 buildStyleMapInstruction(valueConverter) {
13255 if (this._styleMapInput) {
13256 return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
13257 }
13258 return null;
13259 }
13260 _buildMapBasedInstruction(valueConverter, isClassBased, stylingInput) {
13261 // each styling binding value is stored in the LView
13262 // map-based bindings allocate two slots: one for the
13263 // previous binding value and another for the previous
13264 // className or style attribute value.
13265 let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
13266 // these values must be outside of the update block so that they can
13267 // be evaluated (the AST visit call) during creation time so that any
13268 // pipes can be picked up in time before the template is built
13269 const mapValue = stylingInput.value.visit(valueConverter);
13270 let reference;
13271 if (mapValue instanceof Interpolation) {
13272 totalBindingSlotsRequired += mapValue.expressions.length;
13273 reference = isClassBased ? getClassMapInterpolationExpression(mapValue) :
13274 getStyleMapInterpolationExpression(mapValue);
13275 }
13276 else {
13277 reference = isClassBased ? Identifiers$1.classMap : Identifiers$1.styleMap;
13278 }
13279 return {
13280 reference,
13281 calls: [{
13282 supportsInterpolation: true,
13283 sourceSpan: stylingInput.sourceSpan,
13284 allocateBindingSlots: totalBindingSlotsRequired,
13285 params: (convertFn) => {
13286 const convertResult = convertFn(mapValue);
13287 const params = Array.isArray(convertResult) ? convertResult : [convertResult];
13288 return params;
13289 }
13290 }]
13291 };
13292 }
13293 _buildSingleInputs(reference, inputs, valueConverter, getInterpolationExpressionFn, isClassBased) {
13294 const instructions = [];
13295 inputs.forEach(input => {
13296 const previousInstruction = instructions[instructions.length - 1];
13297 const value = input.value.visit(valueConverter);
13298 let referenceForCall = reference;
13299 // each styling binding value is stored in the LView
13300 // but there are two values stored for each binding:
13301 // 1) the value itself
13302 // 2) an intermediate value (concatenation of style up to this point).
13303 // We need to store the intermediate value so that we don't allocate
13304 // the strings on each CD.
13305 let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
13306 if (value instanceof Interpolation) {
13307 totalBindingSlotsRequired += value.expressions.length;
13308 if (getInterpolationExpressionFn) {
13309 referenceForCall = getInterpolationExpressionFn(value);
13310 }
13311 }
13312 const call = {
13313 sourceSpan: input.sourceSpan,
13314 allocateBindingSlots: totalBindingSlotsRequired,
13315 supportsInterpolation: !!getInterpolationExpressionFn,
13316 params: (convertFn) => {
13317 // params => stylingProp(propName, value, suffix)
13318 const params = [];
13319 params.push(literal$1(input.name));
13320 const convertResult = convertFn(value);
13321 if (Array.isArray(convertResult)) {
13322 params.push(...convertResult);
13323 }
13324 else {
13325 params.push(convertResult);
13326 }
13327 // [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore,
13328 // if that is detected then we need to pass that in as an optional param.
13329 if (!isClassBased && input.suffix !== null) {
13330 params.push(literal$1(input.suffix));
13331 }
13332 return params;
13333 }
13334 };
13335 // If we ended up generating a call to the same instruction as the previous styling property
13336 // we can chain the calls together safely to save some bytes, otherwise we have to generate
13337 // a separate instruction call. This is primarily a concern with interpolation instructions
13338 // where we may start off with one `reference`, but end up using another based on the
13339 // number of interpolations.
13340 if (previousInstruction && previousInstruction.reference === referenceForCall) {
13341 previousInstruction.calls.push(call);
13342 }
13343 else {
13344 instructions.push({ reference: referenceForCall, calls: [call] });
13345 }
13346 });
13347 return instructions;
13348 }
13349 _buildClassInputs(valueConverter) {
13350 if (this._singleClassInputs) {
13351 return this._buildSingleInputs(Identifiers$1.classProp, this._singleClassInputs, valueConverter, null, true);
13352 }
13353 return [];
13354 }
13355 _buildStyleInputs(valueConverter) {
13356 if (this._singleStyleInputs) {
13357 return this._buildSingleInputs(Identifiers$1.styleProp, this._singleStyleInputs, valueConverter, getStylePropInterpolationExpression, false);
13358 }
13359 return [];
13360 }
13361 /**
13362 * Constructs all instructions which contain the expressions that will be placed
13363 * into the update block of a template function or a directive hostBindings function.
13364 */
13365 buildUpdateLevelInstructions(valueConverter) {
13366 const instructions = [];
13367 if (this.hasBindings) {
13368 const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
13369 if (styleMapInstruction) {
13370 instructions.push(styleMapInstruction);
13371 }
13372 const classMapInstruction = this.buildClassMapInstruction(valueConverter);
13373 if (classMapInstruction) {
13374 instructions.push(classMapInstruction);
13375 }
13376 instructions.push(...this._buildStyleInputs(valueConverter));
13377 instructions.push(...this._buildClassInputs(valueConverter));
13378 }
13379 return instructions;
13380 }
13381 }
13382 function registerIntoMap(map, key) {
13383 if (!map.has(key)) {
13384 map.set(key, map.size);
13385 }
13386 }
13387 function parseProperty(name) {
13388 let hasOverrideFlag = false;
13389 const overrideIndex = name.indexOf(IMPORTANT_FLAG);
13390 if (overrideIndex !== -1) {
13391 name = overrideIndex > 0 ? name.substring(0, overrideIndex) : '';
13392 hasOverrideFlag = true;
13393 }
13394 let suffix = null;
13395 let property = name;
13396 const unitIndex = name.lastIndexOf('.');
13397 if (unitIndex > 0) {
13398 suffix = name.substr(unitIndex + 1);
13399 property = name.substring(0, unitIndex);
13400 }
13401 return { property, suffix, hasOverrideFlag };
13402 }
13403 /**
13404 * Gets the instruction to generate for an interpolated class map.
13405 * @param interpolation An Interpolation AST
13406 */
13407 function getClassMapInterpolationExpression(interpolation) {
13408 switch (getInterpolationArgsLength(interpolation)) {
13409 case 1:
13410 return Identifiers$1.classMap;
13411 case 3:
13412 return Identifiers$1.classMapInterpolate1;
13413 case 5:
13414 return Identifiers$1.classMapInterpolate2;
13415 case 7:
13416 return Identifiers$1.classMapInterpolate3;
13417 case 9:
13418 return Identifiers$1.classMapInterpolate4;
13419 case 11:
13420 return Identifiers$1.classMapInterpolate5;
13421 case 13:
13422 return Identifiers$1.classMapInterpolate6;
13423 case 15:
13424 return Identifiers$1.classMapInterpolate7;
13425 case 17:
13426 return Identifiers$1.classMapInterpolate8;
13427 default:
13428 return Identifiers$1.classMapInterpolateV;
13429 }
13430 }
13431 /**
13432 * Gets the instruction to generate for an interpolated style map.
13433 * @param interpolation An Interpolation AST
13434 */
13435 function getStyleMapInterpolationExpression(interpolation) {
13436 switch (getInterpolationArgsLength(interpolation)) {
13437 case 1:
13438 return Identifiers$1.styleMap;
13439 case 3:
13440 return Identifiers$1.styleMapInterpolate1;
13441 case 5:
13442 return Identifiers$1.styleMapInterpolate2;
13443 case 7:
13444 return Identifiers$1.styleMapInterpolate3;
13445 case 9:
13446 return Identifiers$1.styleMapInterpolate4;
13447 case 11:
13448 return Identifiers$1.styleMapInterpolate5;
13449 case 13:
13450 return Identifiers$1.styleMapInterpolate6;
13451 case 15:
13452 return Identifiers$1.styleMapInterpolate7;
13453 case 17:
13454 return Identifiers$1.styleMapInterpolate8;
13455 default:
13456 return Identifiers$1.styleMapInterpolateV;
13457 }
13458 }
13459 /**
13460 * Gets the instruction to generate for an interpolated style prop.
13461 * @param interpolation An Interpolation AST
13462 */
13463 function getStylePropInterpolationExpression(interpolation) {
13464 switch (getInterpolationArgsLength(interpolation)) {
13465 case 1:
13466 return Identifiers$1.styleProp;
13467 case 3:
13468 return Identifiers$1.stylePropInterpolate1;
13469 case 5:
13470 return Identifiers$1.stylePropInterpolate2;
13471 case 7:
13472 return Identifiers$1.stylePropInterpolate3;
13473 case 9:
13474 return Identifiers$1.stylePropInterpolate4;
13475 case 11:
13476 return Identifiers$1.stylePropInterpolate5;
13477 case 13:
13478 return Identifiers$1.stylePropInterpolate6;
13479 case 15:
13480 return Identifiers$1.stylePropInterpolate7;
13481 case 17:
13482 return Identifiers$1.stylePropInterpolate8;
13483 default:
13484 return Identifiers$1.stylePropInterpolateV;
13485 }
13486 }
13487 /**
13488 * Checks whether property name is a custom CSS property.
13489 * See: https://www.w3.org/TR/css-variables-1
13490 */
13491 function isCssCustomProperty(name) {
13492 return name.startsWith('--');
13493 }
13494
13495 /**
13496 * @license
13497 * Copyright Google LLC All Rights Reserved.
13498 *
13499 * Use of this source code is governed by an MIT-style license that can be
13500 * found in the LICENSE file at https://angular.io/license
13501 */
13502 var TokenType;
13503 (function (TokenType) {
13504 TokenType[TokenType["Character"] = 0] = "Character";
13505 TokenType[TokenType["Identifier"] = 1] = "Identifier";
13506 TokenType[TokenType["PrivateIdentifier"] = 2] = "PrivateIdentifier";
13507 TokenType[TokenType["Keyword"] = 3] = "Keyword";
13508 TokenType[TokenType["String"] = 4] = "String";
13509 TokenType[TokenType["Operator"] = 5] = "Operator";
13510 TokenType[TokenType["Number"] = 6] = "Number";
13511 TokenType[TokenType["Error"] = 7] = "Error";
13512 })(TokenType || (TokenType = {}));
13513 const KEYWORDS = ['var', 'let', 'as', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
13514 class Lexer {
13515 tokenize(text) {
13516 const scanner = new _Scanner(text);
13517 const tokens = [];
13518 let token = scanner.scanToken();
13519 while (token != null) {
13520 tokens.push(token);
13521 token = scanner.scanToken();
13522 }
13523 return tokens;
13524 }
13525 }
13526 class Token {
13527 constructor(index, end, type, numValue, strValue) {
13528 this.index = index;
13529 this.end = end;
13530 this.type = type;
13531 this.numValue = numValue;
13532 this.strValue = strValue;
13533 }
13534 isCharacter(code) {
13535 return this.type == TokenType.Character && this.numValue == code;
13536 }
13537 isNumber() {
13538 return this.type == TokenType.Number;
13539 }
13540 isString() {
13541 return this.type == TokenType.String;
13542 }
13543 isOperator(operator) {
13544 return this.type == TokenType.Operator && this.strValue == operator;
13545 }
13546 isIdentifier() {
13547 return this.type == TokenType.Identifier;
13548 }
13549 isPrivateIdentifier() {
13550 return this.type == TokenType.PrivateIdentifier;
13551 }
13552 isKeyword() {
13553 return this.type == TokenType.Keyword;
13554 }
13555 isKeywordLet() {
13556 return this.type == TokenType.Keyword && this.strValue == 'let';
13557 }
13558 isKeywordAs() {
13559 return this.type == TokenType.Keyword && this.strValue == 'as';
13560 }
13561 isKeywordNull() {
13562 return this.type == TokenType.Keyword && this.strValue == 'null';
13563 }
13564 isKeywordUndefined() {
13565 return this.type == TokenType.Keyword && this.strValue == 'undefined';
13566 }
13567 isKeywordTrue() {
13568 return this.type == TokenType.Keyword && this.strValue == 'true';
13569 }
13570 isKeywordFalse() {
13571 return this.type == TokenType.Keyword && this.strValue == 'false';
13572 }
13573 isKeywordThis() {
13574 return this.type == TokenType.Keyword && this.strValue == 'this';
13575 }
13576 isError() {
13577 return this.type == TokenType.Error;
13578 }
13579 toNumber() {
13580 return this.type == TokenType.Number ? this.numValue : -1;
13581 }
13582 toString() {
13583 switch (this.type) {
13584 case TokenType.Character:
13585 case TokenType.Identifier:
13586 case TokenType.Keyword:
13587 case TokenType.Operator:
13588 case TokenType.PrivateIdentifier:
13589 case TokenType.String:
13590 case TokenType.Error:
13591 return this.strValue;
13592 case TokenType.Number:
13593 return this.numValue.toString();
13594 default:
13595 return null;
13596 }
13597 }
13598 }
13599 function newCharacterToken(index, end, code) {
13600 return new Token(index, end, TokenType.Character, code, String.fromCharCode(code));
13601 }
13602 function newIdentifierToken(index, end, text) {
13603 return new Token(index, end, TokenType.Identifier, 0, text);
13604 }
13605 function newPrivateIdentifierToken(index, end, text) {
13606 return new Token(index, end, TokenType.PrivateIdentifier, 0, text);
13607 }
13608 function newKeywordToken(index, end, text) {
13609 return new Token(index, end, TokenType.Keyword, 0, text);
13610 }
13611 function newOperatorToken(index, end, text) {
13612 return new Token(index, end, TokenType.Operator, 0, text);
13613 }
13614 function newStringToken(index, end, text) {
13615 return new Token(index, end, TokenType.String, 0, text);
13616 }
13617 function newNumberToken(index, end, n) {
13618 return new Token(index, end, TokenType.Number, n, '');
13619 }
13620 function newErrorToken(index, end, message) {
13621 return new Token(index, end, TokenType.Error, 0, message);
13622 }
13623 const EOF = new Token(-1, -1, TokenType.Character, 0, '');
13624 class _Scanner {
13625 constructor(input) {
13626 this.input = input;
13627 this.peek = 0;
13628 this.index = -1;
13629 this.length = input.length;
13630 this.advance();
13631 }
13632 advance() {
13633 this.peek = ++this.index >= this.length ? $EOF : this.input.charCodeAt(this.index);
13634 }
13635 scanToken() {
13636 const input = this.input, length = this.length;
13637 let peek = this.peek, index = this.index;
13638 // Skip whitespace.
13639 while (peek <= $SPACE) {
13640 if (++index >= length) {
13641 peek = $EOF;
13642 break;
13643 }
13644 else {
13645 peek = input.charCodeAt(index);
13646 }
13647 }
13648 this.peek = peek;
13649 this.index = index;
13650 if (index >= length) {
13651 return null;
13652 }
13653 // Handle identifiers and numbers.
13654 if (isIdentifierStart(peek))
13655 return this.scanIdentifier();
13656 if (isDigit(peek))
13657 return this.scanNumber(index);
13658 const start = index;
13659 switch (peek) {
13660 case $PERIOD:
13661 this.advance();
13662 return isDigit(this.peek) ? this.scanNumber(start) :
13663 newCharacterToken(start, this.index, $PERIOD);
13664 case $LPAREN:
13665 case $RPAREN:
13666 case $LBRACE:
13667 case $RBRACE:
13668 case $LBRACKET:
13669 case $RBRACKET:
13670 case $COMMA:
13671 case $COLON:
13672 case $SEMICOLON:
13673 return this.scanCharacter(start, peek);
13674 case $SQ:
13675 case $DQ:
13676 return this.scanString();
13677 case $HASH:
13678 return this.scanPrivateIdentifier();
13679 case $PLUS:
13680 case $MINUS:
13681 case $STAR:
13682 case $SLASH:
13683 case $PERCENT:
13684 case $CARET:
13685 return this.scanOperator(start, String.fromCharCode(peek));
13686 case $QUESTION:
13687 return this.scanQuestion(start);
13688 case $LT:
13689 case $GT:
13690 return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=');
13691 case $BANG:
13692 case $EQ:
13693 return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=', $EQ, '=');
13694 case $AMPERSAND:
13695 return this.scanComplexOperator(start, '&', $AMPERSAND, '&');
13696 case $BAR:
13697 return this.scanComplexOperator(start, '|', $BAR, '|');
13698 case $NBSP:
13699 while (isWhitespace(this.peek))
13700 this.advance();
13701 return this.scanToken();
13702 }
13703 this.advance();
13704 return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);
13705 }
13706 scanCharacter(start, code) {
13707 this.advance();
13708 return newCharacterToken(start, this.index, code);
13709 }
13710 scanOperator(start, str) {
13711 this.advance();
13712 return newOperatorToken(start, this.index, str);
13713 }
13714 /**
13715 * Tokenize a 2/3 char long operator
13716 *
13717 * @param start start index in the expression
13718 * @param one first symbol (always part of the operator)
13719 * @param twoCode code point for the second symbol
13720 * @param two second symbol (part of the operator when the second code point matches)
13721 * @param threeCode code point for the third symbol
13722 * @param three third symbol (part of the operator when provided and matches source expression)
13723 */
13724 scanComplexOperator(start, one, twoCode, two, threeCode, three) {
13725 this.advance();
13726 let str = one;
13727 if (this.peek == twoCode) {
13728 this.advance();
13729 str += two;
13730 }
13731 if (threeCode != null && this.peek == threeCode) {
13732 this.advance();
13733 str += three;
13734 }
13735 return newOperatorToken(start, this.index, str);
13736 }
13737 scanIdentifier() {
13738 const start = this.index;
13739 this.advance();
13740 while (isIdentifierPart(this.peek))
13741 this.advance();
13742 const str = this.input.substring(start, this.index);
13743 return KEYWORDS.indexOf(str) > -1 ? newKeywordToken(start, this.index, str) :
13744 newIdentifierToken(start, this.index, str);
13745 }
13746 /** Scans an ECMAScript private identifier. */
13747 scanPrivateIdentifier() {
13748 const start = this.index;
13749 this.advance();
13750 if (!isIdentifierStart(this.peek)) {
13751 return this.error('Invalid character [#]', -1);
13752 }
13753 while (isIdentifierPart(this.peek))
13754 this.advance();
13755 const identifierName = this.input.substring(start, this.index);
13756 return newPrivateIdentifierToken(start, this.index, identifierName);
13757 }
13758 scanNumber(start) {
13759 let simple = (this.index === start);
13760 let hasSeparators = false;
13761 this.advance(); // Skip initial digit.
13762 while (true) {
13763 if (isDigit(this.peek)) ;
13764 else if (this.peek === $_) {
13765 // Separators are only valid when they're surrounded by digits. E.g. `1_0_1` is
13766 // valid while `_101` and `101_` are not. The separator can't be next to the decimal
13767 // point or another separator either. Note that it's unlikely that we'll hit a case where
13768 // the underscore is at the start, because that's a valid identifier and it will be picked
13769 // up earlier in the parsing. We validate for it anyway just in case.
13770 if (!isDigit(this.input.charCodeAt(this.index - 1)) ||
13771 !isDigit(this.input.charCodeAt(this.index + 1))) {
13772 return this.error('Invalid numeric separator', 0);
13773 }
13774 hasSeparators = true;
13775 }
13776 else if (this.peek === $PERIOD) {
13777 simple = false;
13778 }
13779 else if (isExponentStart(this.peek)) {
13780 this.advance();
13781 if (isExponentSign(this.peek))
13782 this.advance();
13783 if (!isDigit(this.peek))
13784 return this.error('Invalid exponent', -1);
13785 simple = false;
13786 }
13787 else {
13788 break;
13789 }
13790 this.advance();
13791 }
13792 let str = this.input.substring(start, this.index);
13793 if (hasSeparators) {
13794 str = str.replace(/_/g, '');
13795 }
13796 const value = simple ? parseIntAutoRadix(str) : parseFloat(str);
13797 return newNumberToken(start, this.index, value);
13798 }
13799 scanString() {
13800 const start = this.index;
13801 const quote = this.peek;
13802 this.advance(); // Skip initial quote.
13803 let buffer = '';
13804 let marker = this.index;
13805 const input = this.input;
13806 while (this.peek != quote) {
13807 if (this.peek == $BACKSLASH) {
13808 buffer += input.substring(marker, this.index);
13809 this.advance();
13810 let unescapedCode;
13811 // Workaround for TS2.1-introduced type strictness
13812 this.peek = this.peek;
13813 if (this.peek == $u) {
13814 // 4 character hex code for unicode character.
13815 const hex = input.substring(this.index + 1, this.index + 5);
13816 if (/^[0-9a-f]+$/i.test(hex)) {
13817 unescapedCode = parseInt(hex, 16);
13818 }
13819 else {
13820 return this.error(`Invalid unicode escape [\\u${hex}]`, 0);
13821 }
13822 for (let i = 0; i < 5; i++) {
13823 this.advance();
13824 }
13825 }
13826 else {
13827 unescapedCode = unescape(this.peek);
13828 this.advance();
13829 }
13830 buffer += String.fromCharCode(unescapedCode);
13831 marker = this.index;
13832 }
13833 else if (this.peek == $EOF) {
13834 return this.error('Unterminated quote', 0);
13835 }
13836 else {
13837 this.advance();
13838 }
13839 }
13840 const last = input.substring(marker, this.index);
13841 this.advance(); // Skip terminating quote.
13842 return newStringToken(start, this.index, buffer + last);
13843 }
13844 scanQuestion(start) {
13845 this.advance();
13846 let str = '?';
13847 // Either `a ?? b` or 'a?.b'.
13848 if (this.peek === $QUESTION || this.peek === $PERIOD) {
13849 str += this.peek === $PERIOD ? '.' : '?';
13850 this.advance();
13851 }
13852 return newOperatorToken(start, this.index, str);
13853 }
13854 error(message, offset) {
13855 const position = this.index + offset;
13856 return newErrorToken(position, this.index, `Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
13857 }
13858 }
13859 function isIdentifierStart(code) {
13860 return ($a <= code && code <= $z) || ($A <= code && code <= $Z) ||
13861 (code == $_) || (code == $$);
13862 }
13863 function isIdentifier$1(input) {
13864 if (input.length == 0)
13865 return false;
13866 const scanner = new _Scanner(input);
13867 if (!isIdentifierStart(scanner.peek))
13868 return false;
13869 scanner.advance();
13870 while (scanner.peek !== $EOF) {
13871 if (!isIdentifierPart(scanner.peek))
13872 return false;
13873 scanner.advance();
13874 }
13875 return true;
13876 }
13877 function isIdentifierPart(code) {
13878 return isAsciiLetter(code) || isDigit(code) || (code == $_) ||
13879 (code == $$);
13880 }
13881 function isExponentStart(code) {
13882 return code == $e || code == $E;
13883 }
13884 function isExponentSign(code) {
13885 return code == $MINUS || code == $PLUS;
13886 }
13887 function unescape(code) {
13888 switch (code) {
13889 case $n:
13890 return $LF;
13891 case $f:
13892 return $FF;
13893 case $r:
13894 return $CR;
13895 case $t:
13896 return $TAB;
13897 case $v:
13898 return $VTAB;
13899 default:
13900 return code;
13901 }
13902 }
13903 function parseIntAutoRadix(text) {
13904 const result = parseInt(text);
13905 if (isNaN(result)) {
13906 throw new Error('Invalid integer literal when parsing ' + text);
13907 }
13908 return result;
13909 }
13910
13911 /**
13912 * @license
13913 * Copyright Google LLC All Rights Reserved.
13914 *
13915 * Use of this source code is governed by an MIT-style license that can be
13916 * found in the LICENSE file at https://angular.io/license
13917 */
13918 class SplitInterpolation {
13919 constructor(strings, expressions, offsets) {
13920 this.strings = strings;
13921 this.expressions = expressions;
13922 this.offsets = offsets;
13923 }
13924 }
13925 class TemplateBindingParseResult {
13926 constructor(templateBindings, warnings, errors) {
13927 this.templateBindings = templateBindings;
13928 this.warnings = warnings;
13929 this.errors = errors;
13930 }
13931 }
13932 class Parser {
13933 constructor(_lexer) {
13934 this._lexer = _lexer;
13935 this.errors = [];
13936 this.simpleExpressionChecker = SimpleExpressionChecker;
13937 }
13938 parseAction(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
13939 this._checkNoInterpolation(input, location, interpolationConfig);
13940 const sourceToLex = this._stripComments(input);
13941 const tokens = this._lexer.tokenize(this._stripComments(input));
13942 const ast = new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, true, this.errors, input.length - sourceToLex.length)
13943 .parseChain();
13944 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
13945 }
13946 parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
13947 const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);
13948 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
13949 }
13950 checkSimpleExpression(ast) {
13951 const checker = new this.simpleExpressionChecker();
13952 ast.visit(checker);
13953 return checker.errors;
13954 }
13955 parseSimpleBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
13956 const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);
13957 const errors = this.checkSimpleExpression(ast);
13958 if (errors.length > 0) {
13959 this._reportError(`Host binding expression cannot contain ${errors.join(' ')}`, input, location);
13960 }
13961 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
13962 }
13963 _reportError(message, input, errLocation, ctxLocation) {
13964 this.errors.push(new ParserError(message, input, errLocation, ctxLocation));
13965 }
13966 _parseBindingAst(input, location, absoluteOffset, interpolationConfig) {
13967 // Quotes expressions use 3rd-party expression language. We don't want to use
13968 // our lexer or parser for that, so we check for that ahead of time.
13969 const quote = this._parseQuote(input, location, absoluteOffset);
13970 if (quote != null) {
13971 return quote;
13972 }
13973 this._checkNoInterpolation(input, location, interpolationConfig);
13974 const sourceToLex = this._stripComments(input);
13975 const tokens = this._lexer.tokenize(sourceToLex);
13976 return new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, input.length - sourceToLex.length)
13977 .parseChain();
13978 }
13979 _parseQuote(input, location, absoluteOffset) {
13980 if (input == null)
13981 return null;
13982 const prefixSeparatorIndex = input.indexOf(':');
13983 if (prefixSeparatorIndex == -1)
13984 return null;
13985 const prefix = input.substring(0, prefixSeparatorIndex).trim();
13986 if (!isIdentifier$1(prefix))
13987 return null;
13988 const uninterpretedExpression = input.substring(prefixSeparatorIndex + 1);
13989 const span = new ParseSpan(0, input.length);
13990 return new Quote(span, span.toAbsolute(absoluteOffset), prefix, uninterpretedExpression, location);
13991 }
13992 /**
13993 * Parse microsyntax template expression and return a list of bindings or
13994 * parsing errors in case the given expression is invalid.
13995 *
13996 * For example,
13997 * ```
13998 * <div *ngFor="let item of items">
13999 * ^ ^ absoluteValueOffset for `templateValue`
14000 * absoluteKeyOffset for `templateKey`
14001 * ```
14002 * contains three bindings:
14003 * 1. ngFor -> null
14004 * 2. item -> NgForOfContext.$implicit
14005 * 3. ngForOf -> items
14006 *
14007 * This is apparent from the de-sugared template:
14008 * ```
14009 * <ng-template ngFor let-item [ngForOf]="items">
14010 * ```
14011 *
14012 * @param templateKey name of directive, without the * prefix. For example: ngIf, ngFor
14013 * @param templateValue RHS of the microsyntax attribute
14014 * @param templateUrl template filename if it's external, component filename if it's inline
14015 * @param absoluteKeyOffset start of the `templateKey`
14016 * @param absoluteValueOffset start of the `templateValue`
14017 */
14018 parseTemplateBindings(templateKey, templateValue, templateUrl, absoluteKeyOffset, absoluteValueOffset) {
14019 const tokens = this._lexer.tokenize(templateValue);
14020 const parser = new _ParseAST(templateValue, templateUrl, absoluteValueOffset, tokens, templateValue.length, false /* parseAction */, this.errors, 0 /* relative offset */);
14021 return parser.parseTemplateBindings({
14022 source: templateKey,
14023 span: new AbsoluteSourceSpan$1(absoluteKeyOffset, absoluteKeyOffset + templateKey.length),
14024 });
14025 }
14026 parseInterpolation(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
14027 const { strings, expressions, offsets } = this.splitInterpolation(input, location, interpolationConfig);
14028 if (expressions.length === 0)
14029 return null;
14030 const expressionNodes = [];
14031 for (let i = 0; i < expressions.length; ++i) {
14032 const expressionText = expressions[i].text;
14033 const sourceToLex = this._stripComments(expressionText);
14034 const tokens = this._lexer.tokenize(sourceToLex);
14035 const ast = new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, offsets[i] + (expressionText.length - sourceToLex.length))
14036 .parseChain();
14037 expressionNodes.push(ast);
14038 }
14039 return this.createInterpolationAst(strings.map(s => s.text), expressionNodes, input, location, absoluteOffset);
14040 }
14041 /**
14042 * Similar to `parseInterpolation`, but treats the provided string as a single expression
14043 * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).
14044 * This is used for parsing the switch expression in ICUs.
14045 */
14046 parseInterpolationExpression(expression, location, absoluteOffset) {
14047 const sourceToLex = this._stripComments(expression);
14048 const tokens = this._lexer.tokenize(sourceToLex);
14049 const ast = new _ParseAST(expression, location, absoluteOffset, tokens, sourceToLex.length,
14050 /* parseAction */ false, this.errors, 0)
14051 .parseChain();
14052 const strings = ['', '']; // The prefix and suffix strings are both empty
14053 return this.createInterpolationAst(strings, [ast], expression, location, absoluteOffset);
14054 }
14055 createInterpolationAst(strings, expressions, input, location, absoluteOffset) {
14056 const span = new ParseSpan(0, input.length);
14057 const interpolation = new Interpolation(span, span.toAbsolute(absoluteOffset), strings, expressions);
14058 return new ASTWithSource(interpolation, input, location, absoluteOffset, this.errors);
14059 }
14060 /**
14061 * Splits a string of text into "raw" text segments and expressions present in interpolations in
14062 * the string.
14063 * Returns `null` if there are no interpolations, otherwise a
14064 * `SplitInterpolation` with splits that look like
14065 * <raw text> <expression> <raw text> ... <raw text> <expression> <raw text>
14066 */
14067 splitInterpolation(input, location, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
14068 const strings = [];
14069 const expressions = [];
14070 const offsets = [];
14071 let i = 0;
14072 let atInterpolation = false;
14073 let extendLastString = false;
14074 let { start: interpStart, end: interpEnd } = interpolationConfig;
14075 while (i < input.length) {
14076 if (!atInterpolation) {
14077 // parse until starting {{
14078 const start = i;
14079 i = input.indexOf(interpStart, i);
14080 if (i === -1) {
14081 i = input.length;
14082 }
14083 const text = input.substring(start, i);
14084 strings.push({ text, start, end: i });
14085 atInterpolation = true;
14086 }
14087 else {
14088 // parse from starting {{ to ending }} while ignoring content inside quotes.
14089 const fullStart = i;
14090 const exprStart = fullStart + interpStart.length;
14091 const exprEnd = this._getInterpolationEndIndex(input, interpEnd, exprStart);
14092 if (exprEnd === -1) {
14093 // Could not find the end of the interpolation; do not parse an expression.
14094 // Instead we should extend the content on the last raw string.
14095 atInterpolation = false;
14096 extendLastString = true;
14097 break;
14098 }
14099 const fullEnd = exprEnd + interpEnd.length;
14100 const text = input.substring(exprStart, exprEnd);
14101 if (text.trim().length === 0) {
14102 this._reportError('Blank expressions are not allowed in interpolated strings', input, `at column ${i} in`, location);
14103 }
14104 expressions.push({ text, start: fullStart, end: fullEnd });
14105 offsets.push(exprStart);
14106 i = fullEnd;
14107 atInterpolation = false;
14108 }
14109 }
14110 if (!atInterpolation) {
14111 // If we are now at a text section, add the remaining content as a raw string.
14112 if (extendLastString) {
14113 const piece = strings[strings.length - 1];
14114 piece.text += input.substring(i);
14115 piece.end = input.length;
14116 }
14117 else {
14118 strings.push({ text: input.substring(i), start: i, end: input.length });
14119 }
14120 }
14121 return new SplitInterpolation(strings, expressions, offsets);
14122 }
14123 wrapLiteralPrimitive(input, location, absoluteOffset) {
14124 const span = new ParseSpan(0, input == null ? 0 : input.length);
14125 return new ASTWithSource(new LiteralPrimitive(span, span.toAbsolute(absoluteOffset), input), input, location, absoluteOffset, this.errors);
14126 }
14127 _stripComments(input) {
14128 const i = this._commentStart(input);
14129 return i != null ? input.substring(0, i).trim() : input;
14130 }
14131 _commentStart(input) {
14132 let outerQuote = null;
14133 for (let i = 0; i < input.length - 1; i++) {
14134 const char = input.charCodeAt(i);
14135 const nextChar = input.charCodeAt(i + 1);
14136 if (char === $SLASH && nextChar == $SLASH && outerQuote == null)
14137 return i;
14138 if (outerQuote === char) {
14139 outerQuote = null;
14140 }
14141 else if (outerQuote == null && isQuote(char)) {
14142 outerQuote = char;
14143 }
14144 }
14145 return null;
14146 }
14147 _checkNoInterpolation(input, location, { start, end }) {
14148 let startIndex = -1;
14149 let endIndex = -1;
14150 for (const charIndex of this._forEachUnquotedChar(input, 0)) {
14151 if (startIndex === -1) {
14152 if (input.startsWith(start)) {
14153 startIndex = charIndex;
14154 }
14155 }
14156 else {
14157 endIndex = this._getInterpolationEndIndex(input, end, charIndex);
14158 if (endIndex > -1) {
14159 break;
14160 }
14161 }
14162 }
14163 if (startIndex > -1 && endIndex > -1) {
14164 this._reportError(`Got interpolation (${start}${end}) where expression was expected`, input, `at column ${startIndex} in`, location);
14165 }
14166 }
14167 /**
14168 * Finds the index of the end of an interpolation expression
14169 * while ignoring comments and quoted content.
14170 */
14171 _getInterpolationEndIndex(input, expressionEnd, start) {
14172 for (const charIndex of this._forEachUnquotedChar(input, start)) {
14173 if (input.startsWith(expressionEnd, charIndex)) {
14174 return charIndex;
14175 }
14176 // Nothing else in the expression matters after we've
14177 // hit a comment so look directly for the end token.
14178 if (input.startsWith('//', charIndex)) {
14179 return input.indexOf(expressionEnd, charIndex);
14180 }
14181 }
14182 return -1;
14183 }
14184 /**
14185 * Generator used to iterate over the character indexes of a string that are outside of quotes.
14186 * @param input String to loop through.
14187 * @param start Index within the string at which to start.
14188 */
14189 *_forEachUnquotedChar(input, start) {
14190 let currentQuote = null;
14191 let escapeCount = 0;
14192 for (let i = start; i < input.length; i++) {
14193 const char = input[i];
14194 // Skip the characters inside quotes. Note that we only care about the outer-most
14195 // quotes matching up and we need to account for escape characters.
14196 if (isQuote(input.charCodeAt(i)) && (currentQuote === null || currentQuote === char) &&
14197 escapeCount % 2 === 0) {
14198 currentQuote = currentQuote === null ? char : null;
14199 }
14200 else if (currentQuote === null) {
14201 yield i;
14202 }
14203 escapeCount = char === '\\' ? escapeCount + 1 : 0;
14204 }
14205 }
14206 }
14207 class IvyParser extends Parser {
14208 constructor() {
14209 super(...arguments);
14210 this.simpleExpressionChecker = IvySimpleExpressionChecker;
14211 }
14212 }
14213 /** Describes a stateful context an expression parser is in. */
14214 var ParseContextFlags;
14215 (function (ParseContextFlags) {
14216 ParseContextFlags[ParseContextFlags["None"] = 0] = "None";
14217 /**
14218 * A Writable context is one in which a value may be written to an lvalue.
14219 * For example, after we see a property access, we may expect a write to the
14220 * property via the "=" operator.
14221 * prop
14222 * ^ possible "=" after
14223 */
14224 ParseContextFlags[ParseContextFlags["Writable"] = 1] = "Writable";
14225 })(ParseContextFlags || (ParseContextFlags = {}));
14226 class _ParseAST {
14227 constructor(input, location, absoluteOffset, tokens, inputLength, parseAction, errors, offset) {
14228 this.input = input;
14229 this.location = location;
14230 this.absoluteOffset = absoluteOffset;
14231 this.tokens = tokens;
14232 this.inputLength = inputLength;
14233 this.parseAction = parseAction;
14234 this.errors = errors;
14235 this.offset = offset;
14236 this.rparensExpected = 0;
14237 this.rbracketsExpected = 0;
14238 this.rbracesExpected = 0;
14239 this.context = ParseContextFlags.None;
14240 // Cache of expression start and input indeces to the absolute source span they map to, used to
14241 // prevent creating superfluous source spans in `sourceSpan`.
14242 // A serial of the expression start and input index is used for mapping because both are stateful
14243 // and may change for subsequent expressions visited by the parser.
14244 this.sourceSpanCache = new Map();
14245 this.index = 0;
14246 }
14247 peek(offset) {
14248 const i = this.index + offset;
14249 return i < this.tokens.length ? this.tokens[i] : EOF;
14250 }
14251 get next() {
14252 return this.peek(0);
14253 }
14254 /** Whether all the parser input has been processed. */
14255 get atEOF() {
14256 return this.index >= this.tokens.length;
14257 }
14258 /**
14259 * Index of the next token to be processed, or the end of the last token if all have been
14260 * processed.
14261 */
14262 get inputIndex() {
14263 return this.atEOF ? this.currentEndIndex : this.next.index + this.offset;
14264 }
14265 /**
14266 * End index of the last processed token, or the start of the first token if none have been
14267 * processed.
14268 */
14269 get currentEndIndex() {
14270 if (this.index > 0) {
14271 const curToken = this.peek(-1);
14272 return curToken.end + this.offset;
14273 }
14274 // No tokens have been processed yet; return the next token's start or the length of the input
14275 // if there is no token.
14276 if (this.tokens.length === 0) {
14277 return this.inputLength + this.offset;
14278 }
14279 return this.next.index + this.offset;
14280 }
14281 /**
14282 * Returns the absolute offset of the start of the current token.
14283 */
14284 get currentAbsoluteOffset() {
14285 return this.absoluteOffset + this.inputIndex;
14286 }
14287 /**
14288 * Retrieve a `ParseSpan` from `start` to the current position (or to `artificialEndIndex` if
14289 * provided).
14290 *
14291 * @param start Position from which the `ParseSpan` will start.
14292 * @param artificialEndIndex Optional ending index to be used if provided (and if greater than the
14293 * natural ending index)
14294 */
14295 span(start, artificialEndIndex) {
14296 let endIndex = this.currentEndIndex;
14297 if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) {
14298 endIndex = artificialEndIndex;
14299 }
14300 // In some unusual parsing scenarios (like when certain tokens are missing and an `EmptyExpr` is
14301 // being created), the current token may already be advanced beyond the `currentEndIndex`. This
14302 // appears to be a deep-seated parser bug.
14303 //
14304 // As a workaround for now, swap the start and end indices to ensure a valid `ParseSpan`.
14305 // TODO(alxhub): fix the bug upstream in the parser state, and remove this workaround.
14306 if (start > endIndex) {
14307 const tmp = endIndex;
14308 endIndex = start;
14309 start = tmp;
14310 }
14311 return new ParseSpan(start, endIndex);
14312 }
14313 sourceSpan(start, artificialEndIndex) {
14314 const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`;
14315 if (!this.sourceSpanCache.has(serial)) {
14316 this.sourceSpanCache.set(serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset));
14317 }
14318 return this.sourceSpanCache.get(serial);
14319 }
14320 advance() {
14321 this.index++;
14322 }
14323 /**
14324 * Executes a callback in the provided context.
14325 */
14326 withContext(context, cb) {
14327 this.context |= context;
14328 const ret = cb();
14329 this.context ^= context;
14330 return ret;
14331 }
14332 consumeOptionalCharacter(code) {
14333 if (this.next.isCharacter(code)) {
14334 this.advance();
14335 return true;
14336 }
14337 else {
14338 return false;
14339 }
14340 }
14341 peekKeywordLet() {
14342 return this.next.isKeywordLet();
14343 }
14344 peekKeywordAs() {
14345 return this.next.isKeywordAs();
14346 }
14347 /**
14348 * Consumes an expected character, otherwise emits an error about the missing expected character
14349 * and skips over the token stream until reaching a recoverable point.
14350 *
14351 * See `this.error` and `this.skip` for more details.
14352 */
14353 expectCharacter(code) {
14354 if (this.consumeOptionalCharacter(code))
14355 return;
14356 this.error(`Missing expected ${String.fromCharCode(code)}`);
14357 }
14358 consumeOptionalOperator(op) {
14359 if (this.next.isOperator(op)) {
14360 this.advance();
14361 return true;
14362 }
14363 else {
14364 return false;
14365 }
14366 }
14367 expectOperator(operator) {
14368 if (this.consumeOptionalOperator(operator))
14369 return;
14370 this.error(`Missing expected operator ${operator}`);
14371 }
14372 prettyPrintToken(tok) {
14373 return tok === EOF ? 'end of input' : `token ${tok}`;
14374 }
14375 expectIdentifierOrKeyword() {
14376 const n = this.next;
14377 if (!n.isIdentifier() && !n.isKeyword()) {
14378 if (n.isPrivateIdentifier()) {
14379 this._reportErrorForPrivateIdentifier(n, 'expected identifier or keyword');
14380 }
14381 else {
14382 this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`);
14383 }
14384 return null;
14385 }
14386 this.advance();
14387 return n.toString();
14388 }
14389 expectIdentifierOrKeywordOrString() {
14390 const n = this.next;
14391 if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
14392 if (n.isPrivateIdentifier()) {
14393 this._reportErrorForPrivateIdentifier(n, 'expected identifier, keyword or string');
14394 }
14395 else {
14396 this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier, keyword, or string`);
14397 }
14398 return '';
14399 }
14400 this.advance();
14401 return n.toString();
14402 }
14403 parseChain() {
14404 const exprs = [];
14405 const start = this.inputIndex;
14406 while (this.index < this.tokens.length) {
14407 const expr = this.parsePipe();
14408 exprs.push(expr);
14409 if (this.consumeOptionalCharacter($SEMICOLON)) {
14410 if (!this.parseAction) {
14411 this.error('Binding expression cannot contain chained expression');
14412 }
14413 while (this.consumeOptionalCharacter($SEMICOLON)) {
14414 } // read all semicolons
14415 }
14416 else if (this.index < this.tokens.length) {
14417 this.error(`Unexpected token '${this.next}'`);
14418 }
14419 }
14420 if (exprs.length == 0) {
14421 // We have no expressions so create an empty expression that spans the entire input length
14422 const artificialStart = this.offset;
14423 const artificialEnd = this.offset + this.inputLength;
14424 return new EmptyExpr(this.span(artificialStart, artificialEnd), this.sourceSpan(artificialStart, artificialEnd));
14425 }
14426 if (exprs.length == 1)
14427 return exprs[0];
14428 return new Chain(this.span(start), this.sourceSpan(start), exprs);
14429 }
14430 parsePipe() {
14431 const start = this.inputIndex;
14432 let result = this.parseExpression();
14433 if (this.consumeOptionalOperator('|')) {
14434 if (this.parseAction) {
14435 this.error('Cannot have a pipe in an action expression');
14436 }
14437 do {
14438 const nameStart = this.inputIndex;
14439 let nameId = this.expectIdentifierOrKeyword();
14440 let nameSpan;
14441 let fullSpanEnd = undefined;
14442 if (nameId !== null) {
14443 nameSpan = this.sourceSpan(nameStart);
14444 }
14445 else {
14446 // No valid identifier was found, so we'll assume an empty pipe name ('').
14447 nameId = '';
14448 // However, there may have been whitespace present between the pipe character and the next
14449 // token in the sequence (or the end of input). We want to track this whitespace so that
14450 // the `BindingPipe` we produce covers not just the pipe character, but any trailing
14451 // whitespace beyond it. Another way of thinking about this is that the zero-length name
14452 // is assumed to be at the end of any whitespace beyond the pipe character.
14453 //
14454 // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the
14455 // beginning of the next token, or until the end of input if the next token is EOF.
14456 fullSpanEnd = this.next.index !== -1 ? this.next.index : this.inputLength + this.offset;
14457 // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace
14458 // beyond the pipe character.
14459 nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset);
14460 }
14461 const args = [];
14462 while (this.consumeOptionalCharacter($COLON)) {
14463 args.push(this.parseExpression());
14464 // If there are additional expressions beyond the name, then the artificial end for the
14465 // name is no longer relevant.
14466 }
14467 result = new BindingPipe(this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, nameSpan);
14468 } while (this.consumeOptionalOperator('|'));
14469 }
14470 return result;
14471 }
14472 parseExpression() {
14473 return this.parseConditional();
14474 }
14475 parseConditional() {
14476 const start = this.inputIndex;
14477 const result = this.parseLogicalOr();
14478 if (this.consumeOptionalOperator('?')) {
14479 const yes = this.parsePipe();
14480 let no;
14481 if (!this.consumeOptionalCharacter($COLON)) {
14482 const end = this.inputIndex;
14483 const expression = this.input.substring(start, end);
14484 this.error(`Conditional expression ${expression} requires all 3 expressions`);
14485 no = new EmptyExpr(this.span(start), this.sourceSpan(start));
14486 }
14487 else {
14488 no = this.parsePipe();
14489 }
14490 return new Conditional(this.span(start), this.sourceSpan(start), result, yes, no);
14491 }
14492 else {
14493 return result;
14494 }
14495 }
14496 parseLogicalOr() {
14497 // '||'
14498 const start = this.inputIndex;
14499 let result = this.parseLogicalAnd();
14500 while (this.consumeOptionalOperator('||')) {
14501 const right = this.parseLogicalAnd();
14502 result = new Binary(this.span(start), this.sourceSpan(start), '||', result, right);
14503 }
14504 return result;
14505 }
14506 parseLogicalAnd() {
14507 // '&&'
14508 const start = this.inputIndex;
14509 let result = this.parseNullishCoalescing();
14510 while (this.consumeOptionalOperator('&&')) {
14511 const right = this.parseNullishCoalescing();
14512 result = new Binary(this.span(start), this.sourceSpan(start), '&&', result, right);
14513 }
14514 return result;
14515 }
14516 parseNullishCoalescing() {
14517 // '??'
14518 const start = this.inputIndex;
14519 let result = this.parseEquality();
14520 while (this.consumeOptionalOperator('??')) {
14521 const right = this.parseEquality();
14522 result = new Binary(this.span(start), this.sourceSpan(start), '??', result, right);
14523 }
14524 return result;
14525 }
14526 parseEquality() {
14527 // '==','!=','===','!=='
14528 const start = this.inputIndex;
14529 let result = this.parseRelational();
14530 while (this.next.type == TokenType.Operator) {
14531 const operator = this.next.strValue;
14532 switch (operator) {
14533 case '==':
14534 case '===':
14535 case '!=':
14536 case '!==':
14537 this.advance();
14538 const right = this.parseRelational();
14539 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
14540 continue;
14541 }
14542 break;
14543 }
14544 return result;
14545 }
14546 parseRelational() {
14547 // '<', '>', '<=', '>='
14548 const start = this.inputIndex;
14549 let result = this.parseAdditive();
14550 while (this.next.type == TokenType.Operator) {
14551 const operator = this.next.strValue;
14552 switch (operator) {
14553 case '<':
14554 case '>':
14555 case '<=':
14556 case '>=':
14557 this.advance();
14558 const right = this.parseAdditive();
14559 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
14560 continue;
14561 }
14562 break;
14563 }
14564 return result;
14565 }
14566 parseAdditive() {
14567 // '+', '-'
14568 const start = this.inputIndex;
14569 let result = this.parseMultiplicative();
14570 while (this.next.type == TokenType.Operator) {
14571 const operator = this.next.strValue;
14572 switch (operator) {
14573 case '+':
14574 case '-':
14575 this.advance();
14576 let right = this.parseMultiplicative();
14577 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
14578 continue;
14579 }
14580 break;
14581 }
14582 return result;
14583 }
14584 parseMultiplicative() {
14585 // '*', '%', '/'
14586 const start = this.inputIndex;
14587 let result = this.parsePrefix();
14588 while (this.next.type == TokenType.Operator) {
14589 const operator = this.next.strValue;
14590 switch (operator) {
14591 case '*':
14592 case '%':
14593 case '/':
14594 this.advance();
14595 let right = this.parsePrefix();
14596 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
14597 continue;
14598 }
14599 break;
14600 }
14601 return result;
14602 }
14603 parsePrefix() {
14604 if (this.next.type == TokenType.Operator) {
14605 const start = this.inputIndex;
14606 const operator = this.next.strValue;
14607 let result;
14608 switch (operator) {
14609 case '+':
14610 this.advance();
14611 result = this.parsePrefix();
14612 return Unary.createPlus(this.span(start), this.sourceSpan(start), result);
14613 case '-':
14614 this.advance();
14615 result = this.parsePrefix();
14616 return Unary.createMinus(this.span(start), this.sourceSpan(start), result);
14617 case '!':
14618 this.advance();
14619 result = this.parsePrefix();
14620 return new PrefixNot(this.span(start), this.sourceSpan(start), result);
14621 }
14622 }
14623 return this.parseCallChain();
14624 }
14625 parseCallChain() {
14626 const start = this.inputIndex;
14627 let result = this.parsePrimary();
14628 while (true) {
14629 if (this.consumeOptionalCharacter($PERIOD)) {
14630 result = this.parseAccessMemberOrCall(result, start, false);
14631 }
14632 else if (this.consumeOptionalOperator('?.')) {
14633 result = this.consumeOptionalCharacter($LBRACKET) ?
14634 this.parseKeyedReadOrWrite(result, start, true) :
14635 this.parseAccessMemberOrCall(result, start, true);
14636 }
14637 else if (this.consumeOptionalCharacter($LBRACKET)) {
14638 result = this.parseKeyedReadOrWrite(result, start, false);
14639 }
14640 else if (this.consumeOptionalCharacter($LPAREN)) {
14641 const argumentStart = this.inputIndex;
14642 this.rparensExpected++;
14643 const args = this.parseCallArguments();
14644 const argumentSpan = this.span(argumentStart, this.inputIndex).toAbsolute(this.absoluteOffset);
14645 this.rparensExpected--;
14646 this.expectCharacter($RPAREN);
14647 result = new Call(this.span(start), this.sourceSpan(start), result, args, argumentSpan);
14648 }
14649 else if (this.consumeOptionalOperator('!')) {
14650 result = new NonNullAssert(this.span(start), this.sourceSpan(start), result);
14651 }
14652 else {
14653 return result;
14654 }
14655 }
14656 }
14657 parsePrimary() {
14658 const start = this.inputIndex;
14659 if (this.consumeOptionalCharacter($LPAREN)) {
14660 this.rparensExpected++;
14661 const result = this.parsePipe();
14662 this.rparensExpected--;
14663 this.expectCharacter($RPAREN);
14664 return result;
14665 }
14666 else if (this.next.isKeywordNull()) {
14667 this.advance();
14668 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), null);
14669 }
14670 else if (this.next.isKeywordUndefined()) {
14671 this.advance();
14672 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), void 0);
14673 }
14674 else if (this.next.isKeywordTrue()) {
14675 this.advance();
14676 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), true);
14677 }
14678 else if (this.next.isKeywordFalse()) {
14679 this.advance();
14680 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), false);
14681 }
14682 else if (this.next.isKeywordThis()) {
14683 this.advance();
14684 return new ThisReceiver(this.span(start), this.sourceSpan(start));
14685 }
14686 else if (this.consumeOptionalCharacter($LBRACKET)) {
14687 this.rbracketsExpected++;
14688 const elements = this.parseExpressionList($RBRACKET);
14689 this.rbracketsExpected--;
14690 this.expectCharacter($RBRACKET);
14691 return new LiteralArray(this.span(start), this.sourceSpan(start), elements);
14692 }
14693 else if (this.next.isCharacter($LBRACE)) {
14694 return this.parseLiteralMap();
14695 }
14696 else if (this.next.isIdentifier()) {
14697 return this.parseAccessMemberOrCall(new ImplicitReceiver(this.span(start), this.sourceSpan(start)), start, false);
14698 }
14699 else if (this.next.isNumber()) {
14700 const value = this.next.toNumber();
14701 this.advance();
14702 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), value);
14703 }
14704 else if (this.next.isString()) {
14705 const literalValue = this.next.toString();
14706 this.advance();
14707 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), literalValue);
14708 }
14709 else if (this.next.isPrivateIdentifier()) {
14710 this._reportErrorForPrivateIdentifier(this.next, null);
14711 return new EmptyExpr(this.span(start), this.sourceSpan(start));
14712 }
14713 else if (this.index >= this.tokens.length) {
14714 this.error(`Unexpected end of expression: ${this.input}`);
14715 return new EmptyExpr(this.span(start), this.sourceSpan(start));
14716 }
14717 else {
14718 this.error(`Unexpected token ${this.next}`);
14719 return new EmptyExpr(this.span(start), this.sourceSpan(start));
14720 }
14721 }
14722 parseExpressionList(terminator) {
14723 const result = [];
14724 do {
14725 if (!this.next.isCharacter(terminator)) {
14726 result.push(this.parsePipe());
14727 }
14728 else {
14729 break;
14730 }
14731 } while (this.consumeOptionalCharacter($COMMA));
14732 return result;
14733 }
14734 parseLiteralMap() {
14735 const keys = [];
14736 const values = [];
14737 const start = this.inputIndex;
14738 this.expectCharacter($LBRACE);
14739 if (!this.consumeOptionalCharacter($RBRACE)) {
14740 this.rbracesExpected++;
14741 do {
14742 const keyStart = this.inputIndex;
14743 const quoted = this.next.isString();
14744 const key = this.expectIdentifierOrKeywordOrString();
14745 keys.push({ key, quoted });
14746 // Properties with quoted keys can't use the shorthand syntax.
14747 if (quoted) {
14748 this.expectCharacter($COLON);
14749 values.push(this.parsePipe());
14750 }
14751 else if (this.consumeOptionalCharacter($COLON)) {
14752 values.push(this.parsePipe());
14753 }
14754 else {
14755 const span = this.span(keyStart);
14756 const sourceSpan = this.sourceSpan(keyStart);
14757 values.push(new PropertyRead(span, sourceSpan, sourceSpan, new ImplicitReceiver(span, sourceSpan), key));
14758 }
14759 } while (this.consumeOptionalCharacter($COMMA));
14760 this.rbracesExpected--;
14761 this.expectCharacter($RBRACE);
14762 }
14763 return new LiteralMap(this.span(start), this.sourceSpan(start), keys, values);
14764 }
14765 parseAccessMemberOrCall(readReceiver, start, isSafe) {
14766 const nameStart = this.inputIndex;
14767 const id = this.withContext(ParseContextFlags.Writable, () => {
14768 const id = this.expectIdentifierOrKeyword() ?? '';
14769 if (id.length === 0) {
14770 this.error(`Expected identifier for property access`, readReceiver.span.end);
14771 }
14772 return id;
14773 });
14774 const nameSpan = this.sourceSpan(nameStart);
14775 let receiver;
14776 if (isSafe) {
14777 if (this.consumeOptionalOperator('=')) {
14778 this.error('The \'?.\' operator cannot be used in the assignment');
14779 receiver = new EmptyExpr(this.span(start), this.sourceSpan(start));
14780 }
14781 else {
14782 receiver = new SafePropertyRead(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id);
14783 }
14784 }
14785 else {
14786 if (this.consumeOptionalOperator('=')) {
14787 if (!this.parseAction) {
14788 this.error('Bindings cannot contain assignments');
14789 return new EmptyExpr(this.span(start), this.sourceSpan(start));
14790 }
14791 const value = this.parseConditional();
14792 receiver = new PropertyWrite(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id, value);
14793 }
14794 else {
14795 receiver =
14796 new PropertyRead(this.span(start), this.sourceSpan(start), nameSpan, readReceiver, id);
14797 }
14798 }
14799 if (this.consumeOptionalCharacter($LPAREN)) {
14800 const argumentStart = this.inputIndex;
14801 this.rparensExpected++;
14802 const args = this.parseCallArguments();
14803 const argumentSpan = this.span(argumentStart, this.inputIndex).toAbsolute(this.absoluteOffset);
14804 this.expectCharacter($RPAREN);
14805 this.rparensExpected--;
14806 const span = this.span(start);
14807 const sourceSpan = this.sourceSpan(start);
14808 return new Call(span, sourceSpan, receiver, args, argumentSpan);
14809 }
14810 return receiver;
14811 }
14812 parseCallArguments() {
14813 if (this.next.isCharacter($RPAREN))
14814 return [];
14815 const positionals = [];
14816 do {
14817 positionals.push(this.parsePipe());
14818 } while (this.consumeOptionalCharacter($COMMA));
14819 return positionals;
14820 }
14821 /**
14822 * Parses an identifier, a keyword, a string with an optional `-` in between,
14823 * and returns the string along with its absolute source span.
14824 */
14825 expectTemplateBindingKey() {
14826 let result = '';
14827 let operatorFound = false;
14828 const start = this.currentAbsoluteOffset;
14829 do {
14830 result += this.expectIdentifierOrKeywordOrString();
14831 operatorFound = this.consumeOptionalOperator('-');
14832 if (operatorFound) {
14833 result += '-';
14834 }
14835 } while (operatorFound);
14836 return {
14837 source: result,
14838 span: new AbsoluteSourceSpan$1(start, start + result.length),
14839 };
14840 }
14841 /**
14842 * Parse microsyntax template expression and return a list of bindings or
14843 * parsing errors in case the given expression is invalid.
14844 *
14845 * For example,
14846 * ```
14847 * <div *ngFor="let item of items; index as i; trackBy: func">
14848 * ```
14849 * contains five bindings:
14850 * 1. ngFor -> null
14851 * 2. item -> NgForOfContext.$implicit
14852 * 3. ngForOf -> items
14853 * 4. i -> NgForOfContext.index
14854 * 5. ngForTrackBy -> func
14855 *
14856 * For a full description of the microsyntax grammar, see
14857 * https://gist.github.com/mhevery/d3530294cff2e4a1b3fe15ff75d08855
14858 *
14859 * @param templateKey name of the microsyntax directive, like ngIf, ngFor,
14860 * without the *, along with its absolute span.
14861 */
14862 parseTemplateBindings(templateKey) {
14863 const bindings = [];
14864 // The first binding is for the template key itself
14865 // In *ngFor="let item of items", key = "ngFor", value = null
14866 // In *ngIf="cond | pipe", key = "ngIf", value = "cond | pipe"
14867 bindings.push(...this.parseDirectiveKeywordBindings(templateKey));
14868 while (this.index < this.tokens.length) {
14869 // If it starts with 'let', then this must be variable declaration
14870 const letBinding = this.parseLetBinding();
14871 if (letBinding) {
14872 bindings.push(letBinding);
14873 }
14874 else {
14875 // Two possible cases here, either `value "as" key` or
14876 // "directive-keyword expression". We don't know which case, but both
14877 // "value" and "directive-keyword" are template binding key, so consume
14878 // the key first.
14879 const key = this.expectTemplateBindingKey();
14880 // Peek at the next token, if it is "as" then this must be variable
14881 // declaration.
14882 const binding = this.parseAsBinding(key);
14883 if (binding) {
14884 bindings.push(binding);
14885 }
14886 else {
14887 // Otherwise the key must be a directive keyword, like "of". Transform
14888 // the key to actual key. Eg. of -> ngForOf, trackBy -> ngForTrackBy
14889 key.source =
14890 templateKey.source + key.source.charAt(0).toUpperCase() + key.source.substring(1);
14891 bindings.push(...this.parseDirectiveKeywordBindings(key));
14892 }
14893 }
14894 this.consumeStatementTerminator();
14895 }
14896 return new TemplateBindingParseResult(bindings, [] /* warnings */, this.errors);
14897 }
14898 parseKeyedReadOrWrite(receiver, start, isSafe) {
14899 return this.withContext(ParseContextFlags.Writable, () => {
14900 this.rbracketsExpected++;
14901 const key = this.parsePipe();
14902 if (key instanceof EmptyExpr) {
14903 this.error(`Key access cannot be empty`);
14904 }
14905 this.rbracketsExpected--;
14906 this.expectCharacter($RBRACKET);
14907 if (this.consumeOptionalOperator('=')) {
14908 if (isSafe) {
14909 this.error('The \'?.\' operator cannot be used in the assignment');
14910 }
14911 else {
14912 const value = this.parseConditional();
14913 return new KeyedWrite(this.span(start), this.sourceSpan(start), receiver, key, value);
14914 }
14915 }
14916 else {
14917 return isSafe ? new SafeKeyedRead(this.span(start), this.sourceSpan(start), receiver, key) :
14918 new KeyedRead(this.span(start), this.sourceSpan(start), receiver, key);
14919 }
14920 return new EmptyExpr(this.span(start), this.sourceSpan(start));
14921 });
14922 }
14923 /**
14924 * Parse a directive keyword, followed by a mandatory expression.
14925 * For example, "of items", "trackBy: func".
14926 * The bindings are: ngForOf -> items, ngForTrackBy -> func
14927 * There could be an optional "as" binding that follows the expression.
14928 * For example,
14929 * ```
14930 * *ngFor="let item of items | slice:0:1 as collection".
14931 * ^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
14932 * keyword bound target optional 'as' binding
14933 * ```
14934 *
14935 * @param key binding key, for example, ngFor, ngIf, ngForOf, along with its
14936 * absolute span.
14937 */
14938 parseDirectiveKeywordBindings(key) {
14939 const bindings = [];
14940 this.consumeOptionalCharacter($COLON); // trackBy: trackByFunction
14941 const value = this.getDirectiveBoundTarget();
14942 let spanEnd = this.currentAbsoluteOffset;
14943 // The binding could optionally be followed by "as". For example,
14944 // *ngIf="cond | pipe as x". In this case, the key in the "as" binding
14945 // is "x" and the value is the template key itself ("ngIf"). Note that the
14946 // 'key' in the current context now becomes the "value" in the next binding.
14947 const asBinding = this.parseAsBinding(key);
14948 if (!asBinding) {
14949 this.consumeStatementTerminator();
14950 spanEnd = this.currentAbsoluteOffset;
14951 }
14952 const sourceSpan = new AbsoluteSourceSpan$1(key.span.start, spanEnd);
14953 bindings.push(new ExpressionBinding(sourceSpan, key, value));
14954 if (asBinding) {
14955 bindings.push(asBinding);
14956 }
14957 return bindings;
14958 }
14959 /**
14960 * Return the expression AST for the bound target of a directive keyword
14961 * binding. For example,
14962 * ```
14963 * *ngIf="condition | pipe"
14964 * ^^^^^^^^^^^^^^^^ bound target for "ngIf"
14965 * *ngFor="let item of items"
14966 * ^^^^^ bound target for "ngForOf"
14967 * ```
14968 */
14969 getDirectiveBoundTarget() {
14970 if (this.next === EOF || this.peekKeywordAs() || this.peekKeywordLet()) {
14971 return null;
14972 }
14973 const ast = this.parsePipe(); // example: "condition | async"
14974 const { start, end } = ast.span;
14975 const value = this.input.substring(start, end);
14976 return new ASTWithSource(ast, value, this.location, this.absoluteOffset + start, this.errors);
14977 }
14978 /**
14979 * Return the binding for a variable declared using `as`. Note that the order
14980 * of the key-value pair in this declaration is reversed. For example,
14981 * ```
14982 * *ngFor="let item of items; index as i"
14983 * ^^^^^ ^
14984 * value key
14985 * ```
14986 *
14987 * @param value name of the value in the declaration, "ngIf" in the example
14988 * above, along with its absolute span.
14989 */
14990 parseAsBinding(value) {
14991 if (!this.peekKeywordAs()) {
14992 return null;
14993 }
14994 this.advance(); // consume the 'as' keyword
14995 const key = this.expectTemplateBindingKey();
14996 this.consumeStatementTerminator();
14997 const sourceSpan = new AbsoluteSourceSpan$1(value.span.start, this.currentAbsoluteOffset);
14998 return new VariableBinding(sourceSpan, key, value);
14999 }
15000 /**
15001 * Return the binding for a variable declared using `let`. For example,
15002 * ```
15003 * *ngFor="let item of items; let i=index;"
15004 * ^^^^^^^^ ^^^^^^^^^^^
15005 * ```
15006 * In the first binding, `item` is bound to `NgForOfContext.$implicit`.
15007 * In the second binding, `i` is bound to `NgForOfContext.index`.
15008 */
15009 parseLetBinding() {
15010 if (!this.peekKeywordLet()) {
15011 return null;
15012 }
15013 const spanStart = this.currentAbsoluteOffset;
15014 this.advance(); // consume the 'let' keyword
15015 const key = this.expectTemplateBindingKey();
15016 let value = null;
15017 if (this.consumeOptionalOperator('=')) {
15018 value = this.expectTemplateBindingKey();
15019 }
15020 this.consumeStatementTerminator();
15021 const sourceSpan = new AbsoluteSourceSpan$1(spanStart, this.currentAbsoluteOffset);
15022 return new VariableBinding(sourceSpan, key, value);
15023 }
15024 /**
15025 * Consume the optional statement terminator: semicolon or comma.
15026 */
15027 consumeStatementTerminator() {
15028 this.consumeOptionalCharacter($SEMICOLON) || this.consumeOptionalCharacter($COMMA);
15029 }
15030 /**
15031 * Records an error and skips over the token stream until reaching a recoverable point. See
15032 * `this.skip` for more details on token skipping.
15033 */
15034 error(message, index = null) {
15035 this.errors.push(new ParserError(message, this.input, this.locationText(index), this.location));
15036 this.skip();
15037 }
15038 locationText(index = null) {
15039 if (index == null)
15040 index = this.index;
15041 return (index < this.tokens.length) ? `at column ${this.tokens[index].index + 1} in` :
15042 `at the end of the expression`;
15043 }
15044 /**
15045 * Records an error for an unexpected private identifier being discovered.
15046 * @param token Token representing a private identifier.
15047 * @param extraMessage Optional additional message being appended to the error.
15048 */
15049 _reportErrorForPrivateIdentifier(token, extraMessage) {
15050 let errorMessage = `Private identifiers are not supported. Unexpected private identifier: ${token}`;
15051 if (extraMessage !== null) {
15052 errorMessage += `, ${extraMessage}`;
15053 }
15054 this.error(errorMessage);
15055 }
15056 /**
15057 * Error recovery should skip tokens until it encounters a recovery point.
15058 *
15059 * The following are treated as unconditional recovery points:
15060 * - end of input
15061 * - ';' (parseChain() is always the root production, and it expects a ';')
15062 * - '|' (since pipes may be chained and each pipe expression may be treated independently)
15063 *
15064 * The following are conditional recovery points:
15065 * - ')', '}', ']' if one of calling productions is expecting one of these symbols
15066 * - This allows skip() to recover from errors such as '(a.) + 1' allowing more of the AST to
15067 * be retained (it doesn't skip any tokens as the ')' is retained because of the '(' begins
15068 * an '(' <expr> ')' production).
15069 * The recovery points of grouping symbols must be conditional as they must be skipped if
15070 * none of the calling productions are not expecting the closing token else we will never
15071 * make progress in the case of an extraneous group closing symbol (such as a stray ')').
15072 * That is, we skip a closing symbol if we are not in a grouping production.
15073 * - '=' in a `Writable` context
15074 * - In this context, we are able to recover after seeing the `=` operator, which
15075 * signals the presence of an independent rvalue expression following the `=` operator.
15076 *
15077 * If a production expects one of these token it increments the corresponding nesting count,
15078 * and then decrements it just prior to checking if the token is in the input.
15079 */
15080 skip() {
15081 let n = this.next;
15082 while (this.index < this.tokens.length && !n.isCharacter($SEMICOLON) &&
15083 !n.isOperator('|') && (this.rparensExpected <= 0 || !n.isCharacter($RPAREN)) &&
15084 (this.rbracesExpected <= 0 || !n.isCharacter($RBRACE)) &&
15085 (this.rbracketsExpected <= 0 || !n.isCharacter($RBRACKET)) &&
15086 (!(this.context & ParseContextFlags.Writable) || !n.isOperator('='))) {
15087 if (this.next.isError()) {
15088 this.errors.push(new ParserError(this.next.toString(), this.input, this.locationText(), this.location));
15089 }
15090 this.advance();
15091 n = this.next;
15092 }
15093 }
15094 }
15095 class SimpleExpressionChecker {
15096 constructor() {
15097 this.errors = [];
15098 }
15099 visitImplicitReceiver(ast, context) { }
15100 visitThisReceiver(ast, context) { }
15101 visitInterpolation(ast, context) { }
15102 visitLiteralPrimitive(ast, context) { }
15103 visitPropertyRead(ast, context) { }
15104 visitPropertyWrite(ast, context) { }
15105 visitSafePropertyRead(ast, context) { }
15106 visitCall(ast, context) { }
15107 visitLiteralArray(ast, context) {
15108 this.visitAll(ast.expressions, context);
15109 }
15110 visitLiteralMap(ast, context) {
15111 this.visitAll(ast.values, context);
15112 }
15113 visitUnary(ast, context) { }
15114 visitBinary(ast, context) { }
15115 visitPrefixNot(ast, context) { }
15116 visitNonNullAssert(ast, context) { }
15117 visitConditional(ast, context) { }
15118 visitPipe(ast, context) {
15119 this.errors.push('pipes');
15120 }
15121 visitKeyedRead(ast, context) { }
15122 visitKeyedWrite(ast, context) { }
15123 visitAll(asts, context) {
15124 return asts.map(node => node.visit(this, context));
15125 }
15126 visitChain(ast, context) { }
15127 visitQuote(ast, context) { }
15128 visitSafeKeyedRead(ast, context) { }
15129 }
15130 /**
15131 * This class implements SimpleExpressionChecker used in View Engine and performs more strict checks
15132 * to make sure host bindings do not contain pipes. In View Engine, having pipes in host bindings is
15133 * not supported as well, but in some cases (like `!(value | async)`) the error is not triggered at
15134 * compile time. In order to preserve View Engine behavior, more strict checks are introduced for
15135 * Ivy mode only.
15136 */
15137 class IvySimpleExpressionChecker extends RecursiveAstVisitor {
15138 constructor() {
15139 super(...arguments);
15140 this.errors = [];
15141 }
15142 visitPipe() {
15143 this.errors.push('pipes');
15144 }
15145 }
15146
15147 /**
15148 * @license
15149 * Copyright Google LLC All Rights Reserved.
15150 *
15151 * Use of this source code is governed by an MIT-style license that can be
15152 * found in the LICENSE file at https://angular.io/license
15153 */
15154 class HtmlParser extends Parser$1 {
15155 constructor() {
15156 super(getHtmlTagDefinition);
15157 }
15158 parse(source, url, options) {
15159 return super.parse(source, url, options);
15160 }
15161 }
15162
15163 /**
15164 * @license
15165 * Copyright Google LLC All Rights Reserved.
15166 *
15167 * Use of this source code is governed by an MIT-style license that can be
15168 * found in the LICENSE file at https://angular.io/license
15169 */
15170 function mapLiteral(obj, quoted = false) {
15171 return literalMap(Object.keys(obj).map(key => ({
15172 key,
15173 quoted,
15174 value: obj[key],
15175 })));
15176 }
15177
15178 /**
15179 * @license
15180 * Copyright Google LLC All Rights Reserved.
15181 *
15182 * Use of this source code is governed by an MIT-style license that can be
15183 * found in the LICENSE file at https://angular.io/license
15184 */
15185 // =================================================================================================
15186 // =================================================================================================
15187 // =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
15188 // =================================================================================================
15189 // =================================================================================================
15190 //
15191 // DO NOT EDIT THIS LIST OF SECURITY SENSITIVE PROPERTIES WITHOUT A SECURITY REVIEW!
15192 // Reach out to mprobst for details.
15193 //
15194 // =================================================================================================
15195 /** Map from tagName|propertyName to SecurityContext. Properties applying to all tags use '*'. */
15196 let _SECURITY_SCHEMA;
15197 function SECURITY_SCHEMA() {
15198 if (!_SECURITY_SCHEMA) {
15199 _SECURITY_SCHEMA = {};
15200 // Case is insignificant below, all element and attribute names are lower-cased for lookup.
15201 registerContext(SecurityContext.HTML, [
15202 'iframe|srcdoc',
15203 '*|innerHTML',
15204 '*|outerHTML',
15205 ]);
15206 registerContext(SecurityContext.STYLE, ['*|style']);
15207 // NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them.
15208 registerContext(SecurityContext.URL, [
15209 '*|formAction', 'area|href', 'area|ping', 'audio|src', 'a|href',
15210 'a|ping', 'blockquote|cite', 'body|background', 'del|cite', 'form|action',
15211 'img|src', 'img|srcset', 'input|src', 'ins|cite', 'q|cite',
15212 'source|src', 'source|srcset', 'track|src', 'video|poster', 'video|src',
15213 ]);
15214 registerContext(SecurityContext.RESOURCE_URL, [
15215 'applet|code',
15216 'applet|codebase',
15217 'base|href',
15218 'embed|src',
15219 'frame|src',
15220 'head|profile',
15221 'html|manifest',
15222 'iframe|src',
15223 'link|href',
15224 'media|src',
15225 'object|codebase',
15226 'object|data',
15227 'script|src',
15228 ]);
15229 }
15230 return _SECURITY_SCHEMA;
15231 }
15232 function registerContext(ctx, specs) {
15233 for (const spec of specs)
15234 _SECURITY_SCHEMA[spec.toLowerCase()] = ctx;
15235 }
15236
15237 /**
15238 * @license
15239 * Copyright Google LLC All Rights Reserved.
15240 *
15241 * Use of this source code is governed by an MIT-style license that can be
15242 * found in the LICENSE file at https://angular.io/license
15243 */
15244 class ElementSchemaRegistry {
15245 }
15246
15247 /**
15248 * @license
15249 * Copyright Google LLC All Rights Reserved.
15250 *
15251 * Use of this source code is governed by an MIT-style license that can be
15252 * found in the LICENSE file at https://angular.io/license
15253 */
15254 const BOOLEAN = 'boolean';
15255 const NUMBER = 'number';
15256 const STRING = 'string';
15257 const OBJECT = 'object';
15258 /**
15259 * This array represents the DOM schema. It encodes inheritance, properties, and events.
15260 *
15261 * ## Overview
15262 *
15263 * Each line represents one kind of element. The `element_inheritance` and properties are joined
15264 * using `element_inheritance|properties` syntax.
15265 *
15266 * ## Element Inheritance
15267 *
15268 * The `element_inheritance` can be further subdivided as `element1,element2,...^parentElement`.
15269 * Here the individual elements are separated by `,` (commas). Every element in the list
15270 * has identical properties.
15271 *
15272 * An `element` may inherit additional properties from `parentElement` If no `^parentElement` is
15273 * specified then `""` (blank) element is assumed.
15274 *
15275 * NOTE: The blank element inherits from root `[Element]` element, the super element of all
15276 * elements.
15277 *
15278 * NOTE an element prefix such as `:svg:` has no special meaning to the schema.
15279 *
15280 * ## Properties
15281 *
15282 * Each element has a set of properties separated by `,` (commas). Each property can be prefixed
15283 * by a special character designating its type:
15284 *
15285 * - (no prefix): property is a string.
15286 * - `*`: property represents an event.
15287 * - `!`: property is a boolean.
15288 * - `#`: property is a number.
15289 * - `%`: property is an object.
15290 *
15291 * ## Query
15292 *
15293 * The class creates an internal squas representation which allows to easily answer the query of
15294 * if a given property exist on a given element.
15295 *
15296 * NOTE: We don't yet support querying for types or events.
15297 * NOTE: This schema is auto extracted from `schema_extractor.ts` located in the test folder,
15298 * see dom_element_schema_registry_spec.ts
15299 */
15300 // =================================================================================================
15301 // =================================================================================================
15302 // =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
15303 // =================================================================================================
15304 // =================================================================================================
15305 //
15306 // DO NOT EDIT THIS DOM SCHEMA WITHOUT A SECURITY REVIEW!
15307 //
15308 // Newly added properties must be security reviewed and assigned an appropriate SecurityContext in
15309 // dom_security_schema.ts. Reach out to mprobst & rjamet for details.
15310 //
15311 // =================================================================================================
15312 const SCHEMA = [
15313 '[Element]|textContent,%classList,className,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*copy,*cut,*paste,*search,*selectstart,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerHTML,#scrollLeft,#scrollTop,slot' +
15314 /* added manually to avoid breaking changes */
15315 ',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored',
15316 '[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',
15317 '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',
15318 'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,src,%srcObject,#volume',
15319 ':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',
15320 ':svg:graphics^:svg:|',
15321 ':svg:animation^:svg:|*begin,*end,*repeat',
15322 ':svg:geometry^:svg:|',
15323 ':svg:componentTransferFunction^:svg:|',
15324 ':svg:gradient^:svg:|',
15325 ':svg:textContent^:svg:graphics|',
15326 ':svg:textPositioning^:svg:textContent|',
15327 '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',
15328 'area^[HTMLElement]|alt,coords,download,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,referrerPolicy,rel,search,shape,target,username',
15329 'audio^media|',
15330 'br^[HTMLElement]|clear',
15331 'base^[HTMLElement]|href,target',
15332 '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',
15333 'button^[HTMLElement]|!autofocus,!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value',
15334 'canvas^[HTMLElement]|#height,#width',
15335 'content^[HTMLElement]|select',
15336 'dl^[HTMLElement]|!compact',
15337 'datalist^[HTMLElement]|',
15338 'details^[HTMLElement]|!open',
15339 'dialog^[HTMLElement]|!open,returnValue',
15340 'dir^[HTMLElement]|!compact',
15341 'div^[HTMLElement]|align',
15342 'embed^[HTMLElement]|align,height,name,src,type,width',
15343 'fieldset^[HTMLElement]|!disabled,name',
15344 'font^[HTMLElement]|color,face,size',
15345 'form^[HTMLElement]|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target',
15346 'frame^[HTMLElement]|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src',
15347 'frameset^[HTMLElement]|cols,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows',
15348 'hr^[HTMLElement]|align,color,!noShade,size,width',
15349 'head^[HTMLElement]|',
15350 'h1,h2,h3,h4,h5,h6^[HTMLElement]|align',
15351 'html^[HTMLElement]|version',
15352 'iframe^[HTMLElement]|align,!allowFullscreen,frameBorder,height,longDesc,marginHeight,marginWidth,name,referrerPolicy,%sandbox,scrolling,src,srcdoc,width',
15353 'img^[HTMLElement]|align,alt,border,%crossOrigin,#height,#hspace,!isMap,longDesc,lowsrc,name,referrerPolicy,sizes,src,srcset,useMap,#vspace,#width',
15354 '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',
15355 'li^[HTMLElement]|type,#value',
15356 'label^[HTMLElement]|htmlFor',
15357 'legend^[HTMLElement]|align',
15358 'link^[HTMLElement]|as,charset,%crossOrigin,!disabled,href,hreflang,integrity,media,referrerPolicy,rel,%relList,rev,%sizes,target,type',
15359 'map^[HTMLElement]|name',
15360 'marquee^[HTMLElement]|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width',
15361 'menu^[HTMLElement]|!compact',
15362 'meta^[HTMLElement]|content,httpEquiv,name,scheme',
15363 'meter^[HTMLElement]|#high,#low,#max,#min,#optimum,#value',
15364 'ins,del^[HTMLElement]|cite,dateTime',
15365 'ol^[HTMLElement]|!compact,!reversed,#start,type',
15366 'object^[HTMLElement]|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width',
15367 'optgroup^[HTMLElement]|!disabled,label',
15368 'option^[HTMLElement]|!defaultSelected,!disabled,label,!selected,text,value',
15369 'output^[HTMLElement]|defaultValue,%htmlFor,name,value',
15370 'p^[HTMLElement]|align',
15371 'param^[HTMLElement]|name,type,value,valueType',
15372 'picture^[HTMLElement]|',
15373 'pre^[HTMLElement]|#width',
15374 'progress^[HTMLElement]|#max,#value',
15375 'q,blockquote,cite^[HTMLElement]|',
15376 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,src,text,type',
15377 'select^[HTMLElement]|autocomplete,!autofocus,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value',
15378 'shadow^[HTMLElement]|',
15379 'slot^[HTMLElement]|name',
15380 'source^[HTMLElement]|media,sizes,src,srcset,type',
15381 'span^[HTMLElement]|',
15382 'style^[HTMLElement]|!disabled,media,type',
15383 'caption^[HTMLElement]|align',
15384 'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width',
15385 'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width',
15386 'table^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width',
15387 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign',
15388 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign',
15389 'template^[HTMLElement]|',
15390 'textarea^[HTMLElement]|autocapitalize,autocomplete,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap',
15391 'title^[HTMLElement]|text',
15392 'track^[HTMLElement]|!default,kind,label,src,srclang',
15393 'ul^[HTMLElement]|!compact,type',
15394 'unknown^[HTMLElement]|',
15395 'video^media|#height,poster,#width',
15396 ':svg:a^:svg:graphics|',
15397 ':svg:animate^:svg:animation|',
15398 ':svg:animateMotion^:svg:animation|',
15399 ':svg:animateTransform^:svg:animation|',
15400 ':svg:circle^:svg:geometry|',
15401 ':svg:clipPath^:svg:graphics|',
15402 ':svg:defs^:svg:graphics|',
15403 ':svg:desc^:svg:|',
15404 ':svg:discard^:svg:|',
15405 ':svg:ellipse^:svg:geometry|',
15406 ':svg:feBlend^:svg:|',
15407 ':svg:feColorMatrix^:svg:|',
15408 ':svg:feComponentTransfer^:svg:|',
15409 ':svg:feComposite^:svg:|',
15410 ':svg:feConvolveMatrix^:svg:|',
15411 ':svg:feDiffuseLighting^:svg:|',
15412 ':svg:feDisplacementMap^:svg:|',
15413 ':svg:feDistantLight^:svg:|',
15414 ':svg:feDropShadow^:svg:|',
15415 ':svg:feFlood^:svg:|',
15416 ':svg:feFuncA^:svg:componentTransferFunction|',
15417 ':svg:feFuncB^:svg:componentTransferFunction|',
15418 ':svg:feFuncG^:svg:componentTransferFunction|',
15419 ':svg:feFuncR^:svg:componentTransferFunction|',
15420 ':svg:feGaussianBlur^:svg:|',
15421 ':svg:feImage^:svg:|',
15422 ':svg:feMerge^:svg:|',
15423 ':svg:feMergeNode^:svg:|',
15424 ':svg:feMorphology^:svg:|',
15425 ':svg:feOffset^:svg:|',
15426 ':svg:fePointLight^:svg:|',
15427 ':svg:feSpecularLighting^:svg:|',
15428 ':svg:feSpotLight^:svg:|',
15429 ':svg:feTile^:svg:|',
15430 ':svg:feTurbulence^:svg:|',
15431 ':svg:filter^:svg:|',
15432 ':svg:foreignObject^:svg:graphics|',
15433 ':svg:g^:svg:graphics|',
15434 ':svg:image^:svg:graphics|',
15435 ':svg:line^:svg:geometry|',
15436 ':svg:linearGradient^:svg:gradient|',
15437 ':svg:mpath^:svg:|',
15438 ':svg:marker^:svg:|',
15439 ':svg:mask^:svg:|',
15440 ':svg:metadata^:svg:|',
15441 ':svg:path^:svg:geometry|',
15442 ':svg:pattern^:svg:|',
15443 ':svg:polygon^:svg:geometry|',
15444 ':svg:polyline^:svg:geometry|',
15445 ':svg:radialGradient^:svg:gradient|',
15446 ':svg:rect^:svg:geometry|',
15447 ':svg:svg^:svg:graphics|#currentScale,#zoomAndPan',
15448 ':svg:script^:svg:|type',
15449 ':svg:set^:svg:animation|',
15450 ':svg:stop^:svg:|',
15451 ':svg:style^:svg:|!disabled,media,title,type',
15452 ':svg:switch^:svg:graphics|',
15453 ':svg:symbol^:svg:|',
15454 ':svg:tspan^:svg:textPositioning|',
15455 ':svg:text^:svg:textPositioning|',
15456 ':svg:textPath^:svg:textContent|',
15457 ':svg:title^:svg:|',
15458 ':svg:use^:svg:graphics|',
15459 ':svg:view^:svg:|#zoomAndPan',
15460 'data^[HTMLElement]|value',
15461 'keygen^[HTMLElement]|!autofocus,challenge,!disabled,form,keytype,name',
15462 'menuitem^[HTMLElement]|type,label,icon,!disabled,!checked,radiogroup,!default',
15463 'summary^[HTMLElement]|',
15464 'time^[HTMLElement]|dateTime',
15465 ':svg:cursor^:svg:|',
15466 ];
15467 const _ATTR_TO_PROP = {
15468 'class': 'className',
15469 'for': 'htmlFor',
15470 'formaction': 'formAction',
15471 'innerHtml': 'innerHTML',
15472 'readonly': 'readOnly',
15473 'tabindex': 'tabIndex',
15474 };
15475 // Invert _ATTR_TO_PROP.
15476 const _PROP_TO_ATTR = Object.keys(_ATTR_TO_PROP).reduce((inverted, attr) => {
15477 inverted[_ATTR_TO_PROP[attr]] = attr;
15478 return inverted;
15479 }, {});
15480 class DomElementSchemaRegistry extends ElementSchemaRegistry {
15481 constructor() {
15482 super();
15483 this._schema = {};
15484 // We don't allow binding to events for security reasons. Allowing event bindings would almost
15485 // certainly introduce bad XSS vulnerabilities. Instead, we store events in a separate schema.
15486 this._eventSchema = {};
15487 SCHEMA.forEach(encodedType => {
15488 const type = {};
15489 const events = new Set();
15490 const [strType, strProperties] = encodedType.split('|');
15491 const properties = strProperties.split(',');
15492 const [typeNames, superName] = strType.split('^');
15493 typeNames.split(',').forEach(tag => {
15494 this._schema[tag.toLowerCase()] = type;
15495 this._eventSchema[tag.toLowerCase()] = events;
15496 });
15497 const superType = superName && this._schema[superName.toLowerCase()];
15498 if (superType) {
15499 Object.keys(superType).forEach((prop) => {
15500 type[prop] = superType[prop];
15501 });
15502 for (const superEvent of this._eventSchema[superName.toLowerCase()]) {
15503 events.add(superEvent);
15504 }
15505 }
15506 properties.forEach((property) => {
15507 if (property.length > 0) {
15508 switch (property[0]) {
15509 case '*':
15510 events.add(property.substring(1));
15511 break;
15512 case '!':
15513 type[property.substring(1)] = BOOLEAN;
15514 break;
15515 case '#':
15516 type[property.substring(1)] = NUMBER;
15517 break;
15518 case '%':
15519 type[property.substring(1)] = OBJECT;
15520 break;
15521 default:
15522 type[property] = STRING;
15523 }
15524 }
15525 });
15526 });
15527 }
15528 hasProperty(tagName, propName, schemaMetas) {
15529 if (schemaMetas.some((schema) => schema.name === NO_ERRORS_SCHEMA.name)) {
15530 return true;
15531 }
15532 if (tagName.indexOf('-') > -1) {
15533 if (isNgContainer(tagName) || isNgContent(tagName)) {
15534 return false;
15535 }
15536 if (schemaMetas.some((schema) => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
15537 // Can't tell now as we don't know which properties a custom element will get
15538 // once it is instantiated
15539 return true;
15540 }
15541 }
15542 const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown'];
15543 return !!elementProperties[propName];
15544 }
15545 hasElement(tagName, schemaMetas) {
15546 if (schemaMetas.some((schema) => schema.name === NO_ERRORS_SCHEMA.name)) {
15547 return true;
15548 }
15549 if (tagName.indexOf('-') > -1) {
15550 if (isNgContainer(tagName) || isNgContent(tagName)) {
15551 return true;
15552 }
15553 if (schemaMetas.some((schema) => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
15554 // Allow any custom elements
15555 return true;
15556 }
15557 }
15558 return !!this._schema[tagName.toLowerCase()];
15559 }
15560 /**
15561 * securityContext returns the security context for the given property on the given DOM tag.
15562 *
15563 * Tag and property name are statically known and cannot change at runtime, i.e. it is not
15564 * possible to bind a value into a changing attribute or tag name.
15565 *
15566 * The filtering is based on a list of allowed tags|attributes. All attributes in the schema
15567 * above are assumed to have the 'NONE' security context, i.e. that they are safe inert
15568 * string values. Only specific well known attack vectors are assigned their appropriate context.
15569 */
15570 securityContext(tagName, propName, isAttribute) {
15571 if (isAttribute) {
15572 // NB: For security purposes, use the mapped property name, not the attribute name.
15573 propName = this.getMappedPropName(propName);
15574 }
15575 // Make sure comparisons are case insensitive, so that case differences between attribute and
15576 // property names do not have a security impact.
15577 tagName = tagName.toLowerCase();
15578 propName = propName.toLowerCase();
15579 let ctx = SECURITY_SCHEMA()[tagName + '|' + propName];
15580 if (ctx) {
15581 return ctx;
15582 }
15583 ctx = SECURITY_SCHEMA()['*|' + propName];
15584 return ctx ? ctx : SecurityContext.NONE;
15585 }
15586 getMappedPropName(propName) {
15587 return _ATTR_TO_PROP[propName] || propName;
15588 }
15589 getDefaultComponentElementName() {
15590 return 'ng-component';
15591 }
15592 validateProperty(name) {
15593 if (name.toLowerCase().startsWith('on')) {
15594 const msg = `Binding to event property '${name}' is disallowed for security reasons, ` +
15595 `please use (${name.slice(2)})=...` +
15596 `\nIf '${name}' is a directive input, make sure the directive is imported by the` +
15597 ` current module.`;
15598 return { error: true, msg: msg };
15599 }
15600 else {
15601 return { error: false };
15602 }
15603 }
15604 validateAttribute(name) {
15605 if (name.toLowerCase().startsWith('on')) {
15606 const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
15607 `please use (${name.slice(2)})=...`;
15608 return { error: true, msg: msg };
15609 }
15610 else {
15611 return { error: false };
15612 }
15613 }
15614 allKnownElementNames() {
15615 return Object.keys(this._schema);
15616 }
15617 allKnownAttributesOfElement(tagName) {
15618 const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown'];
15619 // Convert properties to attributes.
15620 return Object.keys(elementProperties).map(prop => _PROP_TO_ATTR[prop] ?? prop);
15621 }
15622 allKnownEventsOfElement(tagName) {
15623 return Array.from(this._eventSchema[tagName.toLowerCase()] ?? []);
15624 }
15625 normalizeAnimationStyleProperty(propName) {
15626 return dashCaseToCamelCase(propName);
15627 }
15628 normalizeAnimationStyleValue(camelCaseProp, userProvidedProp, val) {
15629 let unit = '';
15630 const strVal = val.toString().trim();
15631 let errorMsg = null;
15632 if (_isPixelDimensionStyle(camelCaseProp) && val !== 0 && val !== '0') {
15633 if (typeof val === 'number') {
15634 unit = 'px';
15635 }
15636 else {
15637 const valAndSuffixMatch = val.match(/^[+-]?[\d\.]+([a-z]*)$/);
15638 if (valAndSuffixMatch && valAndSuffixMatch[1].length == 0) {
15639 errorMsg = `Please provide a CSS unit value for ${userProvidedProp}:${val}`;
15640 }
15641 }
15642 }
15643 return { error: errorMsg, value: strVal + unit };
15644 }
15645 }
15646 function _isPixelDimensionStyle(prop) {
15647 switch (prop) {
15648 case 'width':
15649 case 'height':
15650 case 'minWidth':
15651 case 'minHeight':
15652 case 'maxWidth':
15653 case 'maxHeight':
15654 case 'left':
15655 case 'top':
15656 case 'bottom':
15657 case 'right':
15658 case 'fontSize':
15659 case 'outlineWidth':
15660 case 'outlineOffset':
15661 case 'paddingTop':
15662 case 'paddingLeft':
15663 case 'paddingBottom':
15664 case 'paddingRight':
15665 case 'marginTop':
15666 case 'marginLeft':
15667 case 'marginBottom':
15668 case 'marginRight':
15669 case 'borderRadius':
15670 case 'borderWidth':
15671 case 'borderTopWidth':
15672 case 'borderLeftWidth':
15673 case 'borderRightWidth':
15674 case 'borderBottomWidth':
15675 case 'textIndent':
15676 return true;
15677 default:
15678 return false;
15679 }
15680 }
15681
15682 /**
15683 * @license
15684 * Copyright Google LLC All Rights Reserved.
15685 *
15686 * Use of this source code is governed by an MIT-style license that can be
15687 * found in the LICENSE file at https://angular.io/license
15688 */
15689 /**
15690 * Set of tagName|propertyName corresponding to Trusted Types sinks. Properties applying to all
15691 * tags use '*'.
15692 *
15693 * Extracted from, and should be kept in sync with
15694 * https://w3c.github.io/webappsec-trusted-types/dist/spec/#integrations
15695 */
15696 const TRUSTED_TYPES_SINKS = new Set([
15697 // NOTE: All strings in this set *must* be lowercase!
15698 // TrustedHTML
15699 'iframe|srcdoc',
15700 '*|innerhtml',
15701 '*|outerhtml',
15702 // NB: no TrustedScript here, as the corresponding tags are stripped by the compiler.
15703 // TrustedScriptURL
15704 'embed|src',
15705 'object|codebase',
15706 'object|data',
15707 ]);
15708 /**
15709 * isTrustedTypesSink returns true if the given property on the given DOM tag is a Trusted Types
15710 * sink. In that case, use `ElementSchemaRegistry.securityContext` to determine which particular
15711 * Trusted Type is required for values passed to the sink:
15712 * - SecurityContext.HTML corresponds to TrustedHTML
15713 * - SecurityContext.RESOURCE_URL corresponds to TrustedScriptURL
15714 */
15715 function isTrustedTypesSink(tagName, propName) {
15716 // Make sure comparisons are case insensitive, so that case differences between attribute and
15717 // property names do not have a security impact.
15718 tagName = tagName.toLowerCase();
15719 propName = propName.toLowerCase();
15720 return TRUSTED_TYPES_SINKS.has(tagName + '|' + propName) ||
15721 TRUSTED_TYPES_SINKS.has('*|' + propName);
15722 }
15723
15724 /**
15725 * @license
15726 * Copyright Google LLC All Rights Reserved.
15727 *
15728 * Use of this source code is governed by an MIT-style license that can be
15729 * found in the LICENSE file at https://angular.io/license
15730 */
15731 const BIND_NAME_REGEXP = /^(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*)$/;
15732 // Group 1 = "bind-"
15733 const KW_BIND_IDX = 1;
15734 // Group 2 = "let-"
15735 const KW_LET_IDX = 2;
15736 // Group 3 = "ref-/#"
15737 const KW_REF_IDX = 3;
15738 // Group 4 = "on-"
15739 const KW_ON_IDX = 4;
15740 // Group 5 = "bindon-"
15741 const KW_BINDON_IDX = 5;
15742 // Group 6 = "@"
15743 const KW_AT_IDX = 6;
15744 // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@"
15745 const IDENT_KW_IDX = 7;
15746 const BINDING_DELIMS = {
15747 BANANA_BOX: { start: '[(', end: ')]' },
15748 PROPERTY: { start: '[', end: ']' },
15749 EVENT: { start: '(', end: ')' },
15750 };
15751 const TEMPLATE_ATTR_PREFIX = '*';
15752 function htmlAstToRender3Ast(htmlNodes, bindingParser, options) {
15753 const transformer = new HtmlAstToIvyAst(bindingParser, options);
15754 const ivyNodes = visitAll(transformer, htmlNodes);
15755 // Errors might originate in either the binding parser or the html to ivy transformer
15756 const allErrors = bindingParser.errors.concat(transformer.errors);
15757 const result = {
15758 nodes: ivyNodes,
15759 errors: allErrors,
15760 styleUrls: transformer.styleUrls,
15761 styles: transformer.styles,
15762 ngContentSelectors: transformer.ngContentSelectors
15763 };
15764 if (options.collectCommentNodes) {
15765 result.commentNodes = transformer.commentNodes;
15766 }
15767 return result;
15768 }
15769 class HtmlAstToIvyAst {
15770 constructor(bindingParser, options) {
15771 this.bindingParser = bindingParser;
15772 this.options = options;
15773 this.errors = [];
15774 this.styles = [];
15775 this.styleUrls = [];
15776 this.ngContentSelectors = [];
15777 // This array will be populated if `Render3ParseOptions['collectCommentNodes']` is true
15778 this.commentNodes = [];
15779 this.inI18nBlock = false;
15780 }
15781 // HTML visitor
15782 visitElement(element) {
15783 const isI18nRootElement = isI18nRootNode(element.i18n);
15784 if (isI18nRootElement) {
15785 if (this.inI18nBlock) {
15786 this.reportError('Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.', element.sourceSpan);
15787 }
15788 this.inI18nBlock = true;
15789 }
15790 const preparsedElement = preparseElement(element);
15791 if (preparsedElement.type === PreparsedElementType.SCRIPT) {
15792 return null;
15793 }
15794 else if (preparsedElement.type === PreparsedElementType.STYLE) {
15795 const contents = textContents(element);
15796 if (contents !== null) {
15797 this.styles.push(contents);
15798 }
15799 return null;
15800 }
15801 else if (preparsedElement.type === PreparsedElementType.STYLESHEET &&
15802 isStyleUrlResolvable(preparsedElement.hrefAttr)) {
15803 this.styleUrls.push(preparsedElement.hrefAttr);
15804 return null;
15805 }
15806 // Whether the element is a `<ng-template>`
15807 const isTemplateElement = isNgTemplate(element.name);
15808 const parsedProperties = [];
15809 const boundEvents = [];
15810 const variables = [];
15811 const references = [];
15812 const attributes = [];
15813 const i18nAttrsMeta = {};
15814 const templateParsedProperties = [];
15815 const templateVariables = [];
15816 // Whether the element has any *-attribute
15817 let elementHasInlineTemplate = false;
15818 for (const attribute of element.attrs) {
15819 let hasBinding = false;
15820 const normalizedName = normalizeAttributeName(attribute.name);
15821 // `*attr` defines template bindings
15822 let isTemplateBinding = false;
15823 if (attribute.i18n) {
15824 i18nAttrsMeta[attribute.name] = attribute.i18n;
15825 }
15826 if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) {
15827 // *-attributes
15828 if (elementHasInlineTemplate) {
15829 this.reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attribute.sourceSpan);
15830 }
15831 isTemplateBinding = true;
15832 elementHasInlineTemplate = true;
15833 const templateValue = attribute.value;
15834 const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length);
15835 const parsedVariables = [];
15836 const absoluteValueOffset = attribute.valueSpan ?
15837 attribute.valueSpan.start.offset :
15838 // If there is no value span the attribute does not have a value, like `attr` in
15839 //`<div attr></div>`. In this case, point to one character beyond the last character of
15840 // the attribute name.
15841 attribute.sourceSpan.start.offset + attribute.name.length;
15842 this.bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables, true /* isIvyAst */);
15843 templateVariables.push(...parsedVariables.map(v => new Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan)));
15844 }
15845 else {
15846 // Check for variables, events, property bindings, interpolation
15847 hasBinding = this.parseAttribute(isTemplateElement, attribute, [], parsedProperties, boundEvents, variables, references);
15848 }
15849 if (!hasBinding && !isTemplateBinding) {
15850 // don't include the bindings as attributes as well in the AST
15851 attributes.push(this.visitAttribute(attribute));
15852 }
15853 }
15854 const children = visitAll(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children);
15855 let parsedElement;
15856 if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
15857 // `<ng-content>`
15858 if (element.children &&
15859 !element.children.every((node) => isEmptyTextNode(node) || isCommentNode(node))) {
15860 this.reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
15861 }
15862 const selector = preparsedElement.selectAttr;
15863 const attrs = element.attrs.map(attr => this.visitAttribute(attr));
15864 parsedElement = new Content(selector, attrs, element.sourceSpan, element.i18n);
15865 this.ngContentSelectors.push(selector);
15866 }
15867 else if (isTemplateElement) {
15868 // `<ng-template>`
15869 const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
15870 parsedElement = new Template(element.name, attributes, attrs.bound, boundEvents, [ /* no template attributes */], children, references, variables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
15871 }
15872 else {
15873 const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
15874 parsedElement = new Element$1(element.name, attributes, attrs.bound, boundEvents, children, references, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
15875 }
15876 if (elementHasInlineTemplate) {
15877 // If this node is an inline-template (e.g. has *ngFor) then we need to create a template
15878 // node that contains this node.
15879 // Moreover, if the node is an element, then we need to hoist its attributes to the template
15880 // node for matching against content projection selectors.
15881 const attrs = this.extractAttributes('ng-template', templateParsedProperties, i18nAttrsMeta);
15882 const templateAttrs = [];
15883 attrs.literal.forEach(attr => templateAttrs.push(attr));
15884 attrs.bound.forEach(attr => templateAttrs.push(attr));
15885 const hoistedAttrs = parsedElement instanceof Element$1 ?
15886 {
15887 attributes: parsedElement.attributes,
15888 inputs: parsedElement.inputs,
15889 outputs: parsedElement.outputs,
15890 } :
15891 { attributes: [], inputs: [], outputs: [] };
15892 // For <ng-template>s with structural directives on them, avoid passing i18n information to
15893 // the wrapping template to prevent unnecessary i18n instructions from being generated. The
15894 // necessary i18n meta information will be extracted from child elements.
15895 const i18n = isTemplateElement && isI18nRootElement ? undefined : element.i18n;
15896 // TODO(pk): test for this case
15897 parsedElement = new Template(parsedElement.name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, templateAttrs, [parsedElement], [ /* no references */], templateVariables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, i18n);
15898 }
15899 if (isI18nRootElement) {
15900 this.inI18nBlock = false;
15901 }
15902 return parsedElement;
15903 }
15904 visitAttribute(attribute) {
15905 return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
15906 }
15907 visitText(text) {
15908 return this._visitTextWithInterpolation(text.value, text.sourceSpan, text.i18n);
15909 }
15910 visitExpansion(expansion) {
15911 if (!expansion.i18n) {
15912 // do not generate Icu in case it was created
15913 // outside of i18n block in a template
15914 return null;
15915 }
15916 if (!isI18nRootNode(expansion.i18n)) {
15917 throw new Error(`Invalid type "${expansion.i18n.constructor}" for "i18n" property of ${expansion.sourceSpan.toString()}. Expected a "Message"`);
15918 }
15919 const message = expansion.i18n;
15920 const vars = {};
15921 const placeholders = {};
15922 // extract VARs from ICUs - we process them separately while
15923 // assembling resulting message via goog.getMsg function, since
15924 // we need to pass them to top-level goog.getMsg call
15925 Object.keys(message.placeholders).forEach(key => {
15926 const value = message.placeholders[key];
15927 if (key.startsWith(I18N_ICU_VAR_PREFIX)) {
15928 // Currently when the `plural` or `select` keywords in an ICU contain trailing spaces (e.g.
15929 // `{count, select , ...}`), these spaces are also included into the key names in ICU vars
15930 // (e.g. "VAR_SELECT "). These trailing spaces are not desirable, since they will later be
15931 // converted into `_` symbols while normalizing placeholder names, which might lead to
15932 // mismatches at runtime (i.e. placeholder will not be replaced with the correct value).
15933 const formattedKey = key.trim();
15934 const ast = this.bindingParser.parseInterpolationExpression(value.text, value.sourceSpan);
15935 vars[formattedKey] = new BoundText(ast, value.sourceSpan);
15936 }
15937 else {
15938 placeholders[key] = this._visitTextWithInterpolation(value.text, value.sourceSpan);
15939 }
15940 });
15941 return new Icu$1(vars, placeholders, expansion.sourceSpan, message);
15942 }
15943 visitExpansionCase(expansionCase) {
15944 return null;
15945 }
15946 visitComment(comment) {
15947 if (this.options.collectCommentNodes) {
15948 this.commentNodes.push(new Comment$1(comment.value || '', comment.sourceSpan));
15949 }
15950 return null;
15951 }
15952 // convert view engine `ParsedProperty` to a format suitable for IVY
15953 extractAttributes(elementName, properties, i18nPropsMeta) {
15954 const bound = [];
15955 const literal = [];
15956 properties.forEach(prop => {
15957 const i18n = i18nPropsMeta[prop.name];
15958 if (prop.isLiteral) {
15959 literal.push(new TextAttribute(prop.name, prop.expression.source || '', prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n));
15960 }
15961 else {
15962 // Note that validation is skipped and property mapping is disabled
15963 // due to the fact that we need to make sure a given prop is not an
15964 // input of a directive and directive matching happens at runtime.
15965 const bep = this.bindingParser.createBoundElementProperty(elementName, prop, /* skipValidation */ true, /* mapPropertyName */ false);
15966 bound.push(BoundAttribute.fromBoundElementProperty(bep, i18n));
15967 }
15968 });
15969 return { bound, literal };
15970 }
15971 parseAttribute(isTemplateElement, attribute, matchableAttributes, parsedProperties, boundEvents, variables, references) {
15972 const name = normalizeAttributeName(attribute.name);
15973 const value = attribute.value;
15974 const srcSpan = attribute.sourceSpan;
15975 const absoluteOffset = attribute.valueSpan ? attribute.valueSpan.start.offset : srcSpan.start.offset;
15976 function createKeySpan(srcSpan, prefix, identifier) {
15977 // We need to adjust the start location for the keySpan to account for the removed 'data-'
15978 // prefix from `normalizeAttributeName`.
15979 const normalizationAdjustment = attribute.name.length - name.length;
15980 const keySpanStart = srcSpan.start.moveBy(prefix.length + normalizationAdjustment);
15981 const keySpanEnd = keySpanStart.moveBy(identifier.length);
15982 return new ParseSourceSpan(keySpanStart, keySpanEnd, keySpanStart, identifier);
15983 }
15984 const bindParts = name.match(BIND_NAME_REGEXP);
15985 if (bindParts) {
15986 if (bindParts[KW_BIND_IDX] != null) {
15987 const identifier = bindParts[IDENT_KW_IDX];
15988 const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier);
15989 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
15990 }
15991 else if (bindParts[KW_LET_IDX]) {
15992 if (isTemplateElement) {
15993 const identifier = bindParts[IDENT_KW_IDX];
15994 const keySpan = createKeySpan(srcSpan, bindParts[KW_LET_IDX], identifier);
15995 this.parseVariable(identifier, value, srcSpan, keySpan, attribute.valueSpan, variables);
15996 }
15997 else {
15998 this.reportError(`"let-" is only supported on ng-template elements.`, srcSpan);
15999 }
16000 }
16001 else if (bindParts[KW_REF_IDX]) {
16002 const identifier = bindParts[IDENT_KW_IDX];
16003 const keySpan = createKeySpan(srcSpan, bindParts[KW_REF_IDX], identifier);
16004 this.parseReference(identifier, value, srcSpan, keySpan, attribute.valueSpan, references);
16005 }
16006 else if (bindParts[KW_ON_IDX]) {
16007 const events = [];
16008 const identifier = bindParts[IDENT_KW_IDX];
16009 const keySpan = createKeySpan(srcSpan, bindParts[KW_ON_IDX], identifier);
16010 this.bindingParser.parseEvent(identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
16011 addEvents(events, boundEvents);
16012 }
16013 else if (bindParts[KW_BINDON_IDX]) {
16014 const identifier = bindParts[IDENT_KW_IDX];
16015 const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier);
16016 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16017 this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
16018 }
16019 else if (bindParts[KW_AT_IDX]) {
16020 const keySpan = createKeySpan(srcSpan, '', name);
16021 this.bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16022 }
16023 return true;
16024 }
16025 // We didn't see a kw-prefixed property binding, but we have not yet checked
16026 // for the []/()/[()] syntax.
16027 let delims = null;
16028 if (name.startsWith(BINDING_DELIMS.BANANA_BOX.start)) {
16029 delims = BINDING_DELIMS.BANANA_BOX;
16030 }
16031 else if (name.startsWith(BINDING_DELIMS.PROPERTY.start)) {
16032 delims = BINDING_DELIMS.PROPERTY;
16033 }
16034 else if (name.startsWith(BINDING_DELIMS.EVENT.start)) {
16035 delims = BINDING_DELIMS.EVENT;
16036 }
16037 if (delims !== null &&
16038 // NOTE: older versions of the parser would match a start/end delimited
16039 // binding iff the property name was terminated by the ending delimiter
16040 // and the identifier in the binding was non-empty.
16041 // TODO(ayazhafiz): update this to handle malformed bindings.
16042 name.endsWith(delims.end) && name.length > delims.start.length + delims.end.length) {
16043 const identifier = name.substring(delims.start.length, name.length - delims.end.length);
16044 const keySpan = createKeySpan(srcSpan, delims.start, identifier);
16045 if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {
16046 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16047 this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
16048 }
16049 else if (delims.start === BINDING_DELIMS.PROPERTY.start) {
16050 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16051 }
16052 else {
16053 const events = [];
16054 this.bindingParser.parseEvent(identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
16055 addEvents(events, boundEvents);
16056 }
16057 return true;
16058 }
16059 // No explicit binding found.
16060 const keySpan = createKeySpan(srcSpan, '' /* prefix */, name);
16061 const hasBinding = this.bindingParser.parsePropertyInterpolation(name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
16062 return hasBinding;
16063 }
16064 _visitTextWithInterpolation(value, sourceSpan, i18n) {
16065 const valueNoNgsp = replaceNgsp(value);
16066 const expr = this.bindingParser.parseInterpolation(valueNoNgsp, sourceSpan);
16067 return expr ? new BoundText(expr, sourceSpan, i18n) : new Text$2(valueNoNgsp, sourceSpan);
16068 }
16069 parseVariable(identifier, value, sourceSpan, keySpan, valueSpan, variables) {
16070 if (identifier.indexOf('-') > -1) {
16071 this.reportError(`"-" is not allowed in variable names`, sourceSpan);
16072 }
16073 else if (identifier.length === 0) {
16074 this.reportError(`Variable does not have a name`, sourceSpan);
16075 }
16076 variables.push(new Variable(identifier, value, sourceSpan, keySpan, valueSpan));
16077 }
16078 parseReference(identifier, value, sourceSpan, keySpan, valueSpan, references) {
16079 if (identifier.indexOf('-') > -1) {
16080 this.reportError(`"-" is not allowed in reference names`, sourceSpan);
16081 }
16082 else if (identifier.length === 0) {
16083 this.reportError(`Reference does not have a name`, sourceSpan);
16084 }
16085 else if (references.some(reference => reference.name === identifier)) {
16086 this.reportError(`Reference "#${identifier}" is defined more than once`, sourceSpan);
16087 }
16088 references.push(new Reference$1(identifier, value, sourceSpan, keySpan, valueSpan));
16089 }
16090 parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) {
16091 const events = [];
16092 this.bindingParser.parseEvent(`${name}Change`, `${expression}=$event`, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
16093 addEvents(events, boundEvents);
16094 }
16095 reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
16096 this.errors.push(new ParseError(sourceSpan, message, level));
16097 }
16098 }
16099 class NonBindableVisitor {
16100 visitElement(ast) {
16101 const preparsedElement = preparseElement(ast);
16102 if (preparsedElement.type === PreparsedElementType.SCRIPT ||
16103 preparsedElement.type === PreparsedElementType.STYLE ||
16104 preparsedElement.type === PreparsedElementType.STYLESHEET) {
16105 // Skipping <script> for security reasons
16106 // Skipping <style> and stylesheets as we already processed them
16107 // in the StyleCompiler
16108 return null;
16109 }
16110 const children = visitAll(this, ast.children, null);
16111 return new Element$1(ast.name, visitAll(this, ast.attrs),
16112 /* inputs */ [], /* outputs */ [], children, /* references */ [], ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
16113 }
16114 visitComment(comment) {
16115 return null;
16116 }
16117 visitAttribute(attribute) {
16118 return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
16119 }
16120 visitText(text) {
16121 return new Text$2(text.value, text.sourceSpan);
16122 }
16123 visitExpansion(expansion) {
16124 return null;
16125 }
16126 visitExpansionCase(expansionCase) {
16127 return null;
16128 }
16129 }
16130 const NON_BINDABLE_VISITOR = new NonBindableVisitor();
16131 function normalizeAttributeName(attrName) {
16132 return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
16133 }
16134 function addEvents(events, boundEvents) {
16135 boundEvents.push(...events.map(e => BoundEvent.fromParsedEvent(e)));
16136 }
16137 function isEmptyTextNode(node) {
16138 return node instanceof Text && node.value.trim().length == 0;
16139 }
16140 function isCommentNode(node) {
16141 return node instanceof Comment;
16142 }
16143 function textContents(node) {
16144 if (node.children.length !== 1 || !(node.children[0] instanceof Text)) {
16145 return null;
16146 }
16147 else {
16148 return node.children[0].value;
16149 }
16150 }
16151
16152 /**
16153 * @license
16154 * Copyright Google LLC All Rights Reserved.
16155 *
16156 * Use of this source code is governed by an MIT-style license that can be
16157 * found in the LICENSE file at https://angular.io/license
16158 */
16159 var TagType;
16160 (function (TagType) {
16161 TagType[TagType["ELEMENT"] = 0] = "ELEMENT";
16162 TagType[TagType["TEMPLATE"] = 1] = "TEMPLATE";
16163 })(TagType || (TagType = {}));
16164 /**
16165 * Generates an object that is used as a shared state between parent and all child contexts.
16166 */
16167 function setupRegistry() {
16168 return { getUniqueId: getSeqNumberGenerator(), icus: new Map() };
16169 }
16170 /**
16171 * I18nContext is a helper class which keeps track of all i18n-related aspects
16172 * (accumulates placeholders, bindings, etc) between i18nStart and i18nEnd instructions.
16173 *
16174 * When we enter a nested template, the top-level context is being passed down
16175 * to the nested component, which uses this context to generate a child instance
16176 * of I18nContext class (to handle nested template) and at the end, reconciles it back
16177 * with the parent context.
16178 *
16179 * @param index Instruction index of i18nStart, which initiates this context
16180 * @param ref Reference to a translation const that represents the content if thus context
16181 * @param level Nestng level defined for child contexts
16182 * @param templateIndex Instruction index of a template which this context belongs to
16183 * @param meta Meta information (id, meaning, description, etc) associated with this context
16184 */
16185 class I18nContext {
16186 constructor(index, ref, level = 0, templateIndex = null, meta, registry) {
16187 this.index = index;
16188 this.ref = ref;
16189 this.level = level;
16190 this.templateIndex = templateIndex;
16191 this.meta = meta;
16192 this.registry = registry;
16193 this.bindings = new Set();
16194 this.placeholders = new Map();
16195 this.isEmitted = false;
16196 this._unresolvedCtxCount = 0;
16197 this._registry = registry || setupRegistry();
16198 this.id = this._registry.getUniqueId();
16199 }
16200 appendTag(type, node, index, closed) {
16201 if (node.isVoid && closed) {
16202 return; // ignore "close" for void tags
16203 }
16204 const ph = node.isVoid || !closed ? node.startName : node.closeName;
16205 const content = { type, index, ctx: this.id, isVoid: node.isVoid, closed };
16206 updatePlaceholderMap(this.placeholders, ph, content);
16207 }
16208 get icus() {
16209 return this._registry.icus;
16210 }
16211 get isRoot() {
16212 return this.level === 0;
16213 }
16214 get isResolved() {
16215 return this._unresolvedCtxCount === 0;
16216 }
16217 getSerializedPlaceholders() {
16218 const result = new Map();
16219 this.placeholders.forEach((values, key) => result.set(key, values.map(serializePlaceholderValue)));
16220 return result;
16221 }
16222 // public API to accumulate i18n-related content
16223 appendBinding(binding) {
16224 this.bindings.add(binding);
16225 }
16226 appendIcu(name, ref) {
16227 updatePlaceholderMap(this._registry.icus, name, ref);
16228 }
16229 appendBoundText(node) {
16230 const phs = assembleBoundTextPlaceholders(node, this.bindings.size, this.id);
16231 phs.forEach((values, key) => updatePlaceholderMap(this.placeholders, key, ...values));
16232 }
16233 appendTemplate(node, index) {
16234 // add open and close tags at the same time,
16235 // since we process nested templates separately
16236 this.appendTag(TagType.TEMPLATE, node, index, false);
16237 this.appendTag(TagType.TEMPLATE, node, index, true);
16238 this._unresolvedCtxCount++;
16239 }
16240 appendElement(node, index, closed) {
16241 this.appendTag(TagType.ELEMENT, node, index, closed);
16242 }
16243 appendProjection(node, index) {
16244 // Add open and close tags at the same time, since `<ng-content>` has no content,
16245 // so when we come across `<ng-content>` we can register both open and close tags.
16246 // Note: runtime i18n logic doesn't distinguish `<ng-content>` tag placeholders and
16247 // regular element tag placeholders, so we generate element placeholders for both types.
16248 this.appendTag(TagType.ELEMENT, node, index, false);
16249 this.appendTag(TagType.ELEMENT, node, index, true);
16250 }
16251 /**
16252 * Generates an instance of a child context based on the root one,
16253 * when we enter a nested template within I18n section.
16254 *
16255 * @param index Instruction index of corresponding i18nStart, which initiates this context
16256 * @param templateIndex Instruction index of a template which this context belongs to
16257 * @param meta Meta information (id, meaning, description, etc) associated with this context
16258 *
16259 * @returns I18nContext instance
16260 */
16261 forkChildContext(index, templateIndex, meta) {
16262 return new I18nContext(index, this.ref, this.level + 1, templateIndex, meta, this._registry);
16263 }
16264 /**
16265 * Reconciles child context into parent one once the end of the i18n block is reached (i18nEnd).
16266 *
16267 * @param context Child I18nContext instance to be reconciled with parent context.
16268 */
16269 reconcileChildContext(context) {
16270 // set the right context id for open and close
16271 // template tags, so we can use it as sub-block ids
16272 ['start', 'close'].forEach((op) => {
16273 const key = context.meta[`${op}Name`];
16274 const phs = this.placeholders.get(key) || [];
16275 const tag = phs.find(findTemplateFn(this.id, context.templateIndex));
16276 if (tag) {
16277 tag.ctx = context.id;
16278 }
16279 });
16280 // reconcile placeholders
16281 const childPhs = context.placeholders;
16282 childPhs.forEach((values, key) => {
16283 const phs = this.placeholders.get(key);
16284 if (!phs) {
16285 this.placeholders.set(key, values);
16286 return;
16287 }
16288 // try to find matching template...
16289 const tmplIdx = phs.findIndex(findTemplateFn(context.id, context.templateIndex));
16290 if (tmplIdx >= 0) {
16291 // ... if found - replace it with nested template content
16292 const isCloseTag = key.startsWith('CLOSE');
16293 const isTemplateTag = key.endsWith('NG-TEMPLATE');
16294 if (isTemplateTag) {
16295 // current template's content is placed before or after
16296 // parent template tag, depending on the open/close atrribute
16297 phs.splice(tmplIdx + (isCloseTag ? 0 : 1), 0, ...values);
16298 }
16299 else {
16300 const idx = isCloseTag ? values.length - 1 : 0;
16301 values[idx].tmpl = phs[tmplIdx];
16302 phs.splice(tmplIdx, 1, ...values);
16303 }
16304 }
16305 else {
16306 // ... otherwise just append content to placeholder value
16307 phs.push(...values);
16308 }
16309 this.placeholders.set(key, phs);
16310 });
16311 this._unresolvedCtxCount--;
16312 }
16313 }
16314 //
16315 // Helper methods
16316 //
16317 function wrap(symbol, index, contextId, closed) {
16318 const state = closed ? '/' : '';
16319 return wrapI18nPlaceholder(`${state}${symbol}${index}`, contextId);
16320 }
16321 function wrapTag(symbol, { index, ctx, isVoid }, closed) {
16322 return isVoid ? wrap(symbol, index, ctx) + wrap(symbol, index, ctx, true) :
16323 wrap(symbol, index, ctx, closed);
16324 }
16325 function findTemplateFn(ctx, templateIndex) {
16326 return (token) => typeof token === 'object' && token.type === TagType.TEMPLATE &&
16327 token.index === templateIndex && token.ctx === ctx;
16328 }
16329 function serializePlaceholderValue(value) {
16330 const element = (data, closed) => wrapTag('#', data, closed);
16331 const template = (data, closed) => wrapTag('*', data, closed);
16332 switch (value.type) {
16333 case TagType.ELEMENT:
16334 // close element tag
16335 if (value.closed) {
16336 return element(value, true) + (value.tmpl ? template(value.tmpl, true) : '');
16337 }
16338 // open element tag that also initiates a template
16339 if (value.tmpl) {
16340 return template(value.tmpl) + element(value) +
16341 (value.isVoid ? template(value.tmpl, true) : '');
16342 }
16343 return element(value);
16344 case TagType.TEMPLATE:
16345 return template(value, value.closed);
16346 default:
16347 return value;
16348 }
16349 }
16350
16351 /**
16352 * @license
16353 * Copyright Google LLC All Rights Reserved.
16354 *
16355 * Use of this source code is governed by an MIT-style license that can be
16356 * found in the LICENSE file at https://angular.io/license
16357 */
16358 class IcuSerializerVisitor {
16359 visitText(text) {
16360 return text.value;
16361 }
16362 visitContainer(container) {
16363 return container.children.map(child => child.visit(this)).join('');
16364 }
16365 visitIcu(icu) {
16366 const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
16367 const result = `{${icu.expressionPlaceholder}, ${icu.type}, ${strCases.join(' ')}}`;
16368 return result;
16369 }
16370 visitTagPlaceholder(ph) {
16371 return ph.isVoid ?
16372 this.formatPh(ph.startName) :
16373 `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
16374 }
16375 visitPlaceholder(ph) {
16376 return this.formatPh(ph.name);
16377 }
16378 visitIcuPlaceholder(ph, context) {
16379 return this.formatPh(ph.name);
16380 }
16381 formatPh(value) {
16382 return `{${formatI18nPlaceholderName(value, /* useCamelCase */ false)}}`;
16383 }
16384 }
16385 const serializer = new IcuSerializerVisitor();
16386 function serializeIcuNode(icu) {
16387 return icu.visit(serializer);
16388 }
16389
16390 /**
16391 * @license
16392 * Copyright Google LLC All Rights Reserved.
16393 *
16394 * Use of this source code is governed by an MIT-style license that can be
16395 * found in the LICENSE file at https://angular.io/license
16396 */
16397 const TAG_TO_PLACEHOLDER_NAMES = {
16398 'A': 'LINK',
16399 'B': 'BOLD_TEXT',
16400 'BR': 'LINE_BREAK',
16401 'EM': 'EMPHASISED_TEXT',
16402 'H1': 'HEADING_LEVEL1',
16403 'H2': 'HEADING_LEVEL2',
16404 'H3': 'HEADING_LEVEL3',
16405 'H4': 'HEADING_LEVEL4',
16406 'H5': 'HEADING_LEVEL5',
16407 'H6': 'HEADING_LEVEL6',
16408 'HR': 'HORIZONTAL_RULE',
16409 'I': 'ITALIC_TEXT',
16410 'LI': 'LIST_ITEM',
16411 'LINK': 'MEDIA_LINK',
16412 'OL': 'ORDERED_LIST',
16413 'P': 'PARAGRAPH',
16414 'Q': 'QUOTATION',
16415 'S': 'STRIKETHROUGH_TEXT',
16416 'SMALL': 'SMALL_TEXT',
16417 'SUB': 'SUBSTRIPT',
16418 'SUP': 'SUPERSCRIPT',
16419 'TBODY': 'TABLE_BODY',
16420 'TD': 'TABLE_CELL',
16421 'TFOOT': 'TABLE_FOOTER',
16422 'TH': 'TABLE_HEADER_CELL',
16423 'THEAD': 'TABLE_HEADER',
16424 'TR': 'TABLE_ROW',
16425 'TT': 'MONOSPACED_TEXT',
16426 'U': 'UNDERLINED_TEXT',
16427 'UL': 'UNORDERED_LIST',
16428 };
16429 /**
16430 * Creates unique names for placeholder with different content.
16431 *
16432 * Returns the same placeholder name when the content is identical.
16433 */
16434 class PlaceholderRegistry {
16435 constructor() {
16436 // Count the occurrence of the base name top generate a unique name
16437 this._placeHolderNameCounts = {};
16438 // Maps signature to placeholder names
16439 this._signatureToName = {};
16440 }
16441 getStartTagPlaceholderName(tag, attrs, isVoid) {
16442 const signature = this._hashTag(tag, attrs, isVoid);
16443 if (this._signatureToName[signature]) {
16444 return this._signatureToName[signature];
16445 }
16446 const upperTag = tag.toUpperCase();
16447 const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
16448 const name = this._generateUniqueName(isVoid ? baseName : `START_${baseName}`);
16449 this._signatureToName[signature] = name;
16450 return name;
16451 }
16452 getCloseTagPlaceholderName(tag) {
16453 const signature = this._hashClosingTag(tag);
16454 if (this._signatureToName[signature]) {
16455 return this._signatureToName[signature];
16456 }
16457 const upperTag = tag.toUpperCase();
16458 const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
16459 const name = this._generateUniqueName(`CLOSE_${baseName}`);
16460 this._signatureToName[signature] = name;
16461 return name;
16462 }
16463 getPlaceholderName(name, content) {
16464 const upperName = name.toUpperCase();
16465 const signature = `PH: ${upperName}=${content}`;
16466 if (this._signatureToName[signature]) {
16467 return this._signatureToName[signature];
16468 }
16469 const uniqueName = this._generateUniqueName(upperName);
16470 this._signatureToName[signature] = uniqueName;
16471 return uniqueName;
16472 }
16473 getUniquePlaceholder(name) {
16474 return this._generateUniqueName(name.toUpperCase());
16475 }
16476 // Generate a hash for a tag - does not take attribute order into account
16477 _hashTag(tag, attrs, isVoid) {
16478 const start = `<${tag}`;
16479 const strAttrs = Object.keys(attrs).sort().map((name) => ` ${name}=${attrs[name]}`).join('');
16480 const end = isVoid ? '/>' : `></${tag}>`;
16481 return start + strAttrs + end;
16482 }
16483 _hashClosingTag(tag) {
16484 return this._hashTag(`/${tag}`, {}, false);
16485 }
16486 _generateUniqueName(base) {
16487 const seen = this._placeHolderNameCounts.hasOwnProperty(base);
16488 if (!seen) {
16489 this._placeHolderNameCounts[base] = 1;
16490 return base;
16491 }
16492 const id = this._placeHolderNameCounts[base];
16493 this._placeHolderNameCounts[base] = id + 1;
16494 return `${base}_${id}`;
16495 }
16496 }
16497
16498 /**
16499 * @license
16500 * Copyright Google LLC All Rights Reserved.
16501 *
16502 * Use of this source code is governed by an MIT-style license that can be
16503 * found in the LICENSE file at https://angular.io/license
16504 */
16505 const _expParser = new Parser(new Lexer());
16506 /**
16507 * Returns a function converting html nodes to an i18n Message given an interpolationConfig
16508 */
16509 function createI18nMessageFactory(interpolationConfig) {
16510 const visitor = new _I18nVisitor(_expParser, interpolationConfig);
16511 return (nodes, meaning, description, customId, visitNodeFn) => visitor.toI18nMessage(nodes, meaning, description, customId, visitNodeFn);
16512 }
16513 function noopVisitNodeFn(_html, i18n) {
16514 return i18n;
16515 }
16516 class _I18nVisitor {
16517 constructor(_expressionParser, _interpolationConfig) {
16518 this._expressionParser = _expressionParser;
16519 this._interpolationConfig = _interpolationConfig;
16520 }
16521 toI18nMessage(nodes, meaning = '', description = '', customId = '', visitNodeFn) {
16522 const context = {
16523 isIcu: nodes.length == 1 && nodes[0] instanceof Expansion,
16524 icuDepth: 0,
16525 placeholderRegistry: new PlaceholderRegistry(),
16526 placeholderToContent: {},
16527 placeholderToMessage: {},
16528 visitNodeFn: visitNodeFn || noopVisitNodeFn,
16529 };
16530 const i18nodes = visitAll(this, nodes, context);
16531 return new Message(i18nodes, context.placeholderToContent, context.placeholderToMessage, meaning, description, customId);
16532 }
16533 visitElement(el, context) {
16534 const children = visitAll(this, el.children, context);
16535 const attrs = {};
16536 el.attrs.forEach(attr => {
16537 // Do not visit the attributes, translatable ones are top-level ASTs
16538 attrs[attr.name] = attr.value;
16539 });
16540 const isVoid = getHtmlTagDefinition(el.name).isVoid;
16541 const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid);
16542 context.placeholderToContent[startPhName] = {
16543 text: el.startSourceSpan.toString(),
16544 sourceSpan: el.startSourceSpan,
16545 };
16546 let closePhName = '';
16547 if (!isVoid) {
16548 closePhName = context.placeholderRegistry.getCloseTagPlaceholderName(el.name);
16549 context.placeholderToContent[closePhName] = {
16550 text: `</${el.name}>`,
16551 sourceSpan: el.endSourceSpan ?? el.sourceSpan,
16552 };
16553 }
16554 const node = new TagPlaceholder(el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
16555 return context.visitNodeFn(el, node);
16556 }
16557 visitAttribute(attribute, context) {
16558 const node = attribute.valueTokens === undefined || attribute.valueTokens.length === 1 ?
16559 new Text$1(attribute.value, attribute.valueSpan || attribute.sourceSpan) :
16560 this._visitTextWithInterpolation(attribute.valueTokens, attribute.valueSpan || attribute.sourceSpan, context, attribute.i18n);
16561 return context.visitNodeFn(attribute, node);
16562 }
16563 visitText(text, context) {
16564 const node = text.tokens.length === 1 ?
16565 new Text$1(text.value, text.sourceSpan) :
16566 this._visitTextWithInterpolation(text.tokens, text.sourceSpan, context, text.i18n);
16567 return context.visitNodeFn(text, node);
16568 }
16569 visitComment(comment, context) {
16570 return null;
16571 }
16572 visitExpansion(icu, context) {
16573 context.icuDepth++;
16574 const i18nIcuCases = {};
16575 const i18nIcu = new Icu(icu.switchValue, icu.type, i18nIcuCases, icu.sourceSpan);
16576 icu.cases.forEach((caze) => {
16577 i18nIcuCases[caze.value] = new Container(caze.expression.map((node) => node.visit(this, context)), caze.expSourceSpan);
16578 });
16579 context.icuDepth--;
16580 if (context.isIcu || context.icuDepth > 0) {
16581 // Returns an ICU node when:
16582 // - the message (vs a part of the message) is an ICU message, or
16583 // - the ICU message is nested.
16584 const expPh = context.placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`);
16585 i18nIcu.expressionPlaceholder = expPh;
16586 context.placeholderToContent[expPh] = {
16587 text: icu.switchValue,
16588 sourceSpan: icu.switchValueSourceSpan,
16589 };
16590 return context.visitNodeFn(icu, i18nIcu);
16591 }
16592 // Else returns a placeholder
16593 // ICU placeholders should not be replaced with their original content but with the their
16594 // translations.
16595 // TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
16596 const phName = context.placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
16597 context.placeholderToMessage[phName] = this.toI18nMessage([icu], '', '', '', undefined);
16598 const node = new IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
16599 return context.visitNodeFn(icu, node);
16600 }
16601 visitExpansionCase(_icuCase, _context) {
16602 throw new Error('Unreachable code');
16603 }
16604 /**
16605 * Convert, text and interpolated tokens up into text and placeholder pieces.
16606 *
16607 * @param tokens The text and interpolated tokens.
16608 * @param sourceSpan The span of the whole of the `text` string.
16609 * @param context The current context of the visitor, used to compute and store placeholders.
16610 * @param previousI18n Any i18n metadata associated with this `text` from a previous pass.
16611 */
16612 _visitTextWithInterpolation(tokens, sourceSpan, context, previousI18n) {
16613 // Return a sequence of `Text` and `Placeholder` nodes grouped in a `Container`.
16614 const nodes = [];
16615 // We will only create a container if there are actually interpolations,
16616 // so this flag tracks that.
16617 let hasInterpolation = false;
16618 for (const token of tokens) {
16619 switch (token.type) {
16620 case 8 /* INTERPOLATION */:
16621 case 17 /* ATTR_VALUE_INTERPOLATION */:
16622 hasInterpolation = true;
16623 const expression = token.parts[1];
16624 const baseName = extractPlaceholderName(expression) || 'INTERPOLATION';
16625 const phName = context.placeholderRegistry.getPlaceholderName(baseName, expression);
16626 context.placeholderToContent[phName] = {
16627 text: token.parts.join(''),
16628 sourceSpan: token.sourceSpan
16629 };
16630 nodes.push(new Placeholder(expression, phName, token.sourceSpan));
16631 break;
16632 default:
16633 if (token.parts[0].length > 0) {
16634 // This token is text or an encoded entity.
16635 // If it is following on from a previous text node then merge it into that node
16636 // Otherwise, if it is following an interpolation, then add a new node.
16637 const previous = nodes[nodes.length - 1];
16638 if (previous instanceof Text$1) {
16639 previous.value += token.parts[0];
16640 previous.sourceSpan = new ParseSourceSpan(previous.sourceSpan.start, token.sourceSpan.end, previous.sourceSpan.fullStart, previous.sourceSpan.details);
16641 }
16642 else {
16643 nodes.push(new Text$1(token.parts[0], token.sourceSpan));
16644 }
16645 }
16646 break;
16647 }
16648 }
16649 if (hasInterpolation) {
16650 // Whitespace removal may have invalidated the interpolation source-spans.
16651 reusePreviousSourceSpans(nodes, previousI18n);
16652 return new Container(nodes, sourceSpan);
16653 }
16654 else {
16655 return nodes[0];
16656 }
16657 }
16658 }
16659 /**
16660 * Re-use the source-spans from `previousI18n` metadata for the `nodes`.
16661 *
16662 * Whitespace removal can invalidate the source-spans of interpolation nodes, so we
16663 * reuse the source-span stored from a previous pass before the whitespace was removed.
16664 *
16665 * @param nodes The `Text` and `Placeholder` nodes to be processed.
16666 * @param previousI18n Any i18n metadata for these `nodes` stored from a previous pass.
16667 */
16668 function reusePreviousSourceSpans(nodes, previousI18n) {
16669 if (previousI18n instanceof Message) {
16670 // The `previousI18n` is an i18n `Message`, so we are processing an `Attribute` with i18n
16671 // metadata. The `Message` should consist only of a single `Container` that contains the
16672 // parts (`Text` and `Placeholder`) to process.
16673 assertSingleContainerMessage(previousI18n);
16674 previousI18n = previousI18n.nodes[0];
16675 }
16676 if (previousI18n instanceof Container) {
16677 // The `previousI18n` is a `Container`, which means that this is a second i18n extraction pass
16678 // after whitespace has been removed from the AST nodes.
16679 assertEquivalentNodes(previousI18n.children, nodes);
16680 // Reuse the source-spans from the first pass.
16681 for (let i = 0; i < nodes.length; i++) {
16682 nodes[i].sourceSpan = previousI18n.children[i].sourceSpan;
16683 }
16684 }
16685 }
16686 /**
16687 * Asserts that the `message` contains exactly one `Container` node.
16688 */
16689 function assertSingleContainerMessage(message) {
16690 const nodes = message.nodes;
16691 if (nodes.length !== 1 || !(nodes[0] instanceof Container)) {
16692 throw new Error('Unexpected previous i18n message - expected it to consist of only a single `Container` node.');
16693 }
16694 }
16695 /**
16696 * Asserts that the `previousNodes` and `node` collections have the same number of elements and
16697 * corresponding elements have the same node type.
16698 */
16699 function assertEquivalentNodes(previousNodes, nodes) {
16700 if (previousNodes.length !== nodes.length) {
16701 throw new Error('The number of i18n message children changed between first and second pass.');
16702 }
16703 if (previousNodes.some((node, i) => nodes[i].constructor !== node.constructor)) {
16704 throw new Error('The types of the i18n message children changed between first and second pass.');
16705 }
16706 }
16707 const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g;
16708 function extractPlaceholderName(input) {
16709 return input.split(_CUSTOM_PH_EXP)[2];
16710 }
16711
16712 /**
16713 * @license
16714 * Copyright Google LLC All Rights Reserved.
16715 *
16716 * Use of this source code is governed by an MIT-style license that can be
16717 * found in the LICENSE file at https://angular.io/license
16718 */
16719 /**
16720 * An i18n error.
16721 */
16722 class I18nError extends ParseError {
16723 constructor(span, msg) {
16724 super(span, msg);
16725 }
16726 }
16727
16728 /**
16729 * @license
16730 * Copyright Google LLC All Rights Reserved.
16731 *
16732 * Use of this source code is governed by an MIT-style license that can be
16733 * found in the LICENSE file at https://angular.io/license
16734 */
16735 const setI18nRefs = (htmlNode, i18nNode) => {
16736 if (htmlNode instanceof NodeWithI18n) {
16737 if (i18nNode instanceof IcuPlaceholder && htmlNode.i18n instanceof Message) {
16738 // This html node represents an ICU but this is a second processing pass, and the legacy id
16739 // was computed in the previous pass and stored in the `i18n` property as a message.
16740 // We are about to wipe out that property so capture the previous message to be reused when
16741 // generating the message for this ICU later. See `_generateI18nMessage()`.
16742 i18nNode.previousMessage = htmlNode.i18n;
16743 }
16744 htmlNode.i18n = i18nNode;
16745 }
16746 return i18nNode;
16747 };
16748 /**
16749 * This visitor walks over HTML parse tree and converts information stored in
16750 * i18n-related attributes ("i18n" and "i18n-*") into i18n meta object that is
16751 * stored with other element's and attribute's information.
16752 */
16753 class I18nMetaVisitor {
16754 constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false) {
16755 this.interpolationConfig = interpolationConfig;
16756 this.keepI18nAttrs = keepI18nAttrs;
16757 this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
16758 // whether visited nodes contain i18n information
16759 this.hasI18nMeta = false;
16760 this._errors = [];
16761 // i18n message generation factory
16762 this._createI18nMessage = createI18nMessageFactory(this.interpolationConfig);
16763 }
16764 _generateI18nMessage(nodes, meta = '', visitNodeFn) {
16765 const { meaning, description, customId } = this._parseMetadata(meta);
16766 const message = this._createI18nMessage(nodes, meaning, description, customId, visitNodeFn);
16767 this._setMessageId(message, meta);
16768 this._setLegacyIds(message, meta);
16769 return message;
16770 }
16771 visitAllWithErrors(nodes) {
16772 const result = nodes.map(node => node.visit(this, null));
16773 return new ParseTreeResult(result, this._errors);
16774 }
16775 visitElement(element) {
16776 if (hasI18nAttrs(element)) {
16777 this.hasI18nMeta = true;
16778 const attrs = [];
16779 const attrsMeta = {};
16780 for (const attr of element.attrs) {
16781 if (attr.name === I18N_ATTR) {
16782 // root 'i18n' node attribute
16783 const i18n = element.i18n || attr.value;
16784 const message = this._generateI18nMessage(element.children, i18n, setI18nRefs);
16785 // do not assign empty i18n meta
16786 if (message.nodes.length) {
16787 element.i18n = message;
16788 }
16789 }
16790 else if (attr.name.startsWith(I18N_ATTR_PREFIX)) {
16791 // 'i18n-*' attributes
16792 const name = attr.name.slice(I18N_ATTR_PREFIX.length);
16793 if (isTrustedTypesSink(element.name, name)) {
16794 this._reportError(attr, `Translating attribute '${name}' is disallowed for security reasons.`);
16795 }
16796 else {
16797 attrsMeta[name] = attr.value;
16798 }
16799 }
16800 else {
16801 // non-i18n attributes
16802 attrs.push(attr);
16803 }
16804 }
16805 // set i18n meta for attributes
16806 if (Object.keys(attrsMeta).length) {
16807 for (const attr of attrs) {
16808 const meta = attrsMeta[attr.name];
16809 // do not create translation for empty attributes
16810 if (meta !== undefined && attr.value) {
16811 attr.i18n = this._generateI18nMessage([attr], attr.i18n || meta);
16812 }
16813 }
16814 }
16815 if (!this.keepI18nAttrs) {
16816 // update element's attributes,
16817 // keeping only non-i18n related ones
16818 element.attrs = attrs;
16819 }
16820 }
16821 visitAll(this, element.children, element.i18n);
16822 return element;
16823 }
16824 visitExpansion(expansion, currentMessage) {
16825 let message;
16826 const meta = expansion.i18n;
16827 this.hasI18nMeta = true;
16828 if (meta instanceof IcuPlaceholder) {
16829 // set ICU placeholder name (e.g. "ICU_1"),
16830 // generated while processing root element contents,
16831 // so we can reference it when we output translation
16832 const name = meta.name;
16833 message = this._generateI18nMessage([expansion], meta);
16834 const icu = icuFromI18nMessage(message);
16835 icu.name = name;
16836 }
16837 else {
16838 // ICU is a top level message, try to use metadata from container element if provided via
16839 // `context` argument. Note: context may not be available for standalone ICUs (without
16840 // wrapping element), so fallback to ICU metadata in this case.
16841 message = this._generateI18nMessage([expansion], currentMessage || meta);
16842 }
16843 expansion.i18n = message;
16844 return expansion;
16845 }
16846 visitText(text) {
16847 return text;
16848 }
16849 visitAttribute(attribute) {
16850 return attribute;
16851 }
16852 visitComment(comment) {
16853 return comment;
16854 }
16855 visitExpansionCase(expansionCase) {
16856 return expansionCase;
16857 }
16858 /**
16859 * Parse the general form `meta` passed into extract the explicit metadata needed to create a
16860 * `Message`.
16861 *
16862 * There are three possibilities for the `meta` variable
16863 * 1) a string from an `i18n` template attribute: parse it to extract the metadata values.
16864 * 2) a `Message` from a previous processing pass: reuse the metadata values in the message.
16865 * 4) other: ignore this and just process the message metadata as normal
16866 *
16867 * @param meta the bucket that holds information about the message
16868 * @returns the parsed metadata.
16869 */
16870 _parseMetadata(meta) {
16871 return typeof meta === 'string' ? parseI18nMeta(meta) :
16872 meta instanceof Message ? meta : {};
16873 }
16874 /**
16875 * Generate (or restore) message id if not specified already.
16876 */
16877 _setMessageId(message, meta) {
16878 if (!message.id) {
16879 message.id = meta instanceof Message && meta.id || decimalDigest(message);
16880 }
16881 }
16882 /**
16883 * Update the `message` with a `legacyId` if necessary.
16884 *
16885 * @param message the message whose legacy id should be set
16886 * @param meta information about the message being processed
16887 */
16888 _setLegacyIds(message, meta) {
16889 if (this.enableI18nLegacyMessageIdFormat) {
16890 message.legacyIds = [computeDigest(message), computeDecimalDigest(message)];
16891 }
16892 else if (typeof meta !== 'string') {
16893 // This occurs if we are doing the 2nd pass after whitespace removal (see `parseTemplate()` in
16894 // `packages/compiler/src/render3/view/template.ts`).
16895 // In that case we want to reuse the legacy message generated in the 1st pass (see
16896 // `setI18nRefs()`).
16897 const previousMessage = meta instanceof Message ?
16898 meta :
16899 meta instanceof IcuPlaceholder ? meta.previousMessage : undefined;
16900 message.legacyIds = previousMessage ? previousMessage.legacyIds : [];
16901 }
16902 }
16903 _reportError(node, msg) {
16904 this._errors.push(new I18nError(node.sourceSpan, msg));
16905 }
16906 }
16907 /** I18n separators for metadata **/
16908 const I18N_MEANING_SEPARATOR = '|';
16909 const I18N_ID_SEPARATOR = '@@';
16910 /**
16911 * Parses i18n metas like:
16912 * - "@@id",
16913 * - "description[@@id]",
16914 * - "meaning|description[@@id]"
16915 * and returns an object with parsed output.
16916 *
16917 * @param meta String that represents i18n meta
16918 * @returns Object with id, meaning and description fields
16919 */
16920 function parseI18nMeta(meta = '') {
16921 let customId;
16922 let meaning;
16923 let description;
16924 meta = meta.trim();
16925 if (meta) {
16926 const idIndex = meta.indexOf(I18N_ID_SEPARATOR);
16927 const descIndex = meta.indexOf(I18N_MEANING_SEPARATOR);
16928 let meaningAndDesc;
16929 [meaningAndDesc, customId] =
16930 (idIndex > -1) ? [meta.slice(0, idIndex), meta.slice(idIndex + 2)] : [meta, ''];
16931 [meaning, description] = (descIndex > -1) ?
16932 [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] :
16933 ['', meaningAndDesc];
16934 }
16935 return { customId, meaning, description };
16936 }
16937 // Converts i18n meta information for a message (id, description, meaning)
16938 // to a JsDoc statement formatted as expected by the Closure compiler.
16939 function i18nMetaToJSDoc(meta) {
16940 const tags = [];
16941 if (meta.description) {
16942 tags.push({ tagName: "desc" /* Desc */, text: meta.description });
16943 }
16944 if (meta.meaning) {
16945 tags.push({ tagName: "meaning" /* Meaning */, text: meta.meaning });
16946 }
16947 return tags.length == 0 ? null : jsDocComment(tags);
16948 }
16949
16950 /** Closure uses `goog.getMsg(message)` to lookup translations */
16951 const GOOG_GET_MSG = 'goog.getMsg';
16952 function createGoogleGetMsgStatements(variable$1, message, closureVar, params) {
16953 const messageString = serializeI18nMessageForGetMsg(message);
16954 const args = [literal$1(messageString)];
16955 if (Object.keys(params).length) {
16956 args.push(mapLiteral(params, true));
16957 }
16958 // /**
16959 // * @desc description of message
16960 // * @meaning meaning of message
16961 // */
16962 // const MSG_... = goog.getMsg(..);
16963 // I18N_X = MSG_...;
16964 const googGetMsgStmt = closureVar.set(variable(GOOG_GET_MSG).callFn(args)).toConstDecl();
16965 const metaComment = i18nMetaToJSDoc(message);
16966 if (metaComment !== null) {
16967 googGetMsgStmt.addLeadingComment(metaComment);
16968 }
16969 const i18nAssignmentStmt = new ExpressionStatement(variable$1.set(closureVar));
16970 return [googGetMsgStmt, i18nAssignmentStmt];
16971 }
16972 /**
16973 * This visitor walks over i18n tree and generates its string representation, including ICUs and
16974 * placeholders in `{$placeholder}` (for plain messages) or `{PLACEHOLDER}` (inside ICUs) format.
16975 */
16976 class GetMsgSerializerVisitor {
16977 formatPh(value) {
16978 return `{$${formatI18nPlaceholderName(value)}}`;
16979 }
16980 visitText(text) {
16981 return text.value;
16982 }
16983 visitContainer(container) {
16984 return container.children.map(child => child.visit(this)).join('');
16985 }
16986 visitIcu(icu) {
16987 return serializeIcuNode(icu);
16988 }
16989 visitTagPlaceholder(ph) {
16990 return ph.isVoid ?
16991 this.formatPh(ph.startName) :
16992 `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
16993 }
16994 visitPlaceholder(ph) {
16995 return this.formatPh(ph.name);
16996 }
16997 visitIcuPlaceholder(ph, context) {
16998 return this.formatPh(ph.name);
16999 }
17000 }
17001 const serializerVisitor$1 = new GetMsgSerializerVisitor();
17002 function serializeI18nMessageForGetMsg(message) {
17003 return message.nodes.map(node => node.visit(serializerVisitor$1, null)).join('');
17004 }
17005
17006 function createLocalizeStatements(variable, message, params) {
17007 const { messageParts, placeHolders } = serializeI18nMessageForLocalize(message);
17008 const sourceSpan = getSourceSpan(message);
17009 const expressions = placeHolders.map(ph => params[ph.text]);
17010 const localizedString$1 = localizedString(message, messageParts, placeHolders, expressions, sourceSpan);
17011 const variableInitialization = variable.set(localizedString$1);
17012 return [new ExpressionStatement(variableInitialization)];
17013 }
17014 /**
17015 * This visitor walks over an i18n tree, capturing literal strings and placeholders.
17016 *
17017 * The result can be used for generating the `$localize` tagged template literals.
17018 */
17019 class LocalizeSerializerVisitor {
17020 visitText(text, context) {
17021 if (context[context.length - 1] instanceof LiteralPiece) {
17022 // Two literal pieces in a row means that there was some comment node in-between.
17023 context[context.length - 1].text += text.value;
17024 }
17025 else {
17026 const sourceSpan = new ParseSourceSpan(text.sourceSpan.fullStart, text.sourceSpan.end, text.sourceSpan.fullStart, text.sourceSpan.details);
17027 context.push(new LiteralPiece(text.value, sourceSpan));
17028 }
17029 }
17030 visitContainer(container, context) {
17031 container.children.forEach(child => child.visit(this, context));
17032 }
17033 visitIcu(icu, context) {
17034 context.push(new LiteralPiece(serializeIcuNode(icu), icu.sourceSpan));
17035 }
17036 visitTagPlaceholder(ph, context) {
17037 context.push(this.createPlaceholderPiece(ph.startName, ph.startSourceSpan ?? ph.sourceSpan));
17038 if (!ph.isVoid) {
17039 ph.children.forEach(child => child.visit(this, context));
17040 context.push(this.createPlaceholderPiece(ph.closeName, ph.endSourceSpan ?? ph.sourceSpan));
17041 }
17042 }
17043 visitPlaceholder(ph, context) {
17044 context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
17045 }
17046 visitIcuPlaceholder(ph, context) {
17047 context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
17048 }
17049 createPlaceholderPiece(name, sourceSpan) {
17050 return new PlaceholderPiece(formatI18nPlaceholderName(name, /* useCamelCase */ false), sourceSpan);
17051 }
17052 }
17053 const serializerVisitor = new LocalizeSerializerVisitor();
17054 /**
17055 * Serialize an i18n message into two arrays: messageParts and placeholders.
17056 *
17057 * These arrays will be used to generate `$localize` tagged template literals.
17058 *
17059 * @param message The message to be serialized.
17060 * @returns an object containing the messageParts and placeholders.
17061 */
17062 function serializeI18nMessageForLocalize(message) {
17063 const pieces = [];
17064 message.nodes.forEach(node => node.visit(serializerVisitor, pieces));
17065 return processMessagePieces(pieces);
17066 }
17067 function getSourceSpan(message) {
17068 const startNode = message.nodes[0];
17069 const endNode = message.nodes[message.nodes.length - 1];
17070 return new ParseSourceSpan(startNode.sourceSpan.fullStart, endNode.sourceSpan.end, startNode.sourceSpan.fullStart, startNode.sourceSpan.details);
17071 }
17072 /**
17073 * Convert the list of serialized MessagePieces into two arrays.
17074 *
17075 * One contains the literal string pieces and the other the placeholders that will be replaced by
17076 * expressions when rendering `$localize` tagged template literals.
17077 *
17078 * @param pieces The pieces to process.
17079 * @returns an object containing the messageParts and placeholders.
17080 */
17081 function processMessagePieces(pieces) {
17082 const messageParts = [];
17083 const placeHolders = [];
17084 if (pieces[0] instanceof PlaceholderPiece) {
17085 // The first piece was a placeholder so we need to add an initial empty message part.
17086 messageParts.push(createEmptyMessagePart(pieces[0].sourceSpan.start));
17087 }
17088 for (let i = 0; i < pieces.length; i++) {
17089 const part = pieces[i];
17090 if (part instanceof LiteralPiece) {
17091 messageParts.push(part);
17092 }
17093 else {
17094 placeHolders.push(part);
17095 if (pieces[i - 1] instanceof PlaceholderPiece) {
17096 // There were two placeholders in a row, so we need to add an empty message part.
17097 messageParts.push(createEmptyMessagePart(pieces[i - 1].sourceSpan.end));
17098 }
17099 }
17100 }
17101 if (pieces[pieces.length - 1] instanceof PlaceholderPiece) {
17102 // The last piece was a placeholder so we need to add a final empty message part.
17103 messageParts.push(createEmptyMessagePart(pieces[pieces.length - 1].sourceSpan.end));
17104 }
17105 return { messageParts, placeHolders };
17106 }
17107 function createEmptyMessagePart(location) {
17108 return new LiteralPiece('', new ParseSourceSpan(location, location));
17109 }
17110
17111 /**
17112 * @license
17113 * Copyright Google LLC All Rights Reserved.
17114 *
17115 * Use of this source code is governed by an MIT-style license that can be
17116 * found in the LICENSE file at https://angular.io/license
17117 */
17118 // Selector attribute name of `<ng-content>`
17119 const NG_CONTENT_SELECT_ATTR = 'select';
17120 // Attribute name of `ngProjectAs`.
17121 const NG_PROJECT_AS_ATTR_NAME = 'ngProjectAs';
17122 // Global symbols available only inside event bindings.
17123 const EVENT_BINDING_SCOPE_GLOBALS = new Set(['$event']);
17124 // List of supported global targets for event listeners
17125 const GLOBAL_TARGET_RESOLVERS = new Map([['window', Identifiers$1.resolveWindow], ['document', Identifiers$1.resolveDocument], ['body', Identifiers$1.resolveBody]]);
17126 const LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t'];
17127 // if (rf & flags) { .. }
17128 function renderFlagCheckIfStmt(flags, statements) {
17129 return ifStmt(variable(RENDER_FLAGS).bitwiseAnd(literal$1(flags), null, false), statements);
17130 }
17131 function prepareEventListenerParameters(eventAst, handlerName = null, scope = null) {
17132 const { type, name, target, phase, handler } = eventAst;
17133 if (target && !GLOBAL_TARGET_RESOLVERS.has(target)) {
17134 throw new Error(`Unexpected global target '${target}' defined for '${name}' event.
17135 Supported list of global targets: ${Array.from(GLOBAL_TARGET_RESOLVERS.keys())}.`);
17136 }
17137 const eventArgumentName = '$event';
17138 const implicitReceiverAccesses = new Set();
17139 const implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ?
17140 variable(CONTEXT_NAME) :
17141 scope.getOrCreateSharedContextVar(0);
17142 const bindingExpr = convertActionBinding(scope, implicitReceiverExpr, handler, 'b', () => error('Unexpected interpolation'), eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
17143 const statements = [];
17144 if (scope) {
17145 // `variableDeclarations` needs to run first, because
17146 // `restoreViewStatement` depends on the result.
17147 statements.push(...scope.variableDeclarations());
17148 statements.unshift(...scope.restoreViewStatement());
17149 }
17150 statements.push(...bindingExpr.render3Stmts);
17151 const eventName = type === 1 /* Animation */ ? prepareSyntheticListenerName(name, phase) : name;
17152 const fnName = handlerName && sanitizeIdentifier(handlerName);
17153 const fnArgs = [];
17154 if (implicitReceiverAccesses.has(eventArgumentName)) {
17155 fnArgs.push(new FnParam(eventArgumentName, DYNAMIC_TYPE));
17156 }
17157 const handlerFn = fn(fnArgs, statements, INFERRED_TYPE, null, fnName);
17158 const params = [literal$1(eventName), handlerFn];
17159 if (target) {
17160 params.push(literal$1(false), // `useCapture` flag, defaults to `false`
17161 importExpr(GLOBAL_TARGET_RESOLVERS.get(target)));
17162 }
17163 return params;
17164 }
17165 function createComponentDefConsts() {
17166 return {
17167 prepareStatements: [],
17168 constExpressions: [],
17169 i18nVarRefsCache: new Map(),
17170 };
17171 }
17172 class TemplateDefinitionBuilder {
17173 constructor(constantPool, parentBindingScope, level = 0, contextName, i18nContext, templateIndex, templateName, directiveMatcher, directives, pipeTypeByName, pipes, _namespace, relativeContextFilePath, i18nUseExternalIds, _constants = createComponentDefConsts()) {
17174 this.constantPool = constantPool;
17175 this.level = level;
17176 this.contextName = contextName;
17177 this.i18nContext = i18nContext;
17178 this.templateIndex = templateIndex;
17179 this.templateName = templateName;
17180 this.directiveMatcher = directiveMatcher;
17181 this.directives = directives;
17182 this.pipeTypeByName = pipeTypeByName;
17183 this.pipes = pipes;
17184 this._namespace = _namespace;
17185 this.i18nUseExternalIds = i18nUseExternalIds;
17186 this._constants = _constants;
17187 this._dataIndex = 0;
17188 this._bindingContext = 0;
17189 this._prefixCode = [];
17190 /**
17191 * List of callbacks to generate creation mode instructions. We store them here as we process
17192 * the template so bindings in listeners are resolved only once all nodes have been visited.
17193 * This ensures all local refs and context variables are available for matching.
17194 */
17195 this._creationCodeFns = [];
17196 /**
17197 * List of callbacks to generate update mode instructions. We store them here as we process
17198 * the template so bindings are resolved only once all nodes have been visited. This ensures
17199 * all local refs and context variables are available for matching.
17200 */
17201 this._updateCodeFns = [];
17202 /** Index of the currently-selected node. */
17203 this._currentIndex = 0;
17204 /** Temporary variable declarations generated from visiting pipes, literals, etc. */
17205 this._tempVariables = [];
17206 /**
17207 * List of callbacks to build nested templates. Nested templates must not be visited until
17208 * after the parent template has finished visiting all of its nodes. This ensures that all
17209 * local ref bindings in nested templates are able to find local ref values if the refs
17210 * are defined after the template declaration.
17211 */
17212 this._nestedTemplateFns = [];
17213 this._unsupported = unsupported;
17214 // i18n context local to this template
17215 this.i18n = null;
17216 // Number of slots to reserve for pureFunctions
17217 this._pureFunctionSlots = 0;
17218 // Number of binding slots
17219 this._bindingSlots = 0;
17220 // Projection slots found in the template. Projection slots can distribute projected
17221 // nodes based on a selector, or can just use the wildcard selector to match
17222 // all nodes which aren't matching any selector.
17223 this._ngContentReservedSlots = [];
17224 // Number of non-default selectors found in all parent templates of this template. We need to
17225 // track it to properly adjust projection slot index in the `projection` instruction.
17226 this._ngContentSelectorsOffset = 0;
17227 // Expression that should be used as implicit receiver when converting template
17228 // expressions to output AST.
17229 this._implicitReceiverExpr = null;
17230 // These should be handled in the template or element directly.
17231 this.visitReference = invalid;
17232 this.visitVariable = invalid;
17233 this.visitTextAttribute = invalid;
17234 this.visitBoundAttribute = invalid;
17235 this.visitBoundEvent = invalid;
17236 this._bindingScope = parentBindingScope.nestedScope(level);
17237 // Turn the relative context file path into an identifier by replacing non-alphanumeric
17238 // characters with underscores.
17239 this.fileBasedI18nSuffix = relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_') + '_';
17240 this._valueConverter = new ValueConverter(constantPool, () => this.allocateDataSlot(), (numSlots) => this.allocatePureFunctionSlots(numSlots), (name, localName, slot, value) => {
17241 const pipeType = pipeTypeByName.get(name);
17242 if (pipeType) {
17243 this.pipes.add(pipeType);
17244 }
17245 this._bindingScope.set(this.level, localName, value);
17246 this.creationInstruction(null, Identifiers$1.pipe, [literal$1(slot), literal$1(name)]);
17247 });
17248 }
17249 buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n) {
17250 this._ngContentSelectorsOffset = ngContentSelectorsOffset;
17251 if (this._namespace !== Identifiers$1.namespaceHTML) {
17252 this.creationInstruction(null, this._namespace);
17253 }
17254 // Create variable bindings
17255 variables.forEach(v => this.registerContextVariables(v));
17256 // Initiate i18n context in case:
17257 // - this template has parent i18n context
17258 // - or the template has i18n meta associated with it,
17259 // but it's not initiated by the Element (e.g. <ng-template i18n>)
17260 const initI18nContext = this.i18nContext ||
17261 (isI18nRootNode(i18n) && !isSingleI18nIcu(i18n) &&
17262 !(isSingleElementTemplate(nodes) && nodes[0].i18n === i18n));
17263 const selfClosingI18nInstruction = hasTextChildrenOnly(nodes);
17264 if (initI18nContext) {
17265 this.i18nStart(null, i18n, selfClosingI18nInstruction);
17266 }
17267 // This is the initial pass through the nodes of this template. In this pass, we
17268 // queue all creation mode and update mode instructions for generation in the second
17269 // pass. It's necessary to separate the passes to ensure local refs are defined before
17270 // resolving bindings. We also count bindings in this pass as we walk bound expressions.
17271 visitAll$1(this, nodes);
17272 // Add total binding count to pure function count so pure function instructions are
17273 // generated with the correct slot offset when update instructions are processed.
17274 this._pureFunctionSlots += this._bindingSlots;
17275 // Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
17276 // `pipeBind` update instructions), so we have to update the slot offsets manually
17277 // to account for bindings.
17278 this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
17279 // Nested templates must be processed before creation instructions so template()
17280 // instructions can be generated with the correct internal const count.
17281 this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
17282 // Output the `projectionDef` instruction when some `<ng-content>` tags are present.
17283 // The `projectionDef` instruction is only emitted for the component template and
17284 // is skipped for nested templates (<ng-template> tags).
17285 if (this.level === 0 && this._ngContentReservedSlots.length) {
17286 const parameters = [];
17287 // By default the `projectionDef` instructions creates one slot for the wildcard
17288 // selector if no parameters are passed. Therefore we only want to allocate a new
17289 // array for the projection slots if the default projection slot is not sufficient.
17290 if (this._ngContentReservedSlots.length > 1 || this._ngContentReservedSlots[0] !== '*') {
17291 const r3ReservedSlots = this._ngContentReservedSlots.map(s => s !== '*' ? parseSelectorToR3Selector(s) : s);
17292 parameters.push(this.constantPool.getConstLiteral(asLiteral(r3ReservedSlots), true));
17293 }
17294 // Since we accumulate ngContent selectors while processing template elements,
17295 // we *prepend* `projectionDef` to creation instructions block, to put it before
17296 // any `projection` instructions
17297 this.creationInstruction(null, Identifiers$1.projectionDef, parameters, /* prepend */ true);
17298 }
17299 if (initI18nContext) {
17300 this.i18nEnd(null, selfClosingI18nInstruction);
17301 }
17302 // Generate all the creation mode instructions (e.g. resolve bindings in listeners)
17303 const creationStatements = this._creationCodeFns.map((fn) => fn());
17304 // Generate all the update mode instructions (e.g. resolve property or text bindings)
17305 const updateStatements = this._updateCodeFns.map((fn) => fn());
17306 // Variable declaration must occur after binding resolution so we can generate context
17307 // instructions that build on each other.
17308 // e.g. const b = nextContext().$implicit(); const b = nextContext();
17309 const creationVariables = this._bindingScope.viewSnapshotStatements();
17310 const updateVariables = this._bindingScope.variableDeclarations().concat(this._tempVariables);
17311 const creationBlock = creationStatements.length > 0 ?
17312 [renderFlagCheckIfStmt(1 /* Create */, creationVariables.concat(creationStatements))] :
17313 [];
17314 const updateBlock = updateStatements.length > 0 ?
17315 [renderFlagCheckIfStmt(2 /* Update */, updateVariables.concat(updateStatements))] :
17316 [];
17317 return fn(
17318 // i.e. (rf: RenderFlags, ctx: any)
17319 [new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
17320 // Temporary variable declarations for query refresh (i.e. let _t: any;)
17321 ...this._prefixCode,
17322 // Creating mode (i.e. if (rf & RenderFlags.Create) { ... })
17323 ...creationBlock,
17324 // Binding and refresh mode (i.e. if (rf & RenderFlags.Update) {...})
17325 ...updateBlock,
17326 ], INFERRED_TYPE, null, this.templateName);
17327 }
17328 // LocalResolver
17329 getLocal(name) {
17330 return this._bindingScope.get(name);
17331 }
17332 // LocalResolver
17333 notifyImplicitReceiverUse() {
17334 this._bindingScope.notifyImplicitReceiverUse();
17335 }
17336 // LocalResolver
17337 maybeRestoreView() {
17338 this._bindingScope.maybeRestoreView();
17339 }
17340 i18nTranslate(message, params = {}, ref, transformFn) {
17341 const _ref = ref || this.i18nGenerateMainBlockVar();
17342 // Closure Compiler requires const names to start with `MSG_` but disallows any other const to
17343 // start with `MSG_`. We define a variable starting with `MSG_` just for the `goog.getMsg` call
17344 const closureVar = this.i18nGenerateClosureVar(message.id);
17345 const statements = getTranslationDeclStmts(message, _ref, closureVar, params, transformFn);
17346 this._constants.prepareStatements.push(...statements);
17347 return _ref;
17348 }
17349 registerContextVariables(variable$1) {
17350 const scopedName = this._bindingScope.freshReferenceName();
17351 const retrievalLevel = this.level;
17352 const lhs = variable(variable$1.name + scopedName);
17353 this._bindingScope.set(retrievalLevel, variable$1.name, lhs, 1 /* CONTEXT */, (scope, relativeLevel) => {
17354 let rhs;
17355 if (scope.bindingLevel === retrievalLevel) {
17356 if (scope.isListenerScope() && scope.hasRestoreViewVariable()) {
17357 // e.g. restoredCtx.
17358 // We have to get the context from a view reference, if one is available, because
17359 // the context that was passed in during creation may not be correct anymore.
17360 // For more information see: https://github.com/angular/angular/pull/40360.
17361 rhs = variable(RESTORED_VIEW_CONTEXT_NAME);
17362 scope.notifyRestoredViewContextUse();
17363 }
17364 else {
17365 // e.g. ctx
17366 rhs = variable(CONTEXT_NAME);
17367 }
17368 }
17369 else {
17370 const sharedCtxVar = scope.getSharedContextName(retrievalLevel);
17371 // e.g. ctx_r0 OR x(2);
17372 rhs = sharedCtxVar ? sharedCtxVar : generateNextContextExpr(relativeLevel);
17373 }
17374 // e.g. const $item$ = x(2).$implicit;
17375 return [lhs.set(rhs.prop(variable$1.value || IMPLICIT_REFERENCE)).toConstDecl()];
17376 });
17377 }
17378 i18nAppendBindings(expressions) {
17379 if (expressions.length > 0) {
17380 expressions.forEach(expression => this.i18n.appendBinding(expression));
17381 }
17382 }
17383 i18nBindProps(props) {
17384 const bound = {};
17385 Object.keys(props).forEach(key => {
17386 const prop = props[key];
17387 if (prop instanceof Text$2) {
17388 bound[key] = literal$1(prop.value);
17389 }
17390 else {
17391 const value = prop.value.visit(this._valueConverter);
17392 this.allocateBindingSlots(value);
17393 if (value instanceof Interpolation) {
17394 const { strings, expressions } = value;
17395 const { id, bindings } = this.i18n;
17396 const label = assembleI18nBoundString(strings, bindings.size, id);
17397 this.i18nAppendBindings(expressions);
17398 bound[key] = literal$1(label);
17399 }
17400 }
17401 });
17402 return bound;
17403 }
17404 // Generates top level vars for i18n blocks (i.e. `i18n_N`).
17405 i18nGenerateMainBlockVar() {
17406 return variable(this.constantPool.uniqueName(TRANSLATION_VAR_PREFIX));
17407 }
17408 // Generates vars with Closure-specific names for i18n blocks (i.e. `MSG_XXX`).
17409 i18nGenerateClosureVar(messageId) {
17410 let name;
17411 const suffix = this.fileBasedI18nSuffix.toUpperCase();
17412 if (this.i18nUseExternalIds) {
17413 const prefix = getTranslationConstPrefix(`EXTERNAL_`);
17414 const uniqueSuffix = this.constantPool.uniqueName(suffix);
17415 name = `${prefix}${sanitizeIdentifier(messageId)}$$${uniqueSuffix}`;
17416 }
17417 else {
17418 const prefix = getTranslationConstPrefix(suffix);
17419 name = this.constantPool.uniqueName(prefix);
17420 }
17421 return variable(name);
17422 }
17423 i18nUpdateRef(context) {
17424 const { icus, meta, isRoot, isResolved, isEmitted } = context;
17425 if (isRoot && isResolved && !isEmitted && !isSingleI18nIcu(meta)) {
17426 context.isEmitted = true;
17427 const placeholders = context.getSerializedPlaceholders();
17428 let icuMapping = {};
17429 let params = placeholders.size ? placeholdersToParams(placeholders) : {};
17430 if (icus.size) {
17431 icus.forEach((refs, key) => {
17432 if (refs.length === 1) {
17433 // if we have one ICU defined for a given
17434 // placeholder - just output its reference
17435 params[key] = refs[0];
17436 }
17437 else {
17438 // ... otherwise we need to activate post-processing
17439 // to replace ICU placeholders with proper values
17440 const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
17441 params[key] = literal$1(placeholder);
17442 icuMapping[key] = literalArr(refs);
17443 }
17444 });
17445 }
17446 // translation requires post processing in 2 cases:
17447 // - if we have placeholders with multiple values (ex. `START_DIV`: [�#1�, �#2�, ...])
17448 // - if we have multiple ICUs that refer to the same placeholder name
17449 const needsPostprocessing = Array.from(placeholders.values()).some((value) => value.length > 1) ||
17450 Object.keys(icuMapping).length;
17451 let transformFn;
17452 if (needsPostprocessing) {
17453 transformFn = (raw) => {
17454 const args = [raw];
17455 if (Object.keys(icuMapping).length) {
17456 args.push(mapLiteral(icuMapping, true));
17457 }
17458 return instruction(null, Identifiers$1.i18nPostprocess, args);
17459 };
17460 }
17461 this.i18nTranslate(meta, params, context.ref, transformFn);
17462 }
17463 }
17464 i18nStart(span = null, meta, selfClosing) {
17465 const index = this.allocateDataSlot();
17466 this.i18n = this.i18nContext ?
17467 this.i18nContext.forkChildContext(index, this.templateIndex, meta) :
17468 new I18nContext(index, this.i18nGenerateMainBlockVar(), 0, this.templateIndex, meta);
17469 // generate i18nStart instruction
17470 const { id, ref } = this.i18n;
17471 const params = [literal$1(index), this.addToConsts(ref)];
17472 if (id > 0) {
17473 // do not push 3rd argument (sub-block id)
17474 // into i18nStart call for top level i18n context
17475 params.push(literal$1(id));
17476 }
17477 this.creationInstruction(span, selfClosing ? Identifiers$1.i18n : Identifiers$1.i18nStart, params);
17478 }
17479 i18nEnd(span = null, selfClosing) {
17480 if (!this.i18n) {
17481 throw new Error('i18nEnd is executed with no i18n context present');
17482 }
17483 if (this.i18nContext) {
17484 this.i18nContext.reconcileChildContext(this.i18n);
17485 this.i18nUpdateRef(this.i18nContext);
17486 }
17487 else {
17488 this.i18nUpdateRef(this.i18n);
17489 }
17490 // setup accumulated bindings
17491 const { index, bindings } = this.i18n;
17492 if (bindings.size) {
17493 const chainBindings = [];
17494 bindings.forEach(binding => {
17495 chainBindings.push({ sourceSpan: span, value: () => this.convertPropertyBinding(binding) });
17496 });
17497 // for i18n block, advance to the most recent element index (by taking the current number of
17498 // elements and subtracting one) before invoking `i18nExp` instructions, to make sure the
17499 // necessary lifecycle hooks of components/directives are properly flushed.
17500 this.updateInstructionChainWithAdvance(this.getConstCount() - 1, Identifiers$1.i18nExp, chainBindings);
17501 this.updateInstruction(span, Identifiers$1.i18nApply, [literal$1(index)]);
17502 }
17503 if (!selfClosing) {
17504 this.creationInstruction(span, Identifiers$1.i18nEnd);
17505 }
17506 this.i18n = null; // reset local i18n context
17507 }
17508 i18nAttributesInstruction(nodeIndex, attrs, sourceSpan) {
17509 let hasBindings = false;
17510 const i18nAttrArgs = [];
17511 const bindings = [];
17512 attrs.forEach(attr => {
17513 const message = attr.i18n;
17514 const converted = attr.value.visit(this._valueConverter);
17515 this.allocateBindingSlots(converted);
17516 if (converted instanceof Interpolation) {
17517 const placeholders = assembleBoundTextPlaceholders(message);
17518 const params = placeholdersToParams(placeholders);
17519 i18nAttrArgs.push(literal$1(attr.name), this.i18nTranslate(message, params));
17520 converted.expressions.forEach(expression => {
17521 hasBindings = true;
17522 bindings.push({
17523 sourceSpan,
17524 value: () => this.convertPropertyBinding(expression),
17525 });
17526 });
17527 }
17528 });
17529 if (bindings.length > 0) {
17530 this.updateInstructionChainWithAdvance(nodeIndex, Identifiers$1.i18nExp, bindings);
17531 }
17532 if (i18nAttrArgs.length > 0) {
17533 const index = literal$1(this.allocateDataSlot());
17534 const constIndex = this.addToConsts(literalArr(i18nAttrArgs));
17535 this.creationInstruction(sourceSpan, Identifiers$1.i18nAttributes, [index, constIndex]);
17536 if (hasBindings) {
17537 this.updateInstruction(sourceSpan, Identifiers$1.i18nApply, [index]);
17538 }
17539 }
17540 }
17541 getNamespaceInstruction(namespaceKey) {
17542 switch (namespaceKey) {
17543 case 'math':
17544 return Identifiers$1.namespaceMathML;
17545 case 'svg':
17546 return Identifiers$1.namespaceSVG;
17547 default:
17548 return Identifiers$1.namespaceHTML;
17549 }
17550 }
17551 addNamespaceInstruction(nsInstruction, element) {
17552 this._namespace = nsInstruction;
17553 this.creationInstruction(element.startSourceSpan, nsInstruction);
17554 }
17555 /**
17556 * Adds an update instruction for an interpolated property or attribute, such as
17557 * `prop="{{value}}"` or `attr.title="{{value}}"`
17558 */
17559 interpolatedUpdateInstruction(instruction, elementIndex, attrName, input, value, params) {
17560 this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, instruction, () => [literal$1(attrName), ...this.getUpdateInstructionArguments(value), ...params]);
17561 }
17562 visitContent(ngContent) {
17563 const slot = this.allocateDataSlot();
17564 const projectionSlotIdx = this._ngContentSelectorsOffset + this._ngContentReservedSlots.length;
17565 const parameters = [literal$1(slot)];
17566 this._ngContentReservedSlots.push(ngContent.selector);
17567 const nonContentSelectAttributes = ngContent.attributes.filter(attr => attr.name.toLowerCase() !== NG_CONTENT_SELECT_ATTR);
17568 const attributes = this.getAttributeExpressions(ngContent.name, nonContentSelectAttributes, [], []);
17569 if (attributes.length > 0) {
17570 parameters.push(literal$1(projectionSlotIdx), literalArr(attributes));
17571 }
17572 else if (projectionSlotIdx !== 0) {
17573 parameters.push(literal$1(projectionSlotIdx));
17574 }
17575 this.creationInstruction(ngContent.sourceSpan, Identifiers$1.projection, parameters);
17576 if (this.i18n) {
17577 this.i18n.appendProjection(ngContent.i18n, slot);
17578 }
17579 }
17580 visitElement(element) {
17581 const elementIndex = this.allocateDataSlot();
17582 const stylingBuilder = new StylingBuilder(null);
17583 let isNonBindableMode = false;
17584 const isI18nRootElement = isI18nRootNode(element.i18n) && !isSingleI18nIcu(element.i18n);
17585 const outputAttrs = [];
17586 const [namespaceKey, elementName] = splitNsName(element.name);
17587 const isNgContainer$1 = isNgContainer(element.name);
17588 // Handle styling, i18n, ngNonBindable attributes
17589 for (const attr of element.attributes) {
17590 const { name, value } = attr;
17591 if (name === NON_BINDABLE_ATTR) {
17592 isNonBindableMode = true;
17593 }
17594 else if (name === 'style') {
17595 stylingBuilder.registerStyleAttr(value);
17596 }
17597 else if (name === 'class') {
17598 stylingBuilder.registerClassAttr(value);
17599 }
17600 else {
17601 outputAttrs.push(attr);
17602 }
17603 }
17604 // Match directives on non i18n attributes
17605 this.matchDirectives(element.name, element);
17606 // Regular element or ng-container creation mode
17607 const parameters = [literal$1(elementIndex)];
17608 if (!isNgContainer$1) {
17609 parameters.push(literal$1(elementName));
17610 }
17611 // Add the attributes
17612 const allOtherInputs = [];
17613 const boundI18nAttrs = [];
17614 element.inputs.forEach(input => {
17615 const stylingInputWasSet = stylingBuilder.registerBoundInput(input);
17616 if (!stylingInputWasSet) {
17617 if (input.type === 0 /* Property */ && input.i18n) {
17618 boundI18nAttrs.push(input);
17619 }
17620 else {
17621 allOtherInputs.push(input);
17622 }
17623 }
17624 });
17625 // add attributes for directive and projection matching purposes
17626 const attributes = this.getAttributeExpressions(element.name, outputAttrs, allOtherInputs, element.outputs, stylingBuilder, [], boundI18nAttrs);
17627 parameters.push(this.addAttrsToConsts(attributes));
17628 // local refs (ex.: <div #foo #bar="baz">)
17629 const refs = this.prepareRefsArray(element.references);
17630 parameters.push(this.addToConsts(refs));
17631 const wasInNamespace = this._namespace;
17632 const currentNamespace = this.getNamespaceInstruction(namespaceKey);
17633 // If the namespace is changing now, include an instruction to change it
17634 // during element creation.
17635 if (currentNamespace !== wasInNamespace) {
17636 this.addNamespaceInstruction(currentNamespace, element);
17637 }
17638 if (this.i18n) {
17639 this.i18n.appendElement(element.i18n, elementIndex);
17640 }
17641 // Note that we do not append text node instructions and ICUs inside i18n section,
17642 // so we exclude them while calculating whether current element has children
17643 const hasChildren = (!isI18nRootElement && this.i18n) ? !hasTextChildrenOnly(element.children) :
17644 element.children.length > 0;
17645 const createSelfClosingInstruction = !stylingBuilder.hasBindingsWithPipes &&
17646 element.outputs.length === 0 && boundI18nAttrs.length === 0 && !hasChildren;
17647 const createSelfClosingI18nInstruction = !createSelfClosingInstruction && hasTextChildrenOnly(element.children);
17648 if (createSelfClosingInstruction) {
17649 this.creationInstruction(element.sourceSpan, isNgContainer$1 ? Identifiers$1.elementContainer : Identifiers$1.element, trimTrailingNulls(parameters));
17650 }
17651 else {
17652 this.creationInstruction(element.startSourceSpan, isNgContainer$1 ? Identifiers$1.elementContainerStart : Identifiers$1.elementStart, trimTrailingNulls(parameters));
17653 if (isNonBindableMode) {
17654 this.creationInstruction(element.startSourceSpan, Identifiers$1.disableBindings);
17655 }
17656 if (boundI18nAttrs.length > 0) {
17657 this.i18nAttributesInstruction(elementIndex, boundI18nAttrs, element.startSourceSpan ?? element.sourceSpan);
17658 }
17659 // Generate Listeners (outputs)
17660 if (element.outputs.length > 0) {
17661 const listeners = element.outputs.map((outputAst) => ({
17662 sourceSpan: outputAst.sourceSpan,
17663 params: this.prepareListenerParameter(element.name, outputAst, elementIndex)
17664 }));
17665 this.creationInstructionChain(Identifiers$1.listener, listeners);
17666 }
17667 // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
17668 // listeners, to make sure i18nAttributes instruction targets current element at runtime.
17669 if (isI18nRootElement) {
17670 this.i18nStart(element.startSourceSpan, element.i18n, createSelfClosingI18nInstruction);
17671 }
17672 }
17673 // the code here will collect all update-level styling instructions and add them to the
17674 // update block of the template function AOT code. Instructions like `styleProp`,
17675 // `styleMap`, `classMap`, `classProp`
17676 // are all generated and assigned in the code below.
17677 const stylingInstructions = stylingBuilder.buildUpdateLevelInstructions(this._valueConverter);
17678 const limit = stylingInstructions.length - 1;
17679 for (let i = 0; i <= limit; i++) {
17680 const instruction = stylingInstructions[i];
17681 this._bindingSlots += this.processStylingUpdateInstruction(elementIndex, instruction);
17682 }
17683 // the reason why `undefined` is used is because the renderer understands this as a
17684 // special value to symbolize that there is no RHS to this binding
17685 // TODO (matsko): revisit this once FW-959 is approached
17686 const emptyValueBindInstruction = literal$1(undefined);
17687 const propertyBindings = [];
17688 const attributeBindings = [];
17689 // Generate element input bindings
17690 allOtherInputs.forEach(input => {
17691 const inputType = input.type;
17692 if (inputType === 4 /* Animation */) {
17693 const value = input.value.visit(this._valueConverter);
17694 // animation bindings can be presented in the following formats:
17695 // 1. [@binding]="fooExp"
17696 // 2. [@binding]="{value:fooExp, params:{...}}"
17697 // 3. [@binding]
17698 // 4. @binding
17699 // All formats will be valid for when a synthetic binding is created.
17700 // The reasoning for this is because the renderer should get each
17701 // synthetic binding value in the order of the array that they are
17702 // defined in...
17703 const hasValue = value instanceof LiteralPrimitive ? !!value.value : true;
17704 this.allocateBindingSlots(value);
17705 propertyBindings.push({
17706 name: prepareSyntheticPropertyName(input.name),
17707 sourceSpan: input.sourceSpan,
17708 value: () => hasValue ? this.convertPropertyBinding(value) : emptyValueBindInstruction
17709 });
17710 }
17711 else {
17712 // we must skip attributes with associated i18n context, since these attributes are handled
17713 // separately and corresponding `i18nExp` and `i18nApply` instructions will be generated
17714 if (input.i18n)
17715 return;
17716 const value = input.value.visit(this._valueConverter);
17717 if (value !== undefined) {
17718 const params = [];
17719 const [attrNamespace, attrName] = splitNsName(input.name);
17720 const isAttributeBinding = inputType === 1 /* Attribute */;
17721 const sanitizationRef = resolveSanitizationFn(input.securityContext, isAttributeBinding);
17722 if (sanitizationRef)
17723 params.push(sanitizationRef);
17724 if (attrNamespace) {
17725 const namespaceLiteral = literal$1(attrNamespace);
17726 if (sanitizationRef) {
17727 params.push(namespaceLiteral);
17728 }
17729 else {
17730 // If there wasn't a sanitization ref, we need to add
17731 // an extra param so that we can pass in the namespace.
17732 params.push(literal$1(null), namespaceLiteral);
17733 }
17734 }
17735 this.allocateBindingSlots(value);
17736 if (inputType === 0 /* Property */) {
17737 if (value instanceof Interpolation) {
17738 // prop="{{value}}" and friends
17739 this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), elementIndex, attrName, input, value, params);
17740 }
17741 else {
17742 // [prop]="value"
17743 // Collect all the properties so that we can chain into a single function at the end.
17744 propertyBindings.push({
17745 name: attrName,
17746 sourceSpan: input.sourceSpan,
17747 value: () => this.convertPropertyBinding(value),
17748 params
17749 });
17750 }
17751 }
17752 else if (inputType === 1 /* Attribute */) {
17753 if (value instanceof Interpolation && getInterpolationArgsLength(value) > 1) {
17754 // attr.name="text{{value}}" and friends
17755 this.interpolatedUpdateInstruction(getAttributeInterpolationExpression(value), elementIndex, attrName, input, value, params);
17756 }
17757 else {
17758 const boundValue = value instanceof Interpolation ? value.expressions[0] : value;
17759 // [attr.name]="value" or attr.name="{{value}}"
17760 // Collect the attribute bindings so that they can be chained at the end.
17761 attributeBindings.push({
17762 name: attrName,
17763 sourceSpan: input.sourceSpan,
17764 value: () => this.convertPropertyBinding(boundValue),
17765 params
17766 });
17767 }
17768 }
17769 else {
17770 // class prop
17771 this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, Identifiers$1.classProp, () => {
17772 return [
17773 literal$1(elementIndex), literal$1(attrName), this.convertPropertyBinding(value),
17774 ...params
17775 ];
17776 });
17777 }
17778 }
17779 }
17780 });
17781 if (propertyBindings.length > 0) {
17782 this.updateInstructionChainWithAdvance(elementIndex, Identifiers$1.property, propertyBindings);
17783 }
17784 if (attributeBindings.length > 0) {
17785 this.updateInstructionChainWithAdvance(elementIndex, Identifiers$1.attribute, attributeBindings);
17786 }
17787 // Traverse element child nodes
17788 visitAll$1(this, element.children);
17789 if (!isI18nRootElement && this.i18n) {
17790 this.i18n.appendElement(element.i18n, elementIndex, true);
17791 }
17792 if (!createSelfClosingInstruction) {
17793 // Finish element construction mode.
17794 const span = element.endSourceSpan ?? element.sourceSpan;
17795 if (isI18nRootElement) {
17796 this.i18nEnd(span, createSelfClosingI18nInstruction);
17797 }
17798 if (isNonBindableMode) {
17799 this.creationInstruction(span, Identifiers$1.enableBindings);
17800 }
17801 this.creationInstruction(span, isNgContainer$1 ? Identifiers$1.elementContainerEnd : Identifiers$1.elementEnd);
17802 }
17803 }
17804 visitTemplate(template) {
17805 const NG_TEMPLATE_TAG_NAME = 'ng-template';
17806 const templateIndex = this.allocateDataSlot();
17807 if (this.i18n) {
17808 this.i18n.appendTemplate(template.i18n, templateIndex);
17809 }
17810 const tagNameWithoutNamespace = template.tagName ? splitNsName(template.tagName)[1] : template.tagName;
17811 const contextName = `${this.contextName}${template.tagName ? '_' + sanitizeIdentifier(template.tagName) : ''}_${templateIndex}`;
17812 const templateName = `${contextName}_Template`;
17813 const parameters = [
17814 literal$1(templateIndex),
17815 variable(templateName),
17816 // We don't care about the tag's namespace here, because we infer
17817 // it based on the parent nodes inside the template instruction.
17818 literal$1(tagNameWithoutNamespace),
17819 ];
17820 // find directives matching on a given <ng-template> node
17821 this.matchDirectives(NG_TEMPLATE_TAG_NAME, template);
17822 // prepare attributes parameter (including attributes used for directive matching)
17823 const attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs);
17824 parameters.push(this.addAttrsToConsts(attrsExprs));
17825 // local refs (ex.: <ng-template #foo>)
17826 if (template.references && template.references.length) {
17827 const refs = this.prepareRefsArray(template.references);
17828 parameters.push(this.addToConsts(refs));
17829 parameters.push(importExpr(Identifiers$1.templateRefExtractor));
17830 }
17831 // Create the template function
17832 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);
17833 // Nested templates must not be visited until after their parent templates have completed
17834 // processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
17835 // be able to support bindings in nested templates to local refs that occur after the
17836 // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div>
17837 this._nestedTemplateFns.push(() => {
17838 const templateFunctionExpr = templateVisitor.buildTemplateFunction(template.children, template.variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, template.i18n);
17839 this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName));
17840 if (templateVisitor._ngContentReservedSlots.length) {
17841 this._ngContentReservedSlots.push(...templateVisitor._ngContentReservedSlots);
17842 }
17843 });
17844 // e.g. template(1, MyComp_Template_1)
17845 this.creationInstruction(template.sourceSpan, Identifiers$1.templateCreate, () => {
17846 parameters.splice(2, 0, literal$1(templateVisitor.getConstCount()), literal$1(templateVisitor.getVarCount()));
17847 return trimTrailingNulls(parameters);
17848 });
17849 // handle property bindings e.g. ɵɵproperty('ngForOf', ctx.items), et al;
17850 this.templatePropertyBindings(templateIndex, template.templateAttrs);
17851 // Only add normal input/output binding instructions on explicit <ng-template> elements.
17852 if (tagNameWithoutNamespace === NG_TEMPLATE_TAG_NAME) {
17853 const [i18nInputs, inputs] = partitionArray(template.inputs, hasI18nMeta);
17854 // Add i18n attributes that may act as inputs to directives. If such attributes are present,
17855 // generate `i18nAttributes` instruction. Note: we generate it only for explicit <ng-template>
17856 // elements, in case of inline templates, corresponding instructions will be generated in the
17857 // nested template function.
17858 if (i18nInputs.length > 0) {
17859 this.i18nAttributesInstruction(templateIndex, i18nInputs, template.startSourceSpan ?? template.sourceSpan);
17860 }
17861 // Add the input bindings
17862 if (inputs.length > 0) {
17863 this.templatePropertyBindings(templateIndex, inputs);
17864 }
17865 // Generate listeners for directive output
17866 if (template.outputs.length > 0) {
17867 const listeners = template.outputs.map((outputAst) => ({
17868 sourceSpan: outputAst.sourceSpan,
17869 params: this.prepareListenerParameter('ng_template', outputAst, templateIndex)
17870 }));
17871 this.creationInstructionChain(Identifiers$1.listener, listeners);
17872 }
17873 }
17874 }
17875 visitBoundText(text) {
17876 if (this.i18n) {
17877 const value = text.value.visit(this._valueConverter);
17878 this.allocateBindingSlots(value);
17879 if (value instanceof Interpolation) {
17880 this.i18n.appendBoundText(text.i18n);
17881 this.i18nAppendBindings(value.expressions);
17882 }
17883 return;
17884 }
17885 const nodeIndex = this.allocateDataSlot();
17886 this.creationInstruction(text.sourceSpan, Identifiers$1.text, [literal$1(nodeIndex)]);
17887 const value = text.value.visit(this._valueConverter);
17888 this.allocateBindingSlots(value);
17889 if (value instanceof Interpolation) {
17890 this.updateInstructionWithAdvance(nodeIndex, text.sourceSpan, getTextInterpolationExpression(value), () => this.getUpdateInstructionArguments(value));
17891 }
17892 else {
17893 error('Text nodes should be interpolated and never bound directly.');
17894 }
17895 }
17896 visitText(text) {
17897 // when a text element is located within a translatable
17898 // block, we exclude this text element from instructions set,
17899 // since it will be captured in i18n content and processed at runtime
17900 if (!this.i18n) {
17901 this.creationInstruction(text.sourceSpan, Identifiers$1.text, [literal$1(this.allocateDataSlot()), literal$1(text.value)]);
17902 }
17903 }
17904 visitIcu(icu) {
17905 let initWasInvoked = false;
17906 // if an ICU was created outside of i18n block, we still treat
17907 // it as a translatable entity and invoke i18nStart and i18nEnd
17908 // to generate i18n context and the necessary instructions
17909 if (!this.i18n) {
17910 initWasInvoked = true;
17911 this.i18nStart(null, icu.i18n, true);
17912 }
17913 const i18n = this.i18n;
17914 const vars = this.i18nBindProps(icu.vars);
17915 const placeholders = this.i18nBindProps(icu.placeholders);
17916 // output ICU directly and keep ICU reference in context
17917 const message = icu.i18n;
17918 // we always need post-processing function for ICUs, to make sure that:
17919 // - all placeholders in a form of {PLACEHOLDER} are replaced with actual values (note:
17920 // `goog.getMsg` does not process ICUs and uses the `{PLACEHOLDER}` format for placeholders
17921 // inside ICUs)
17922 // - all ICU vars (such as `VAR_SELECT` or `VAR_PLURAL`) are replaced with correct values
17923 const transformFn = (raw) => {
17924 const params = { ...vars, ...placeholders };
17925 const formatted = i18nFormatPlaceholderNames(params, /* useCamelCase */ false);
17926 return instruction(null, Identifiers$1.i18nPostprocess, [raw, mapLiteral(formatted, true)]);
17927 };
17928 // in case the whole i18n message is a single ICU - we do not need to
17929 // create a separate top-level translation, we can use the root ref instead
17930 // and make this ICU a top-level translation
17931 // note: ICU placeholders are replaced with actual values in `i18nPostprocess` function
17932 // separately, so we do not pass placeholders into `i18nTranslate` function.
17933 if (isSingleI18nIcu(i18n.meta)) {
17934 this.i18nTranslate(message, /* placeholders */ {}, i18n.ref, transformFn);
17935 }
17936 else {
17937 // output ICU directly and keep ICU reference in context
17938 const ref = this.i18nTranslate(message, /* placeholders */ {}, /* ref */ undefined, transformFn);
17939 i18n.appendIcu(icuFromI18nMessage(message).name, ref);
17940 }
17941 if (initWasInvoked) {
17942 this.i18nEnd(null, true);
17943 }
17944 return null;
17945 }
17946 allocateDataSlot() {
17947 return this._dataIndex++;
17948 }
17949 getConstCount() {
17950 return this._dataIndex;
17951 }
17952 getVarCount() {
17953 return this._pureFunctionSlots;
17954 }
17955 getConsts() {
17956 return this._constants;
17957 }
17958 getNgContentSelectors() {
17959 return this._ngContentReservedSlots.length ?
17960 this.constantPool.getConstLiteral(asLiteral(this._ngContentReservedSlots), true) :
17961 null;
17962 }
17963 bindingContext() {
17964 return `${this._bindingContext++}`;
17965 }
17966 templatePropertyBindings(templateIndex, attrs) {
17967 const propertyBindings = [];
17968 attrs.forEach(input => {
17969 if (input instanceof BoundAttribute) {
17970 const value = input.value.visit(this._valueConverter);
17971 if (value !== undefined) {
17972 this.allocateBindingSlots(value);
17973 if (value instanceof Interpolation) {
17974 // Params typically contain attribute namespace and value sanitizer, which is applicable
17975 // for regular HTML elements, but not applicable for <ng-template> (since props act as
17976 // inputs to directives), so keep params array empty.
17977 const params = [];
17978 // prop="{{value}}" case
17979 this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), templateIndex, input.name, input, value, params);
17980 }
17981 else {
17982 // [prop]="value" case
17983 propertyBindings.push({
17984 name: input.name,
17985 sourceSpan: input.sourceSpan,
17986 value: () => this.convertPropertyBinding(value)
17987 });
17988 }
17989 }
17990 }
17991 });
17992 if (propertyBindings.length > 0) {
17993 this.updateInstructionChainWithAdvance(templateIndex, Identifiers$1.property, propertyBindings);
17994 }
17995 }
17996 // Bindings must only be resolved after all local refs have been visited, so all
17997 // instructions are queued in callbacks that execute once the initial pass has completed.
17998 // Otherwise, we wouldn't be able to support local refs that are defined after their
17999 // bindings. e.g. {{ foo }} <div #foo></div>
18000 instructionFn(fns, span, reference, paramsOrFn, prepend = false) {
18001 fns[prepend ? 'unshift' : 'push'](() => {
18002 const params = Array.isArray(paramsOrFn) ? paramsOrFn : paramsOrFn();
18003 return instruction(span, reference, params).toStmt();
18004 });
18005 }
18006 processStylingUpdateInstruction(elementIndex, instruction) {
18007 let allocateBindingSlots = 0;
18008 if (instruction) {
18009 const calls = [];
18010 instruction.calls.forEach(call => {
18011 allocateBindingSlots += call.allocateBindingSlots;
18012 calls.push({
18013 sourceSpan: call.sourceSpan,
18014 value: () => {
18015 return call.params(value => (call.supportsInterpolation && value instanceof Interpolation) ?
18016 this.getUpdateInstructionArguments(value) :
18017 this.convertPropertyBinding(value));
18018 }
18019 });
18020 });
18021 this.updateInstructionChainWithAdvance(elementIndex, instruction.reference, calls);
18022 }
18023 return allocateBindingSlots;
18024 }
18025 creationInstruction(span, reference, paramsOrFn, prepend) {
18026 this.instructionFn(this._creationCodeFns, span, reference, paramsOrFn || [], prepend);
18027 }
18028 creationInstructionChain(reference, calls) {
18029 const span = calls.length ? calls[0].sourceSpan : null;
18030 this._creationCodeFns.push(() => {
18031 return chainedInstruction(reference, calls.map(call => call.params()), span).toStmt();
18032 });
18033 }
18034 updateInstructionWithAdvance(nodeIndex, span, reference, paramsOrFn) {
18035 this.addAdvanceInstructionIfNecessary(nodeIndex, span);
18036 this.updateInstruction(span, reference, paramsOrFn);
18037 }
18038 updateInstruction(span, reference, paramsOrFn) {
18039 this.instructionFn(this._updateCodeFns, span, reference, paramsOrFn || []);
18040 }
18041 updateInstructionChain(reference, bindings) {
18042 const span = bindings.length ? bindings[0].sourceSpan : null;
18043 this._updateCodeFns.push(() => {
18044 const calls = bindings.map(property => {
18045 const value = property.value();
18046 const fnParams = Array.isArray(value) ? value : [value];
18047 if (property.params) {
18048 fnParams.push(...property.params);
18049 }
18050 if (property.name) {
18051 // We want the property name to always be the first function parameter.
18052 fnParams.unshift(literal$1(property.name));
18053 }
18054 return fnParams;
18055 });
18056 return chainedInstruction(reference, calls, span).toStmt();
18057 });
18058 }
18059 updateInstructionChainWithAdvance(nodeIndex, reference, bindings) {
18060 this.addAdvanceInstructionIfNecessary(nodeIndex, bindings.length ? bindings[0].sourceSpan : null);
18061 this.updateInstructionChain(reference, bindings);
18062 }
18063 addAdvanceInstructionIfNecessary(nodeIndex, span) {
18064 if (nodeIndex !== this._currentIndex) {
18065 const delta = nodeIndex - this._currentIndex;
18066 if (delta < 1) {
18067 throw new Error('advance instruction can only go forwards');
18068 }
18069 this.instructionFn(this._updateCodeFns, span, Identifiers$1.advance, [literal$1(delta)]);
18070 this._currentIndex = nodeIndex;
18071 }
18072 }
18073 allocatePureFunctionSlots(numSlots) {
18074 const originalSlots = this._pureFunctionSlots;
18075 this._pureFunctionSlots += numSlots;
18076 return originalSlots;
18077 }
18078 allocateBindingSlots(value) {
18079 this._bindingSlots += value instanceof Interpolation ? value.expressions.length : 1;
18080 }
18081 /**
18082 * Gets an expression that refers to the implicit receiver. The implicit
18083 * receiver is always the root level context.
18084 */
18085 getImplicitReceiverExpr() {
18086 if (this._implicitReceiverExpr) {
18087 return this._implicitReceiverExpr;
18088 }
18089 return this._implicitReceiverExpr = this.level === 0 ?
18090 variable(CONTEXT_NAME) :
18091 this._bindingScope.getOrCreateSharedContextVar(0);
18092 }
18093 convertPropertyBinding(value) {
18094 const convertedPropertyBinding = convertPropertyBinding(this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.Expression, () => error('Unexpected interpolation'));
18095 const valExpr = convertedPropertyBinding.currValExpr;
18096 this._tempVariables.push(...convertedPropertyBinding.stmts);
18097 return valExpr;
18098 }
18099 /**
18100 * Gets a list of argument expressions to pass to an update instruction expression. Also updates
18101 * the temp variables state with temp variables that were identified as needing to be created
18102 * while visiting the arguments.
18103 * @param value The original expression we will be resolving an arguments list from.
18104 */
18105 getUpdateInstructionArguments(value) {
18106 const { args, stmts } = convertUpdateArguments(this, this.getImplicitReceiverExpr(), value, this.bindingContext());
18107 this._tempVariables.push(...stmts);
18108 return args;
18109 }
18110 matchDirectives(elementName, elOrTpl) {
18111 if (this.directiveMatcher) {
18112 const selector = createCssSelector(elementName, getAttrsForDirectiveMatching(elOrTpl));
18113 this.directiveMatcher.match(selector, (cssSelector, staticType) => {
18114 this.directives.add(staticType);
18115 });
18116 }
18117 }
18118 /**
18119 * Prepares all attribute expression values for the `TAttributes` array.
18120 *
18121 * The purpose of this function is to properly construct an attributes array that
18122 * is passed into the `elementStart` (or just `element`) functions. Because there
18123 * are many different types of attributes, the array needs to be constructed in a
18124 * special way so that `elementStart` can properly evaluate them.
18125 *
18126 * The format looks like this:
18127 *
18128 * ```
18129 * attrs = [prop, value, prop2, value2,
18130 * PROJECT_AS, selector,
18131 * CLASSES, class1, class2,
18132 * STYLES, style1, value1, style2, value2,
18133 * BINDINGS, name1, name2, name3,
18134 * TEMPLATE, name4, name5, name6,
18135 * I18N, name7, name8, ...]
18136 * ```
18137 *
18138 * Note that this function will fully ignore all synthetic (@foo) attribute values
18139 * because those values are intended to always be generated as property instructions.
18140 */
18141 getAttributeExpressions(elementName, renderAttributes, inputs, outputs, styles, templateAttrs = [], boundI18nAttrs = []) {
18142 const alreadySeen = new Set();
18143 const attrExprs = [];
18144 let ngProjectAsAttr;
18145 for (const attr of renderAttributes) {
18146 if (attr.name === NG_PROJECT_AS_ATTR_NAME) {
18147 ngProjectAsAttr = attr;
18148 }
18149 // Note that static i18n attributes aren't in the i18n array,
18150 // because they're treated in the same way as regular attributes.
18151 if (attr.i18n) {
18152 // When i18n attributes are present on elements with structural directives
18153 // (e.g. `<div *ngIf title="Hello" i18n-title>`), we want to avoid generating
18154 // duplicate i18n translation blocks for `ɵɵtemplate` and `ɵɵelement` instruction
18155 // attributes. So we do a cache lookup to see if suitable i18n translation block
18156 // already exists.
18157 const { i18nVarRefsCache } = this._constants;
18158 let i18nVarRef;
18159 if (i18nVarRefsCache.has(attr.i18n)) {
18160 i18nVarRef = i18nVarRefsCache.get(attr.i18n);
18161 }
18162 else {
18163 i18nVarRef = this.i18nTranslate(attr.i18n);
18164 i18nVarRefsCache.set(attr.i18n, i18nVarRef);
18165 }
18166 attrExprs.push(literal$1(attr.name), i18nVarRef);
18167 }
18168 else {
18169 attrExprs.push(...getAttributeNameLiterals(attr.name), trustedConstAttribute(elementName, attr));
18170 }
18171 }
18172 // Keep ngProjectAs next to the other name, value pairs so we can verify that we match
18173 // ngProjectAs marker in the attribute name slot.
18174 if (ngProjectAsAttr) {
18175 attrExprs.push(...getNgProjectAsLiteral(ngProjectAsAttr));
18176 }
18177 function addAttrExpr(key, value) {
18178 if (typeof key === 'string') {
18179 if (!alreadySeen.has(key)) {
18180 attrExprs.push(...getAttributeNameLiterals(key));
18181 value !== undefined && attrExprs.push(value);
18182 alreadySeen.add(key);
18183 }
18184 }
18185 else {
18186 attrExprs.push(literal$1(key));
18187 }
18188 }
18189 // it's important that this occurs before BINDINGS and TEMPLATE because once `elementStart`
18190 // comes across the BINDINGS or TEMPLATE markers then it will continue reading each value as
18191 // as single property value cell by cell.
18192 if (styles) {
18193 styles.populateInitialStylingAttrs(attrExprs);
18194 }
18195 if (inputs.length || outputs.length) {
18196 const attrsLengthBeforeInputs = attrExprs.length;
18197 for (let i = 0; i < inputs.length; i++) {
18198 const input = inputs[i];
18199 // We don't want the animation and attribute bindings in the
18200 // attributes array since they aren't used for directive matching.
18201 if (input.type !== 4 /* Animation */ && input.type !== 1 /* Attribute */) {
18202 addAttrExpr(input.name);
18203 }
18204 }
18205 for (let i = 0; i < outputs.length; i++) {
18206 const output = outputs[i];
18207 if (output.type !== 1 /* Animation */) {
18208 addAttrExpr(output.name);
18209 }
18210 }
18211 // this is a cheap way of adding the marker only after all the input/output
18212 // values have been filtered (by not including the animation ones) and added
18213 // to the expressions. The marker is important because it tells the runtime
18214 // code that this is where attributes without values start...
18215 if (attrExprs.length !== attrsLengthBeforeInputs) {
18216 attrExprs.splice(attrsLengthBeforeInputs, 0, literal$1(3 /* Bindings */));
18217 }
18218 }
18219 if (templateAttrs.length) {
18220 attrExprs.push(literal$1(4 /* Template */));
18221 templateAttrs.forEach(attr => addAttrExpr(attr.name));
18222 }
18223 if (boundI18nAttrs.length) {
18224 attrExprs.push(literal$1(6 /* I18n */));
18225 boundI18nAttrs.forEach(attr => addAttrExpr(attr.name));
18226 }
18227 return attrExprs;
18228 }
18229 addToConsts(expression) {
18230 if (isNull(expression)) {
18231 return TYPED_NULL_EXPR;
18232 }
18233 const consts = this._constants.constExpressions;
18234 // Try to reuse a literal that's already in the array, if possible.
18235 for (let i = 0; i < consts.length; i++) {
18236 if (consts[i].isEquivalent(expression)) {
18237 return literal$1(i);
18238 }
18239 }
18240 return literal$1(consts.push(expression) - 1);
18241 }
18242 addAttrsToConsts(attrs) {
18243 return attrs.length > 0 ? this.addToConsts(literalArr(attrs)) : TYPED_NULL_EXPR;
18244 }
18245 prepareRefsArray(references) {
18246 if (!references || references.length === 0) {
18247 return TYPED_NULL_EXPR;
18248 }
18249 const refsParam = flatten(references.map(reference => {
18250 const slot = this.allocateDataSlot();
18251 // Generate the update temporary.
18252 const variableName = this._bindingScope.freshReferenceName();
18253 const retrievalLevel = this.level;
18254 const lhs = variable(variableName);
18255 this._bindingScope.set(retrievalLevel, reference.name, lhs, 0 /* DEFAULT */, (scope, relativeLevel) => {
18256 // e.g. nextContext(2);
18257 const nextContextStmt = relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : [];
18258 // e.g. const $foo$ = reference(1);
18259 const refExpr = lhs.set(importExpr(Identifiers$1.reference).callFn([literal$1(slot)]));
18260 return nextContextStmt.concat(refExpr.toConstDecl());
18261 }, true);
18262 return [reference.name, reference.value];
18263 }));
18264 return asLiteral(refsParam);
18265 }
18266 prepareListenerParameter(tagName, outputAst, index) {
18267 return () => {
18268 const eventName = outputAst.name;
18269 const bindingFnName = outputAst.type === 1 /* Animation */ ?
18270 // synthetic @listener.foo values are treated the exact same as are standard listeners
18271 prepareSyntheticListenerFunctionName(eventName, outputAst.phase) :
18272 sanitizeIdentifier(eventName);
18273 const handlerName = `${this.templateName}_${tagName}_${bindingFnName}_${index}_listener`;
18274 const scope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel, EVENT_BINDING_SCOPE_GLOBALS);
18275 return prepareEventListenerParameters(outputAst, handlerName, scope);
18276 };
18277 }
18278 }
18279 class ValueConverter extends AstMemoryEfficientTransformer {
18280 constructor(constantPool, allocateSlot, allocatePureFunctionSlots, definePipe) {
18281 super();
18282 this.constantPool = constantPool;
18283 this.allocateSlot = allocateSlot;
18284 this.allocatePureFunctionSlots = allocatePureFunctionSlots;
18285 this.definePipe = definePipe;
18286 this._pipeBindExprs = [];
18287 }
18288 // AstMemoryEfficientTransformer
18289 visitPipe(pipe, context) {
18290 // Allocate a slot to create the pipe
18291 const slot = this.allocateSlot();
18292 const slotPseudoLocal = `PIPE:${slot}`;
18293 // Allocate one slot for the result plus one slot per pipe argument
18294 const pureFunctionSlot = this.allocatePureFunctionSlots(2 + pipe.args.length);
18295 const target = new PropertyRead(pipe.span, pipe.sourceSpan, pipe.nameSpan, new ImplicitReceiver(pipe.span, pipe.sourceSpan), slotPseudoLocal);
18296 const { identifier, isVarLength } = pipeBindingCallInfo(pipe.args);
18297 this.definePipe(pipe.name, slotPseudoLocal, slot, importExpr(identifier));
18298 const args = [pipe.exp, ...pipe.args];
18299 const convertedArgs = isVarLength ?
18300 this.visitAll([new LiteralArray(pipe.span, pipe.sourceSpan, args)]) :
18301 this.visitAll(args);
18302 const pipeBindExpr = new Call(pipe.span, pipe.sourceSpan, target, [
18303 new LiteralPrimitive(pipe.span, pipe.sourceSpan, slot),
18304 new LiteralPrimitive(pipe.span, pipe.sourceSpan, pureFunctionSlot),
18305 ...convertedArgs,
18306 ], null);
18307 this._pipeBindExprs.push(pipeBindExpr);
18308 return pipeBindExpr;
18309 }
18310 updatePipeSlotOffsets(bindingSlots) {
18311 this._pipeBindExprs.forEach((pipe) => {
18312 // update the slot offset arg (index 1) to account for binding slots
18313 const slotOffset = pipe.args[1];
18314 slotOffset.value += bindingSlots;
18315 });
18316 }
18317 visitLiteralArray(array, context) {
18318 return new BuiltinFunctionCall(array.span, array.sourceSpan, this.visitAll(array.expressions), values => {
18319 // If the literal has calculated (non-literal) elements transform it into
18320 // calls to literal factories that compose the literal and will cache intermediate
18321 // values.
18322 const literal = literalArr(values);
18323 return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
18324 });
18325 }
18326 visitLiteralMap(map, context) {
18327 return new BuiltinFunctionCall(map.span, map.sourceSpan, this.visitAll(map.values), values => {
18328 // If the literal has calculated (non-literal) elements transform it into
18329 // calls to literal factories that compose the literal and will cache intermediate
18330 // values.
18331 const literal = literalMap(values.map((value, index) => ({ key: map.keys[index].key, value, quoted: map.keys[index].quoted })));
18332 return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
18333 });
18334 }
18335 }
18336 // Pipes always have at least one parameter, the value they operate on
18337 const pipeBindingIdentifiers = [Identifiers$1.pipeBind1, Identifiers$1.pipeBind2, Identifiers$1.pipeBind3, Identifiers$1.pipeBind4];
18338 function pipeBindingCallInfo(args) {
18339 const identifier = pipeBindingIdentifiers[args.length];
18340 return {
18341 identifier: identifier || Identifiers$1.pipeBindV,
18342 isVarLength: !identifier,
18343 };
18344 }
18345 const pureFunctionIdentifiers = [
18346 Identifiers$1.pureFunction0, Identifiers$1.pureFunction1, Identifiers$1.pureFunction2, Identifiers$1.pureFunction3, Identifiers$1.pureFunction4,
18347 Identifiers$1.pureFunction5, Identifiers$1.pureFunction6, Identifiers$1.pureFunction7, Identifiers$1.pureFunction8
18348 ];
18349 function pureFunctionCallInfo(args) {
18350 const identifier = pureFunctionIdentifiers[args.length];
18351 return {
18352 identifier: identifier || Identifiers$1.pureFunctionV,
18353 isVarLength: !identifier,
18354 };
18355 }
18356 function instruction(span, reference, params) {
18357 return importExpr(reference, null, span).callFn(params, span);
18358 }
18359 // e.g. x(2);
18360 function generateNextContextExpr(relativeLevelDiff) {
18361 return importExpr(Identifiers$1.nextContext)
18362 .callFn(relativeLevelDiff > 1 ? [literal$1(relativeLevelDiff)] : []);
18363 }
18364 function getLiteralFactory(constantPool, literal, allocateSlots) {
18365 const { literalFactory, literalFactoryArguments } = constantPool.getLiteralFactory(literal);
18366 // Allocate 1 slot for the result plus 1 per argument
18367 const startSlot = allocateSlots(1 + literalFactoryArguments.length);
18368 const { identifier, isVarLength } = pureFunctionCallInfo(literalFactoryArguments);
18369 // Literal factories are pure functions that only need to be re-invoked when the parameters
18370 // change.
18371 const args = [literal$1(startSlot), literalFactory];
18372 if (isVarLength) {
18373 args.push(literalArr(literalFactoryArguments));
18374 }
18375 else {
18376 args.push(...literalFactoryArguments);
18377 }
18378 return importExpr(identifier).callFn(args);
18379 }
18380 /**
18381 * Gets an array of literals that can be added to an expression
18382 * to represent the name and namespace of an attribute. E.g.
18383 * `:xlink:href` turns into `[AttributeMarker.NamespaceURI, 'xlink', 'href']`.
18384 *
18385 * @param name Name of the attribute, including the namespace.
18386 */
18387 function getAttributeNameLiterals(name) {
18388 const [attributeNamespace, attributeName] = splitNsName(name);
18389 const nameLiteral = literal$1(attributeName);
18390 if (attributeNamespace) {
18391 return [
18392 literal$1(0 /* NamespaceURI */), literal$1(attributeNamespace), nameLiteral
18393 ];
18394 }
18395 return [nameLiteral];
18396 }
18397 /** The prefix used to get a shared context in BindingScope's map. */
18398 const SHARED_CONTEXT_KEY = '$$shared_ctx$$';
18399 class BindingScope {
18400 constructor(bindingLevel = 0, parent = null, globals) {
18401 this.bindingLevel = bindingLevel;
18402 this.parent = parent;
18403 this.globals = globals;
18404 /** Keeps a map from local variables to their BindingData. */
18405 this.map = new Map();
18406 this.referenceNameIndex = 0;
18407 this.restoreViewVariable = null;
18408 this.usesRestoredViewContext = false;
18409 if (globals !== undefined) {
18410 for (const name of globals) {
18411 this.set(0, name, variable(name));
18412 }
18413 }
18414 }
18415 static createRootScope() {
18416 return new BindingScope();
18417 }
18418 get(name) {
18419 let current = this;
18420 while (current) {
18421 let value = current.map.get(name);
18422 if (value != null) {
18423 if (current !== this) {
18424 // make a local copy and reset the `declare` state
18425 value = {
18426 retrievalLevel: value.retrievalLevel,
18427 lhs: value.lhs,
18428 declareLocalCallback: value.declareLocalCallback,
18429 declare: false,
18430 priority: value.priority
18431 };
18432 // Cache the value locally.
18433 this.map.set(name, value);
18434 // Possibly generate a shared context var
18435 this.maybeGenerateSharedContextVar(value);
18436 this.maybeRestoreView();
18437 }
18438 if (value.declareLocalCallback && !value.declare) {
18439 value.declare = true;
18440 }
18441 return value.lhs;
18442 }
18443 current = current.parent;
18444 }
18445 // If we get to this point, we are looking for a property on the top level component
18446 // - If level === 0, we are on the top and don't need to re-declare `ctx`.
18447 // - If level > 0, we are in an embedded view. We need to retrieve the name of the
18448 // local var we used to store the component context, e.g. const $comp$ = x();
18449 return this.bindingLevel === 0 ? null : this.getComponentProperty(name);
18450 }
18451 /**
18452 * Create a local variable for later reference.
18453 *
18454 * @param retrievalLevel The level from which this value can be retrieved
18455 * @param name Name of the variable.
18456 * @param lhs AST representing the left hand side of the `let lhs = rhs;`.
18457 * @param priority The sorting priority of this var
18458 * @param declareLocalCallback The callback to invoke when declaring this local var
18459 * @param localRef Whether or not this is a local ref
18460 */
18461 set(retrievalLevel, name, lhs, priority = 0 /* DEFAULT */, declareLocalCallback, localRef) {
18462 if (this.map.has(name)) {
18463 if (localRef) {
18464 // Do not throw an error if it's a local ref and do not update existing value,
18465 // so the first defined ref is always returned.
18466 return this;
18467 }
18468 error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`);
18469 }
18470 this.map.set(name, {
18471 retrievalLevel: retrievalLevel,
18472 lhs: lhs,
18473 declare: false,
18474 declareLocalCallback: declareLocalCallback,
18475 priority: priority,
18476 });
18477 return this;
18478 }
18479 // Implemented as part of LocalResolver.
18480 getLocal(name) {
18481 return this.get(name);
18482 }
18483 // Implemented as part of LocalResolver.
18484 notifyImplicitReceiverUse() {
18485 if (this.bindingLevel !== 0) {
18486 // Since the implicit receiver is accessed in an embedded view, we need to
18487 // ensure that we declare a shared context variable for the current template
18488 // in the update variables.
18489 this.map.get(SHARED_CONTEXT_KEY + 0).declare = true;
18490 }
18491 }
18492 nestedScope(level, globals) {
18493 const newScope = new BindingScope(level, this, globals);
18494 if (level > 0)
18495 newScope.generateSharedContextVar(0);
18496 return newScope;
18497 }
18498 /**
18499 * Gets or creates a shared context variable and returns its expression. Note that
18500 * this does not mean that the shared variable will be declared. Variables in the
18501 * binding scope will be only declared if they are used.
18502 */
18503 getOrCreateSharedContextVar(retrievalLevel) {
18504 const bindingKey = SHARED_CONTEXT_KEY + retrievalLevel;
18505 if (!this.map.has(bindingKey)) {
18506 this.generateSharedContextVar(retrievalLevel);
18507 }
18508 // Shared context variables are always generated as "ReadVarExpr".
18509 return this.map.get(bindingKey).lhs;
18510 }
18511 getSharedContextName(retrievalLevel) {
18512 const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + retrievalLevel);
18513 // Shared context variables are always generated as "ReadVarExpr".
18514 return sharedCtxObj && sharedCtxObj.declare ? sharedCtxObj.lhs : null;
18515 }
18516 maybeGenerateSharedContextVar(value) {
18517 if (value.priority === 1 /* CONTEXT */ &&
18518 value.retrievalLevel < this.bindingLevel) {
18519 const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + value.retrievalLevel);
18520 if (sharedCtxObj) {
18521 sharedCtxObj.declare = true;
18522 }
18523 else {
18524 this.generateSharedContextVar(value.retrievalLevel);
18525 }
18526 }
18527 }
18528 generateSharedContextVar(retrievalLevel) {
18529 const lhs = variable(CONTEXT_NAME + this.freshReferenceName());
18530 this.map.set(SHARED_CONTEXT_KEY + retrievalLevel, {
18531 retrievalLevel: retrievalLevel,
18532 lhs: lhs,
18533 declareLocalCallback: (scope, relativeLevel) => {
18534 // const ctx_r0 = nextContext(2);
18535 return [lhs.set(generateNextContextExpr(relativeLevel)).toConstDecl()];
18536 },
18537 declare: false,
18538 priority: 2 /* SHARED_CONTEXT */,
18539 });
18540 }
18541 getComponentProperty(name) {
18542 const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0);
18543 componentValue.declare = true;
18544 this.maybeRestoreView();
18545 return componentValue.lhs.prop(name);
18546 }
18547 maybeRestoreView() {
18548 // View restoration is required for listener instructions inside embedded views, because
18549 // they only run in creation mode and they can have references to the context object.
18550 // If the context object changes in update mode, the reference will be incorrect, because
18551 // it was established during creation.
18552 if (this.isListenerScope()) {
18553 if (!this.parent.restoreViewVariable) {
18554 // parent saves variable to generate a shared `const $s$ = getCurrentView();` instruction
18555 this.parent.restoreViewVariable = variable(this.parent.freshReferenceName());
18556 }
18557 this.restoreViewVariable = this.parent.restoreViewVariable;
18558 }
18559 }
18560 restoreViewStatement() {
18561 const statements = [];
18562 if (this.restoreViewVariable) {
18563 const restoreCall = instruction(null, Identifiers$1.restoreView, [this.restoreViewVariable]);
18564 // Either `const restoredCtx = restoreView($state$);` or `restoreView($state$);`
18565 // depending on whether it is being used.
18566 statements.push(this.usesRestoredViewContext ?
18567 variable(RESTORED_VIEW_CONTEXT_NAME).set(restoreCall).toConstDecl() :
18568 restoreCall.toStmt());
18569 }
18570 return statements;
18571 }
18572 viewSnapshotStatements() {
18573 // const $state$ = getCurrentView();
18574 return this.restoreViewVariable ?
18575 [this.restoreViewVariable.set(instruction(null, Identifiers$1.getCurrentView, [])).toConstDecl()] :
18576 [];
18577 }
18578 isListenerScope() {
18579 return this.parent && this.parent.bindingLevel === this.bindingLevel;
18580 }
18581 variableDeclarations() {
18582 let currentContextLevel = 0;
18583 return Array.from(this.map.values())
18584 .filter(value => value.declare)
18585 .sort((a, b) => b.retrievalLevel - a.retrievalLevel || b.priority - a.priority)
18586 .reduce((stmts, value) => {
18587 const levelDiff = this.bindingLevel - value.retrievalLevel;
18588 const currStmts = value.declareLocalCallback(this, levelDiff - currentContextLevel);
18589 currentContextLevel = levelDiff;
18590 return stmts.concat(currStmts);
18591 }, []);
18592 }
18593 freshReferenceName() {
18594 let current = this;
18595 // Find the top scope as it maintains the global reference count
18596 while (current.parent)
18597 current = current.parent;
18598 const ref = `${REFERENCE_PREFIX}${current.referenceNameIndex++}`;
18599 return ref;
18600 }
18601 hasRestoreViewVariable() {
18602 return !!this.restoreViewVariable;
18603 }
18604 notifyRestoredViewContextUse() {
18605 this.usesRestoredViewContext = true;
18606 }
18607 }
18608 /**
18609 * Creates a `CssSelector` given a tag name and a map of attributes
18610 */
18611 function createCssSelector(elementName, attributes) {
18612 const cssSelector = new CssSelector();
18613 const elementNameNoNs = splitNsName(elementName)[1];
18614 cssSelector.setElement(elementNameNoNs);
18615 Object.getOwnPropertyNames(attributes).forEach((name) => {
18616 const nameNoNs = splitNsName(name)[1];
18617 const value = attributes[name];
18618 cssSelector.addAttribute(nameNoNs, value);
18619 if (name.toLowerCase() === 'class') {
18620 const classes = value.trim().split(/\s+/);
18621 classes.forEach(className => cssSelector.addClassName(className));
18622 }
18623 });
18624 return cssSelector;
18625 }
18626 /**
18627 * Creates an array of expressions out of an `ngProjectAs` attributes
18628 * which can be added to the instruction parameters.
18629 */
18630 function getNgProjectAsLiteral(attribute) {
18631 // Parse the attribute value into a CssSelectorList. Note that we only take the
18632 // first selector, because we don't support multiple selectors in ngProjectAs.
18633 const parsedR3Selector = parseSelectorToR3Selector(attribute.value)[0];
18634 return [literal$1(5 /* ProjectAs */), asLiteral(parsedR3Selector)];
18635 }
18636 /**
18637 * Gets the instruction to generate for an interpolated property
18638 * @param interpolation An Interpolation AST
18639 */
18640 function getPropertyInterpolationExpression(interpolation) {
18641 switch (getInterpolationArgsLength(interpolation)) {
18642 case 1:
18643 return Identifiers$1.propertyInterpolate;
18644 case 3:
18645 return Identifiers$1.propertyInterpolate1;
18646 case 5:
18647 return Identifiers$1.propertyInterpolate2;
18648 case 7:
18649 return Identifiers$1.propertyInterpolate3;
18650 case 9:
18651 return Identifiers$1.propertyInterpolate4;
18652 case 11:
18653 return Identifiers$1.propertyInterpolate5;
18654 case 13:
18655 return Identifiers$1.propertyInterpolate6;
18656 case 15:
18657 return Identifiers$1.propertyInterpolate7;
18658 case 17:
18659 return Identifiers$1.propertyInterpolate8;
18660 default:
18661 return Identifiers$1.propertyInterpolateV;
18662 }
18663 }
18664 /**
18665 * Gets the instruction to generate for an interpolated attribute
18666 * @param interpolation An Interpolation AST
18667 */
18668 function getAttributeInterpolationExpression(interpolation) {
18669 switch (getInterpolationArgsLength(interpolation)) {
18670 case 3:
18671 return Identifiers$1.attributeInterpolate1;
18672 case 5:
18673 return Identifiers$1.attributeInterpolate2;
18674 case 7:
18675 return Identifiers$1.attributeInterpolate3;
18676 case 9:
18677 return Identifiers$1.attributeInterpolate4;
18678 case 11:
18679 return Identifiers$1.attributeInterpolate5;
18680 case 13:
18681 return Identifiers$1.attributeInterpolate6;
18682 case 15:
18683 return Identifiers$1.attributeInterpolate7;
18684 case 17:
18685 return Identifiers$1.attributeInterpolate8;
18686 default:
18687 return Identifiers$1.attributeInterpolateV;
18688 }
18689 }
18690 /**
18691 * Gets the instruction to generate for interpolated text.
18692 * @param interpolation An Interpolation AST
18693 */
18694 function getTextInterpolationExpression(interpolation) {
18695 switch (getInterpolationArgsLength(interpolation)) {
18696 case 1:
18697 return Identifiers$1.textInterpolate;
18698 case 3:
18699 return Identifiers$1.textInterpolate1;
18700 case 5:
18701 return Identifiers$1.textInterpolate2;
18702 case 7:
18703 return Identifiers$1.textInterpolate3;
18704 case 9:
18705 return Identifiers$1.textInterpolate4;
18706 case 11:
18707 return Identifiers$1.textInterpolate5;
18708 case 13:
18709 return Identifiers$1.textInterpolate6;
18710 case 15:
18711 return Identifiers$1.textInterpolate7;
18712 case 17:
18713 return Identifiers$1.textInterpolate8;
18714 default:
18715 return Identifiers$1.textInterpolateV;
18716 }
18717 }
18718 /**
18719 * Parse a template into render3 `Node`s and additional metadata, with no other dependencies.
18720 *
18721 * @param template text of the template to parse
18722 * @param templateUrl URL to use for source mapping of the parsed template
18723 * @param options options to modify how the template is parsed
18724 */
18725 function parseTemplate(template, templateUrl, options = {}) {
18726 const { interpolationConfig, preserveWhitespaces, enableI18nLegacyMessageIdFormat } = options;
18727 const bindingParser = makeBindingParser(interpolationConfig);
18728 const htmlParser = new HtmlParser();
18729 const parseResult = htmlParser.parse(template, templateUrl, { leadingTriviaChars: LEADING_TRIVIA_CHARS, ...options, tokenizeExpansionForms: true });
18730 if (!options.alwaysAttemptHtmlToR3AstConversion && parseResult.errors &&
18731 parseResult.errors.length > 0) {
18732 const parsedTemplate = {
18733 interpolationConfig,
18734 preserveWhitespaces,
18735 errors: parseResult.errors,
18736 nodes: [],
18737 styleUrls: [],
18738 styles: [],
18739 ngContentSelectors: []
18740 };
18741 if (options.collectCommentNodes) {
18742 parsedTemplate.commentNodes = [];
18743 }
18744 return parsedTemplate;
18745 }
18746 let rootNodes = parseResult.rootNodes;
18747 // process i18n meta information (scan attributes, generate ids)
18748 // before we run whitespace removal process, because existing i18n
18749 // extraction process (ng extract-i18n) relies on a raw content to generate
18750 // message ids
18751 const i18nMetaVisitor = new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ !preserveWhitespaces, enableI18nLegacyMessageIdFormat);
18752 const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes);
18753 if (!options.alwaysAttemptHtmlToR3AstConversion && i18nMetaResult.errors &&
18754 i18nMetaResult.errors.length > 0) {
18755 const parsedTemplate = {
18756 interpolationConfig,
18757 preserveWhitespaces,
18758 errors: i18nMetaResult.errors,
18759 nodes: [],
18760 styleUrls: [],
18761 styles: [],
18762 ngContentSelectors: []
18763 };
18764 if (options.collectCommentNodes) {
18765 parsedTemplate.commentNodes = [];
18766 }
18767 return parsedTemplate;
18768 }
18769 rootNodes = i18nMetaResult.rootNodes;
18770 if (!preserveWhitespaces) {
18771 rootNodes = visitAll(new WhitespaceVisitor(), rootNodes);
18772 // run i18n meta visitor again in case whitespaces are removed (because that might affect
18773 // generated i18n message content) and first pass indicated that i18n content is present in a
18774 // template. During this pass i18n IDs generated at the first pass will be preserved, so we can
18775 // mimic existing extraction process (ng extract-i18n)
18776 if (i18nMetaVisitor.hasI18nMeta) {
18777 rootNodes = visitAll(new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ false), rootNodes);
18778 }
18779 }
18780 const { nodes, errors, styleUrls, styles, ngContentSelectors, commentNodes } = htmlAstToRender3Ast(rootNodes, bindingParser, { collectCommentNodes: !!options.collectCommentNodes });
18781 errors.push(...parseResult.errors, ...i18nMetaResult.errors);
18782 const parsedTemplate = {
18783 interpolationConfig,
18784 preserveWhitespaces,
18785 errors: errors.length > 0 ? errors : null,
18786 nodes,
18787 styleUrls,
18788 styles,
18789 ngContentSelectors
18790 };
18791 if (options.collectCommentNodes) {
18792 parsedTemplate.commentNodes = commentNodes;
18793 }
18794 return parsedTemplate;
18795 }
18796 const elementRegistry = new DomElementSchemaRegistry();
18797 /**
18798 * Construct a `BindingParser` with a default configuration.
18799 */
18800 function makeBindingParser(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
18801 return new BindingParser(new IvyParser(new Lexer()), interpolationConfig, elementRegistry, null, []);
18802 }
18803 function resolveSanitizationFn(context, isAttribute) {
18804 switch (context) {
18805 case SecurityContext.HTML:
18806 return importExpr(Identifiers$1.sanitizeHtml);
18807 case SecurityContext.SCRIPT:
18808 return importExpr(Identifiers$1.sanitizeScript);
18809 case SecurityContext.STYLE:
18810 // the compiler does not fill in an instruction for [style.prop?] binding
18811 // values because the style algorithm knows internally what props are subject
18812 // to sanitization (only [attr.style] values are explicitly sanitized)
18813 return isAttribute ? importExpr(Identifiers$1.sanitizeStyle) : null;
18814 case SecurityContext.URL:
18815 return importExpr(Identifiers$1.sanitizeUrl);
18816 case SecurityContext.RESOURCE_URL:
18817 return importExpr(Identifiers$1.sanitizeResourceUrl);
18818 default:
18819 return null;
18820 }
18821 }
18822 function trustedConstAttribute(tagName, attr) {
18823 const value = asLiteral(attr.value);
18824 if (isTrustedTypesSink(tagName, attr.name)) {
18825 switch (elementRegistry.securityContext(tagName, attr.name, /* isAttribute */ true)) {
18826 case SecurityContext.HTML:
18827 return taggedTemplate(importExpr(Identifiers$1.trustConstantHtml), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan);
18828 // NB: no SecurityContext.SCRIPT here, as the corresponding tags are stripped by the compiler.
18829 case SecurityContext.RESOURCE_URL:
18830 return taggedTemplate(importExpr(Identifiers$1.trustConstantResourceUrl), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan);
18831 default:
18832 return value;
18833 }
18834 }
18835 else {
18836 return value;
18837 }
18838 }
18839 function isSingleElementTemplate(children) {
18840 return children.length === 1 && children[0] instanceof Element$1;
18841 }
18842 function isTextNode(node) {
18843 return node instanceof Text$2 || node instanceof BoundText || node instanceof Icu$1;
18844 }
18845 function hasTextChildrenOnly(children) {
18846 return children.every(isTextNode);
18847 }
18848 /** Name of the global variable that is used to determine if we use Closure translations or not */
18849 const NG_I18N_CLOSURE_MODE = 'ngI18nClosureMode';
18850 /**
18851 * Generate statements that define a given translation message.
18852 *
18853 * ```
18854 * var I18N_1;
18855 * if (typeof ngI18nClosureMode !== undefined && ngI18nClosureMode) {
18856 * var MSG_EXTERNAL_XXX = goog.getMsg(
18857 * "Some message with {$interpolation}!",
18858 * { "interpolation": "\uFFFD0\uFFFD" }
18859 * );
18860 * I18N_1 = MSG_EXTERNAL_XXX;
18861 * }
18862 * else {
18863 * I18N_1 = $localize`Some message with ${'\uFFFD0\uFFFD'}!`;
18864 * }
18865 * ```
18866 *
18867 * @param message The original i18n AST message node
18868 * @param variable The variable that will be assigned the translation, e.g. `I18N_1`.
18869 * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
18870 * @param params Object mapping placeholder names to their values (e.g.
18871 * `{ "interpolation": "\uFFFD0\uFFFD" }`).
18872 * @param transformFn Optional transformation function that will be applied to the translation (e.g.
18873 * post-processing).
18874 * @returns An array of statements that defined a given translation.
18875 */
18876 function getTranslationDeclStmts(message, variable, closureVar, params = {}, transformFn) {
18877 const statements = [
18878 declareI18nVariable(variable),
18879 ifStmt(createClosureModeGuard(), createGoogleGetMsgStatements(variable, message, closureVar, i18nFormatPlaceholderNames(params, /* useCamelCase */ true)), createLocalizeStatements(variable, message, i18nFormatPlaceholderNames(params, /* useCamelCase */ false))),
18880 ];
18881 if (transformFn) {
18882 statements.push(new ExpressionStatement(variable.set(transformFn(variable))));
18883 }
18884 return statements;
18885 }
18886 /**
18887 * Create the expression that will be used to guard the closure mode block
18888 * It is equivalent to:
18889 *
18890 * ```
18891 * typeof ngI18nClosureMode !== undefined && ngI18nClosureMode
18892 * ```
18893 */
18894 function createClosureModeGuard() {
18895 return typeofExpr(variable(NG_I18N_CLOSURE_MODE))
18896 .notIdentical(literal$1('undefined', STRING_TYPE))
18897 .and(variable(NG_I18N_CLOSURE_MODE));
18898 }
18899
18900 /**
18901 * @license
18902 * Copyright Google LLC All Rights Reserved.
18903 *
18904 * Use of this source code is governed by an MIT-style license that can be
18905 * found in the LICENSE file at https://angular.io/license
18906 */
18907 // This regex matches any binding names that contain the "attr." prefix, e.g. "attr.required"
18908 // If there is a match, the first matching group will contain the attribute name to bind.
18909 const ATTR_REGEX = /attr\.([^\]]+)/;
18910 function baseDirectiveFields(meta, constantPool, bindingParser) {
18911 const definitionMap = new DefinitionMap();
18912 const selectors = parseSelectorToR3Selector(meta.selector);
18913 // e.g. `type: MyDirective`
18914 definitionMap.set('type', meta.internalType);
18915 // e.g. `selectors: [['', 'someDir', '']]`
18916 if (selectors.length > 0) {
18917 definitionMap.set('selectors', asLiteral(selectors));
18918 }
18919 if (meta.queries.length > 0) {
18920 // e.g. `contentQueries: (rf, ctx, dirIndex) => { ... }
18921 definitionMap.set('contentQueries', createContentQueriesFunction(meta.queries, constantPool, meta.name));
18922 }
18923 if (meta.viewQueries.length) {
18924 definitionMap.set('viewQuery', createViewQueriesFunction(meta.viewQueries, constantPool, meta.name));
18925 }
18926 // e.g. `hostBindings: (rf, ctx) => { ... }
18927 definitionMap.set('hostBindings', createHostBindingsFunction(meta.host, meta.typeSourceSpan, bindingParser, constantPool, meta.selector || '', meta.name, definitionMap));
18928 // e.g 'inputs: {a: 'a'}`
18929 definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
18930 // e.g 'outputs: {a: 'a'}`
18931 definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
18932 if (meta.exportAs !== null) {
18933 definitionMap.set('exportAs', literalArr(meta.exportAs.map(e => literal$1(e))));
18934 }
18935 return definitionMap;
18936 }
18937 /**
18938 * Add features to the definition map.
18939 */
18940 function addFeatures(definitionMap, meta) {
18941 // e.g. `features: [NgOnChangesFeature]`
18942 const features = [];
18943 const providers = meta.providers;
18944 const viewProviders = meta.viewProviders;
18945 if (providers || viewProviders) {
18946 const args = [providers || new LiteralArrayExpr([])];
18947 if (viewProviders) {
18948 args.push(viewProviders);
18949 }
18950 features.push(importExpr(Identifiers$1.ProvidersFeature).callFn(args));
18951 }
18952 if (meta.usesInheritance) {
18953 features.push(importExpr(Identifiers$1.InheritDefinitionFeature));
18954 }
18955 if (meta.fullInheritance) {
18956 features.push(importExpr(Identifiers$1.CopyDefinitionFeature));
18957 }
18958 if (meta.lifecycle.usesOnChanges) {
18959 features.push(importExpr(Identifiers$1.NgOnChangesFeature));
18960 }
18961 if (features.length) {
18962 definitionMap.set('features', literalArr(features));
18963 }
18964 }
18965 /**
18966 * Compile a directive for the render3 runtime as defined by the `R3DirectiveMetadata`.
18967 */
18968 function compileDirectiveFromMetadata(meta, constantPool, bindingParser) {
18969 const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
18970 addFeatures(definitionMap, meta);
18971 const expression = importExpr(Identifiers$1.defineDirective).callFn([definitionMap.toLiteralMap()], undefined, true);
18972 const type = createDirectiveType(meta);
18973 return { expression, type, statements: [] };
18974 }
18975 /**
18976 * Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`.
18977 */
18978 function compileComponentFromMetadata(meta, constantPool, bindingParser) {
18979 const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
18980 addFeatures(definitionMap, meta);
18981 const selector = meta.selector && CssSelector.parse(meta.selector);
18982 const firstSelector = selector && selector[0];
18983 // e.g. `attr: ["class", ".my.app"]`
18984 // This is optional an only included if the first selector of a component specifies attributes.
18985 if (firstSelector) {
18986 const selectorAttributes = firstSelector.getAttrs();
18987 if (selectorAttributes.length) {
18988 definitionMap.set('attrs', constantPool.getConstLiteral(literalArr(selectorAttributes.map(value => value != null ? literal$1(value) : literal$1(undefined))),
18989 /* forceShared */ true));
18990 }
18991 }
18992 // Generate the CSS matcher that recognize directive
18993 let directiveMatcher = null;
18994 if (meta.directives.length > 0) {
18995 const matcher = new SelectorMatcher();
18996 for (const { selector, type } of meta.directives) {
18997 matcher.addSelectables(CssSelector.parse(selector), type);
18998 }
18999 directiveMatcher = matcher;
19000 }
19001 // e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
19002 const templateTypeName = meta.name;
19003 const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
19004 const directivesUsed = new Set();
19005 const pipesUsed = new Set();
19006 const changeDetection = meta.changeDetection;
19007 const template = meta.template;
19008 const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, directiveMatcher, directivesUsed, meta.pipes, pipesUsed, Identifiers$1.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds);
19009 const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
19010 // We need to provide this so that dynamically generated components know what
19011 // projected content blocks to pass through to the component when it is instantiated.
19012 const ngContentSelectors = templateBuilder.getNgContentSelectors();
19013 if (ngContentSelectors) {
19014 definitionMap.set('ngContentSelectors', ngContentSelectors);
19015 }
19016 // e.g. `decls: 2`
19017 definitionMap.set('decls', literal$1(templateBuilder.getConstCount()));
19018 // e.g. `vars: 2`
19019 definitionMap.set('vars', literal$1(templateBuilder.getVarCount()));
19020 // Generate `consts` section of ComponentDef:
19021 // - either as an array:
19022 // `consts: [['one', 'two'], ['three', 'four']]`
19023 // - or as a factory function in case additional statements are present (to support i18n):
19024 // `consts: function() { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0]; }`
19025 const { constExpressions, prepareStatements } = templateBuilder.getConsts();
19026 if (constExpressions.length > 0) {
19027 let constsExpr = literalArr(constExpressions);
19028 // Prepare statements are present - turn `consts` into a function.
19029 if (prepareStatements.length > 0) {
19030 constsExpr = fn([], [...prepareStatements, new ReturnStatement(constsExpr)]);
19031 }
19032 definitionMap.set('consts', constsExpr);
19033 }
19034 definitionMap.set('template', templateFunctionExpression);
19035 // e.g. `directives: [MyDirective]`
19036 if (directivesUsed.size) {
19037 const directivesList = literalArr(Array.from(directivesUsed));
19038 const directivesExpr = compileDeclarationList(directivesList, meta.declarationListEmitMode);
19039 definitionMap.set('directives', directivesExpr);
19040 }
19041 // e.g. `pipes: [MyPipe]`
19042 if (pipesUsed.size) {
19043 const pipesList = literalArr(Array.from(pipesUsed));
19044 const pipesExpr = compileDeclarationList(pipesList, meta.declarationListEmitMode);
19045 definitionMap.set('pipes', pipesExpr);
19046 }
19047 if (meta.encapsulation === null) {
19048 meta.encapsulation = ViewEncapsulation.Emulated;
19049 }
19050 // e.g. `styles: [str1, str2]`
19051 if (meta.styles && meta.styles.length) {
19052 const styleValues = meta.encapsulation == ViewEncapsulation.Emulated ?
19053 compileStyles(meta.styles, CONTENT_ATTR, HOST_ATTR) :
19054 meta.styles;
19055 const strings = styleValues.map(str => constantPool.getConstLiteral(literal$1(str)));
19056 definitionMap.set('styles', literalArr(strings));
19057 }
19058 else if (meta.encapsulation === ViewEncapsulation.Emulated) {
19059 // If there is no style, don't generate css selectors on elements
19060 meta.encapsulation = ViewEncapsulation.None;
19061 }
19062 // Only set view encapsulation if it's not the default value
19063 if (meta.encapsulation !== ViewEncapsulation.Emulated) {
19064 definitionMap.set('encapsulation', literal$1(meta.encapsulation));
19065 }
19066 // e.g. `animation: [trigger('123', [])]`
19067 if (meta.animations !== null) {
19068 definitionMap.set('data', literalMap([{ key: 'animation', value: meta.animations, quoted: false }]));
19069 }
19070 // Only set the change detection flag if it's defined and it's not the default.
19071 if (changeDetection != null && changeDetection !== ChangeDetectionStrategy.Default) {
19072 definitionMap.set('changeDetection', literal$1(changeDetection));
19073 }
19074 const expression = importExpr(Identifiers$1.defineComponent).callFn([definitionMap.toLiteralMap()], undefined, true);
19075 const type = createComponentType(meta);
19076 return { expression, type, statements: [] };
19077 }
19078 /**
19079 * Creates the type specification from the component meta. This type is inserted into .d.ts files
19080 * to be consumed by upstream compilations.
19081 */
19082 function createComponentType(meta) {
19083 const typeParams = createDirectiveTypeParams(meta);
19084 typeParams.push(stringArrayAsType(meta.template.ngContentSelectors));
19085 return expressionType(importExpr(Identifiers$1.ComponentDeclaration, typeParams));
19086 }
19087 /**
19088 * Compiles the array literal of declarations into an expression according to the provided emit
19089 * mode.
19090 */
19091 function compileDeclarationList(list, mode) {
19092 switch (mode) {
19093 case 0 /* Direct */:
19094 // directives: [MyDir],
19095 return list;
19096 case 1 /* Closure */:
19097 // directives: function () { return [MyDir]; }
19098 return fn([], [new ReturnStatement(list)]);
19099 case 2 /* ClosureResolved */:
19100 // directives: function () { return [MyDir].map(ng.resolveForwardRef); }
19101 const resolvedList = list.prop('map').callFn([importExpr(Identifiers$1.resolveForwardRef)]);
19102 return fn([], [new ReturnStatement(resolvedList)]);
19103 }
19104 }
19105 function prepareQueryParams(query, constantPool) {
19106 const parameters = [getQueryPredicate(query, constantPool), literal$1(toQueryFlags(query))];
19107 if (query.read) {
19108 parameters.push(query.read);
19109 }
19110 return parameters;
19111 }
19112 /**
19113 * Translates query flags into `TQueryFlags` type in packages/core/src/render3/interfaces/query.ts
19114 * @param query
19115 */
19116 function toQueryFlags(query) {
19117 return (query.descendants ? 1 /* descendants */ : 0 /* none */) |
19118 (query.static ? 2 /* isStatic */ : 0 /* none */) |
19119 (query.emitDistinctChangesOnly ? 4 /* emitDistinctChangesOnly */ : 0 /* none */);
19120 }
19121 function convertAttributesToExpressions(attributes) {
19122 const values = [];
19123 for (let key of Object.getOwnPropertyNames(attributes)) {
19124 const value = attributes[key];
19125 values.push(literal$1(key), value);
19126 }
19127 return values;
19128 }
19129 // Define and update any content queries
19130 function createContentQueriesFunction(queries, constantPool, name) {
19131 const createStatements = [];
19132 const updateStatements = [];
19133 const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
19134 for (const query of queries) {
19135 // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null);
19136 createStatements.push(importExpr(Identifiers$1.contentQuery)
19137 .callFn([variable('dirIndex'), ...prepareQueryParams(query, constantPool)])
19138 .toStmt());
19139 // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
19140 const temporary = tempAllocator();
19141 const getQueryList = importExpr(Identifiers$1.loadQuery).callFn([]);
19142 const refresh = importExpr(Identifiers$1.queryRefresh).callFn([temporary.set(getQueryList)]);
19143 const updateDirective = variable(CONTEXT_NAME)
19144 .prop(query.propertyName)
19145 .set(query.first ? temporary.prop('first') : temporary);
19146 updateStatements.push(refresh.and(updateDirective).toStmt());
19147 }
19148 const contentQueriesFnName = name ? `${name}_ContentQueries` : null;
19149 return fn([
19150 new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null),
19151 new FnParam('dirIndex', null)
19152 ], [
19153 renderFlagCheckIfStmt(1 /* Create */, createStatements),
19154 renderFlagCheckIfStmt(2 /* Update */, updateStatements)
19155 ], INFERRED_TYPE, null, contentQueriesFnName);
19156 }
19157 function stringAsType(str) {
19158 return expressionType(literal$1(str));
19159 }
19160 function stringMapAsType(map) {
19161 const mapValues = Object.keys(map).map(key => {
19162 const value = Array.isArray(map[key]) ? map[key][0] : map[key];
19163 return {
19164 key,
19165 value: literal$1(value),
19166 quoted: true,
19167 };
19168 });
19169 return expressionType(literalMap(mapValues));
19170 }
19171 function stringArrayAsType(arr) {
19172 return arr.length > 0 ? expressionType(literalArr(arr.map(value => literal$1(value)))) :
19173 NONE_TYPE;
19174 }
19175 function createDirectiveTypeParams(meta) {
19176 // On the type side, remove newlines from the selector as it will need to fit into a TypeScript
19177 // string literal, which must be on one line.
19178 const selectorForType = meta.selector !== null ? meta.selector.replace(/\n/g, '') : null;
19179 return [
19180 typeWithParameters(meta.type.type, meta.typeArgumentCount),
19181 selectorForType !== null ? stringAsType(selectorForType) : NONE_TYPE,
19182 meta.exportAs !== null ? stringArrayAsType(meta.exportAs) : NONE_TYPE,
19183 stringMapAsType(meta.inputs),
19184 stringMapAsType(meta.outputs),
19185 stringArrayAsType(meta.queries.map(q => q.propertyName)),
19186 ];
19187 }
19188 /**
19189 * Creates the type specification from the directive meta. This type is inserted into .d.ts files
19190 * to be consumed by upstream compilations.
19191 */
19192 function createDirectiveType(meta) {
19193 const typeParams = createDirectiveTypeParams(meta);
19194 return expressionType(importExpr(Identifiers$1.DirectiveDeclaration, typeParams));
19195 }
19196 // Define and update any view queries
19197 function createViewQueriesFunction(viewQueries, constantPool, name) {
19198 const createStatements = [];
19199 const updateStatements = [];
19200 const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
19201 viewQueries.forEach((query) => {
19202 // creation, e.g. r3.viewQuery(somePredicate, true);
19203 const queryDefinition = importExpr(Identifiers$1.viewQuery).callFn(prepareQueryParams(query, constantPool));
19204 createStatements.push(queryDefinition.toStmt());
19205 // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
19206 const temporary = tempAllocator();
19207 const getQueryList = importExpr(Identifiers$1.loadQuery).callFn([]);
19208 const refresh = importExpr(Identifiers$1.queryRefresh).callFn([temporary.set(getQueryList)]);
19209 const updateDirective = variable(CONTEXT_NAME)
19210 .prop(query.propertyName)
19211 .set(query.first ? temporary.prop('first') : temporary);
19212 updateStatements.push(refresh.and(updateDirective).toStmt());
19213 });
19214 const viewQueryFnName = name ? `${name}_Query` : null;
19215 return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
19216 renderFlagCheckIfStmt(1 /* Create */, createStatements),
19217 renderFlagCheckIfStmt(2 /* Update */, updateStatements)
19218 ], INFERRED_TYPE, null, viewQueryFnName);
19219 }
19220 // Return a host binding function or null if one is not necessary.
19221 function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindingParser, constantPool, selector, name, definitionMap) {
19222 const bindingContext = variable(CONTEXT_NAME);
19223 const styleBuilder = new StylingBuilder(bindingContext);
19224 const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
19225 if (styleAttr !== undefined) {
19226 styleBuilder.registerStyleAttr(styleAttr);
19227 }
19228 if (classAttr !== undefined) {
19229 styleBuilder.registerClassAttr(classAttr);
19230 }
19231 const createStatements = [];
19232 const updateStatements = [];
19233 const hostBindingSourceSpan = typeSourceSpan;
19234 const directiveSummary = metadataAsSummary(hostBindingsMetadata);
19235 // Calculate host event bindings
19236 const eventBindings = bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
19237 if (eventBindings && eventBindings.length) {
19238 const listeners = createHostListeners(eventBindings, name);
19239 createStatements.push(...listeners);
19240 }
19241 // Calculate the host property bindings
19242 const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
19243 const allOtherBindings = [];
19244 // We need to calculate the total amount of binding slots required by
19245 // all the instructions together before any value conversions happen.
19246 // Value conversions may require additional slots for interpolation and
19247 // bindings with pipes. These calculates happen after this block.
19248 let totalHostVarsCount = 0;
19249 bindings && bindings.forEach((binding) => {
19250 const stylingInputWasSet = styleBuilder.registerInputBasedOnName(binding.name, binding.expression, hostBindingSourceSpan);
19251 if (stylingInputWasSet) {
19252 totalHostVarsCount += MIN_STYLING_BINDING_SLOTS_REQUIRED;
19253 }
19254 else {
19255 allOtherBindings.push(binding);
19256 totalHostVarsCount++;
19257 }
19258 });
19259 let valueConverter;
19260 const getValueConverter = () => {
19261 if (!valueConverter) {
19262 const hostVarsCountFn = (numSlots) => {
19263 const originalVarsCount = totalHostVarsCount;
19264 totalHostVarsCount += numSlots;
19265 return originalVarsCount;
19266 };
19267 valueConverter = new ValueConverter(constantPool, () => error('Unexpected node'), // new nodes are illegal here
19268 hostVarsCountFn, () => error('Unexpected pipe')); // pipes are illegal here
19269 }
19270 return valueConverter;
19271 };
19272 const propertyBindings = [];
19273 const attributeBindings = [];
19274 const syntheticHostBindings = [];
19275 allOtherBindings.forEach((binding) => {
19276 // resolve literal arrays and literal objects
19277 const value = binding.expression.visit(getValueConverter());
19278 const bindingExpr = bindingFn(bindingContext, value);
19279 const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
19280 const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
19281 .filter(context => context !== SecurityContext.NONE);
19282 let sanitizerFn = null;
19283 if (securityContexts.length) {
19284 if (securityContexts.length === 2 &&
19285 securityContexts.indexOf(SecurityContext.URL) > -1 &&
19286 securityContexts.indexOf(SecurityContext.RESOURCE_URL) > -1) {
19287 // Special case for some URL attributes (such as "src" and "href") that may be a part
19288 // of different security contexts. In this case we use special sanitization function and
19289 // select the actual sanitizer at runtime based on a tag name that is provided while
19290 // invoking sanitization function.
19291 sanitizerFn = importExpr(Identifiers$1.sanitizeUrlOrResourceUrl);
19292 }
19293 else {
19294 sanitizerFn = resolveSanitizationFn(securityContexts[0], isAttribute);
19295 }
19296 }
19297 const instructionParams = [literal$1(bindingName), bindingExpr.currValExpr];
19298 if (sanitizerFn) {
19299 instructionParams.push(sanitizerFn);
19300 }
19301 updateStatements.push(...bindingExpr.stmts);
19302 if (instruction === Identifiers$1.hostProperty) {
19303 propertyBindings.push(instructionParams);
19304 }
19305 else if (instruction === Identifiers$1.attribute) {
19306 attributeBindings.push(instructionParams);
19307 }
19308 else if (instruction === Identifiers$1.syntheticHostProperty) {
19309 syntheticHostBindings.push(instructionParams);
19310 }
19311 else {
19312 updateStatements.push(importExpr(instruction).callFn(instructionParams).toStmt());
19313 }
19314 });
19315 if (propertyBindings.length > 0) {
19316 updateStatements.push(chainedInstruction(Identifiers$1.hostProperty, propertyBindings).toStmt());
19317 }
19318 if (attributeBindings.length > 0) {
19319 updateStatements.push(chainedInstruction(Identifiers$1.attribute, attributeBindings).toStmt());
19320 }
19321 if (syntheticHostBindings.length > 0) {
19322 updateStatements.push(chainedInstruction(Identifiers$1.syntheticHostProperty, syntheticHostBindings).toStmt());
19323 }
19324 // since we're dealing with directives/components and both have hostBinding
19325 // functions, we need to generate a special hostAttrs instruction that deals
19326 // with both the assignment of styling as well as static attributes to the host
19327 // element. The instruction below will instruct all initial styling (styling
19328 // that is inside of a host binding within a directive/component) to be attached
19329 // to the host element alongside any of the provided host attributes that were
19330 // collected earlier.
19331 const hostAttrs = convertAttributesToExpressions(hostBindingsMetadata.attributes);
19332 styleBuilder.assignHostAttrs(hostAttrs, definitionMap);
19333 if (styleBuilder.hasBindings) {
19334 // finally each binding that was registered in the statement above will need to be added to
19335 // the update block of a component/directive templateFn/hostBindingsFn so that the bindings
19336 // are evaluated and updated for the element.
19337 styleBuilder.buildUpdateLevelInstructions(getValueConverter()).forEach(instruction => {
19338 if (instruction.calls.length > 0) {
19339 const calls = [];
19340 instruction.calls.forEach(call => {
19341 // we subtract a value of `1` here because the binding slot was already allocated
19342 // at the top of this method when all the input bindings were counted.
19343 totalHostVarsCount +=
19344 Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
19345 calls.push(convertStylingCall(call, bindingContext, bindingFn));
19346 });
19347 updateStatements.push(chainedInstruction(instruction.reference, calls).toStmt());
19348 }
19349 });
19350 }
19351 if (totalHostVarsCount) {
19352 definitionMap.set('hostVars', literal$1(totalHostVarsCount));
19353 }
19354 if (createStatements.length > 0 || updateStatements.length > 0) {
19355 const hostBindingsFnName = name ? `${name}_HostBindings` : null;
19356 const statements = [];
19357 if (createStatements.length > 0) {
19358 statements.push(renderFlagCheckIfStmt(1 /* Create */, createStatements));
19359 }
19360 if (updateStatements.length > 0) {
19361 statements.push(renderFlagCheckIfStmt(2 /* Update */, updateStatements));
19362 }
19363 return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], statements, INFERRED_TYPE, null, hostBindingsFnName);
19364 }
19365 return null;
19366 }
19367 function bindingFn(implicit, value) {
19368 return convertPropertyBinding(null, implicit, value, 'b', BindingForm.Expression, () => error('Unexpected interpolation'));
19369 }
19370 function convertStylingCall(call, bindingContext, bindingFn) {
19371 return call.params(value => bindingFn(bindingContext, value).currValExpr);
19372 }
19373 function getBindingNameAndInstruction(binding) {
19374 let bindingName = binding.name;
19375 let instruction;
19376 // Check to see if this is an attr binding or a property binding
19377 const attrMatches = bindingName.match(ATTR_REGEX);
19378 if (attrMatches) {
19379 bindingName = attrMatches[1];
19380 instruction = Identifiers$1.attribute;
19381 }
19382 else {
19383 if (binding.isAnimation) {
19384 bindingName = prepareSyntheticPropertyName(bindingName);
19385 // host bindings that have a synthetic property (e.g. @foo) should always be rendered
19386 // in the context of the component and not the parent. Therefore there is a special
19387 // compatibility instruction available for this purpose.
19388 instruction = Identifiers$1.syntheticHostProperty;
19389 }
19390 else {
19391 instruction = Identifiers$1.hostProperty;
19392 }
19393 }
19394 return { bindingName, instruction, isAttribute: !!attrMatches };
19395 }
19396 function createHostListeners(eventBindings, name) {
19397 const listeners = [];
19398 const syntheticListeners = [];
19399 const instructions = [];
19400 eventBindings.forEach(binding => {
19401 let bindingName = binding.name && sanitizeIdentifier(binding.name);
19402 const bindingFnName = binding.type === 1 /* Animation */ ?
19403 prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase) :
19404 bindingName;
19405 const handlerName = name && bindingName ? `${name}_${bindingFnName}_HostBindingHandler` : null;
19406 const params = prepareEventListenerParameters(BoundEvent.fromParsedEvent(binding), handlerName);
19407 if (binding.type == 1 /* Animation */) {
19408 syntheticListeners.push(params);
19409 }
19410 else {
19411 listeners.push(params);
19412 }
19413 });
19414 if (syntheticListeners.length > 0) {
19415 instructions.push(chainedInstruction(Identifiers$1.syntheticHostListener, syntheticListeners).toStmt());
19416 }
19417 if (listeners.length > 0) {
19418 instructions.push(chainedInstruction(Identifiers$1.listener, listeners).toStmt());
19419 }
19420 return instructions;
19421 }
19422 function metadataAsSummary(meta) {
19423 // clang-format off
19424 return {
19425 // This is used by the BindingParser, which only deals with listeners and properties. There's no
19426 // need to pass attributes to it.
19427 hostAttributes: {},
19428 hostListeners: meta.listeners,
19429 hostProperties: meta.properties,
19430 };
19431 // clang-format on
19432 }
19433 const HOST_REG_EXP = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/;
19434 function parseHostBindings(host) {
19435 const attributes = {};
19436 const listeners = {};
19437 const properties = {};
19438 const specialAttributes = {};
19439 for (const key of Object.keys(host)) {
19440 const value = host[key];
19441 const matches = key.match(HOST_REG_EXP);
19442 if (matches === null) {
19443 switch (key) {
19444 case 'class':
19445 if (typeof value !== 'string') {
19446 // TODO(alxhub): make this a diagnostic.
19447 throw new Error(`Class binding must be string`);
19448 }
19449 specialAttributes.classAttr = value;
19450 break;
19451 case 'style':
19452 if (typeof value !== 'string') {
19453 // TODO(alxhub): make this a diagnostic.
19454 throw new Error(`Style binding must be string`);
19455 }
19456 specialAttributes.styleAttr = value;
19457 break;
19458 default:
19459 if (typeof value === 'string') {
19460 attributes[key] = literal$1(value);
19461 }
19462 else {
19463 attributes[key] = value;
19464 }
19465 }
19466 }
19467 else if (matches[1 /* Binding */] != null) {
19468 if (typeof value !== 'string') {
19469 // TODO(alxhub): make this a diagnostic.
19470 throw new Error(`Property binding must be string`);
19471 }
19472 // synthetic properties (the ones that have a `@` as a prefix)
19473 // are still treated the same as regular properties. Therefore
19474 // there is no point in storing them in a separate map.
19475 properties[matches[1 /* Binding */]] = value;
19476 }
19477 else if (matches[2 /* Event */] != null) {
19478 if (typeof value !== 'string') {
19479 // TODO(alxhub): make this a diagnostic.
19480 throw new Error(`Event binding must be string`);
19481 }
19482 listeners[matches[2 /* Event */]] = value;
19483 }
19484 }
19485 return { attributes, listeners, properties, specialAttributes };
19486 }
19487 /**
19488 * Verifies host bindings and returns the list of errors (if any). Empty array indicates that a
19489 * given set of host bindings has no errors.
19490 *
19491 * @param bindings set of host bindings to verify.
19492 * @param sourceSpan source span where host bindings were defined.
19493 * @returns array of errors associated with a given set of host bindings.
19494 */
19495 function verifyHostBindings(bindings, sourceSpan) {
19496 const summary = metadataAsSummary(bindings);
19497 // TODO: abstract out host bindings verification logic and use it instead of
19498 // creating events and properties ASTs to detect errors (FW-996)
19499 const bindingParser = makeBindingParser();
19500 bindingParser.createDirectiveHostEventAsts(summary, sourceSpan);
19501 bindingParser.createBoundHostProperties(summary, sourceSpan);
19502 return bindingParser.errors;
19503 }
19504 function compileStyles(styles, selector, hostSelector) {
19505 const shadowCss = new ShadowCss();
19506 return styles.map(style => {
19507 return shadowCss.shimCssText(style, selector, hostSelector);
19508 });
19509 }
19510
19511 /**
19512 * @license
19513 * Copyright Google LLC All Rights Reserved.
19514 *
19515 * Use of this source code is governed by an MIT-style license that can be
19516 * found in the LICENSE file at https://angular.io/license
19517 */
19518 /**
19519 * An interface for retrieving documents by URL that the compiler uses to
19520 * load templates.
19521 *
19522 * This is an abstract class, rather than an interface, so that it can be used
19523 * as injection token.
19524 */
19525 class ResourceLoader {
19526 }
19527
19528 /**
19529 * @license
19530 * Copyright Google LLC All Rights Reserved.
19531 *
19532 * Use of this source code is governed by an MIT-style license that can be
19533 * found in the LICENSE file at https://angular.io/license
19534 */
19535 class CompilerFacadeImpl {
19536 constructor(jitEvaluator = new JitEvaluator()) {
19537 this.jitEvaluator = jitEvaluator;
19538 this.FactoryTarget = FactoryTarget$1;
19539 this.ResourceLoader = ResourceLoader;
19540 this.elementSchemaRegistry = new DomElementSchemaRegistry();
19541 }
19542 compilePipe(angularCoreEnv, sourceMapUrl, facade) {
19543 const metadata = {
19544 name: facade.name,
19545 type: wrapReference(facade.type),
19546 internalType: new WrappedNodeExpr(facade.type),
19547 typeArgumentCount: 0,
19548 deps: null,
19549 pipeName: facade.pipeName,
19550 pure: facade.pure,
19551 };
19552 const res = compilePipeFromMetadata(metadata);
19553 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
19554 }
19555 compilePipeDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
19556 const meta = convertDeclarePipeFacadeToMetadata(declaration);
19557 const res = compilePipeFromMetadata(meta);
19558 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
19559 }
19560 compileInjectable(angularCoreEnv, sourceMapUrl, facade) {
19561 const { expression, statements } = compileInjectable({
19562 name: facade.name,
19563 type: wrapReference(facade.type),
19564 internalType: new WrappedNodeExpr(facade.type),
19565 typeArgumentCount: facade.typeArgumentCount,
19566 providedIn: computeProvidedIn(facade.providedIn),
19567 useClass: convertToProviderExpression(facade, USE_CLASS),
19568 useFactory: wrapExpression(facade, USE_FACTORY),
19569 useValue: convertToProviderExpression(facade, USE_VALUE),
19570 useExisting: convertToProviderExpression(facade, USE_EXISTING),
19571 deps: facade.deps?.map(convertR3DependencyMetadata),
19572 },
19573 /* resolveForwardRefs */ true);
19574 return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements);
19575 }
19576 compileInjectableDeclaration(angularCoreEnv, sourceMapUrl, facade) {
19577 const { expression, statements } = compileInjectable({
19578 name: facade.type.name,
19579 type: wrapReference(facade.type),
19580 internalType: new WrappedNodeExpr(facade.type),
19581 typeArgumentCount: 0,
19582 providedIn: computeProvidedIn(facade.providedIn),
19583 useClass: convertToProviderExpression(facade, USE_CLASS),
19584 useFactory: wrapExpression(facade, USE_FACTORY),
19585 useValue: convertToProviderExpression(facade, USE_VALUE),
19586 useExisting: convertToProviderExpression(facade, USE_EXISTING),
19587 deps: facade.deps?.map(convertR3DeclareDependencyMetadata),
19588 },
19589 /* resolveForwardRefs */ true);
19590 return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements);
19591 }
19592 compileInjector(angularCoreEnv, sourceMapUrl, facade) {
19593 const meta = {
19594 name: facade.name,
19595 type: wrapReference(facade.type),
19596 internalType: new WrappedNodeExpr(facade.type),
19597 providers: new WrappedNodeExpr(facade.providers),
19598 imports: facade.imports.map(i => new WrappedNodeExpr(i)),
19599 };
19600 const res = compileInjector(meta);
19601 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
19602 }
19603 compileInjectorDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
19604 const meta = convertDeclareInjectorFacadeToMetadata(declaration);
19605 const res = compileInjector(meta);
19606 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
19607 }
19608 compileNgModule(angularCoreEnv, sourceMapUrl, facade) {
19609 const meta = {
19610 type: wrapReference(facade.type),
19611 internalType: new WrappedNodeExpr(facade.type),
19612 adjacentType: new WrappedNodeExpr(facade.type),
19613 bootstrap: facade.bootstrap.map(wrapReference),
19614 declarations: facade.declarations.map(wrapReference),
19615 imports: facade.imports.map(wrapReference),
19616 exports: facade.exports.map(wrapReference),
19617 emitInline: true,
19618 containsForwardDecls: false,
19619 schemas: facade.schemas ? facade.schemas.map(wrapReference) : null,
19620 id: facade.id ? new WrappedNodeExpr(facade.id) : null,
19621 };
19622 const res = compileNgModule(meta);
19623 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
19624 }
19625 compileNgModuleDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
19626 const expression = compileNgModuleDeclarationExpression(declaration);
19627 return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, []);
19628 }
19629 compileDirective(angularCoreEnv, sourceMapUrl, facade) {
19630 const meta = convertDirectiveFacadeToMetadata(facade);
19631 return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
19632 }
19633 compileDirectiveDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
19634 const typeSourceSpan = this.createParseSourceSpan('Directive', declaration.type.name, sourceMapUrl);
19635 const meta = convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan);
19636 return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
19637 }
19638 compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta) {
19639 const constantPool = new ConstantPool();
19640 const bindingParser = makeBindingParser();
19641 const res = compileDirectiveFromMetadata(meta, constantPool, bindingParser);
19642 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
19643 }
19644 compileComponent(angularCoreEnv, sourceMapUrl, facade) {
19645 // Parse the template and check for errors.
19646 const { template, interpolation } = parseJitTemplate(facade.template, facade.name, sourceMapUrl, facade.preserveWhitespaces, facade.interpolation);
19647 // Compile the component metadata, including template, into an expression.
19648 const meta = {
19649 ...facade,
19650 ...convertDirectiveFacadeToMetadata(facade),
19651 selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(),
19652 template,
19653 declarationListEmitMode: 0 /* Direct */,
19654 styles: [...facade.styles, ...template.styles],
19655 encapsulation: facade.encapsulation,
19656 interpolation,
19657 changeDetection: facade.changeDetection,
19658 animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null,
19659 viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) :
19660 null,
19661 relativeContextFilePath: '',
19662 i18nUseExternalIds: true,
19663 };
19664 const jitExpressionSourceMap = `ng:///${facade.name}.js`;
19665 return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
19666 }
19667 compileComponentDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
19668 const typeSourceSpan = this.createParseSourceSpan('Component', declaration.type.name, sourceMapUrl);
19669 const meta = convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl);
19670 return this.compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta);
19671 }
19672 compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta) {
19673 const constantPool = new ConstantPool();
19674 const bindingParser = makeBindingParser(meta.interpolation);
19675 const res = compileComponentFromMetadata(meta, constantPool, bindingParser);
19676 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
19677 }
19678 compileFactory(angularCoreEnv, sourceMapUrl, meta) {
19679 const factoryRes = compileFactoryFunction({
19680 name: meta.name,
19681 type: wrapReference(meta.type),
19682 internalType: new WrappedNodeExpr(meta.type),
19683 typeArgumentCount: meta.typeArgumentCount,
19684 deps: convertR3DependencyMetadataArray(meta.deps),
19685 target: meta.target,
19686 });
19687 return this.jitExpression(factoryRes.expression, angularCoreEnv, sourceMapUrl, factoryRes.statements);
19688 }
19689 compileFactoryDeclaration(angularCoreEnv, sourceMapUrl, meta) {
19690 const factoryRes = compileFactoryFunction({
19691 name: meta.type.name,
19692 type: wrapReference(meta.type),
19693 internalType: new WrappedNodeExpr(meta.type),
19694 typeArgumentCount: 0,
19695 deps: Array.isArray(meta.deps) ? meta.deps.map(convertR3DeclareDependencyMetadata) :
19696 meta.deps,
19697 target: meta.target,
19698 });
19699 return this.jitExpression(factoryRes.expression, angularCoreEnv, sourceMapUrl, factoryRes.statements);
19700 }
19701 createParseSourceSpan(kind, typeName, sourceUrl) {
19702 return r3JitTypeSourceSpan(kind, typeName, sourceUrl);
19703 }
19704 /**
19705 * JIT compiles an expression and returns the result of executing that expression.
19706 *
19707 * @param def the definition which will be compiled and executed to get the value to patch
19708 * @param context an object map of @angular/core symbol names to symbols which will be available
19709 * in the context of the compiled expression
19710 * @param sourceUrl a URL to use for the source map of the compiled expression
19711 * @param preStatements a collection of statements that should be evaluated before the expression.
19712 */
19713 jitExpression(def, context, sourceUrl, preStatements) {
19714 // The ConstantPool may contain Statements which declare variables used in the final expression.
19715 // Therefore, its statements need to precede the actual JIT operation. The final statement is a
19716 // declaration of $def which is set to the expression being compiled.
19717 const statements = [
19718 ...preStatements,
19719 new DeclareVarStmt('$def', def, undefined, [StmtModifier.Exported]),
19720 ];
19721 const res = this.jitEvaluator.evaluateStatements(sourceUrl, statements, new R3JitReflector(context), /* enableSourceMaps */ true);
19722 return res['$def'];
19723 }
19724 }
19725 const USE_CLASS = Object.keys({ useClass: null })[0];
19726 const USE_FACTORY = Object.keys({ useFactory: null })[0];
19727 const USE_VALUE = Object.keys({ useValue: null })[0];
19728 const USE_EXISTING = Object.keys({ useExisting: null })[0];
19729 function convertToR3QueryMetadata(facade) {
19730 return {
19731 ...facade,
19732 predicate: convertQueryPredicate(facade.predicate),
19733 read: facade.read ? new WrappedNodeExpr(facade.read) : null,
19734 static: facade.static,
19735 emitDistinctChangesOnly: facade.emitDistinctChangesOnly,
19736 };
19737 }
19738 function convertQueryDeclarationToMetadata(declaration) {
19739 return {
19740 propertyName: declaration.propertyName,
19741 first: declaration.first ?? false,
19742 predicate: convertQueryPredicate(declaration.predicate),
19743 descendants: declaration.descendants ?? false,
19744 read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
19745 static: declaration.static ?? false,
19746 emitDistinctChangesOnly: declaration.emitDistinctChangesOnly ?? true,
19747 };
19748 }
19749 function convertQueryPredicate(predicate) {
19750 return Array.isArray(predicate) ?
19751 // The predicate is an array of strings so pass it through.
19752 predicate :
19753 // The predicate is a type - assume that we will need to unwrap any `forwardRef()` calls.
19754 createMayBeForwardRefExpression(new WrappedNodeExpr(predicate), 1 /* Wrapped */);
19755 }
19756 function convertDirectiveFacadeToMetadata(facade) {
19757 const inputsFromMetadata = parseInputOutputs(facade.inputs || []);
19758 const outputsFromMetadata = parseInputOutputs(facade.outputs || []);
19759 const propMetadata = facade.propMetadata;
19760 const inputsFromType = {};
19761 const outputsFromType = {};
19762 for (const field in propMetadata) {
19763 if (propMetadata.hasOwnProperty(field)) {
19764 propMetadata[field].forEach(ann => {
19765 if (isInput(ann)) {
19766 inputsFromType[field] =
19767 ann.bindingPropertyName ? [ann.bindingPropertyName, field] : field;
19768 }
19769 else if (isOutput(ann)) {
19770 outputsFromType[field] = ann.bindingPropertyName || field;
19771 }
19772 });
19773 }
19774 }
19775 return {
19776 ...facade,
19777 typeArgumentCount: 0,
19778 typeSourceSpan: facade.typeSourceSpan,
19779 type: wrapReference(facade.type),
19780 internalType: new WrappedNodeExpr(facade.type),
19781 deps: null,
19782 host: extractHostBindings$1(facade.propMetadata, facade.typeSourceSpan, facade.host),
19783 inputs: { ...inputsFromMetadata, ...inputsFromType },
19784 outputs: { ...outputsFromMetadata, ...outputsFromType },
19785 queries: facade.queries.map(convertToR3QueryMetadata),
19786 providers: facade.providers != null ? new WrappedNodeExpr(facade.providers) : null,
19787 viewQueries: facade.viewQueries.map(convertToR3QueryMetadata),
19788 fullInheritance: false,
19789 };
19790 }
19791 function convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan) {
19792 return {
19793 name: declaration.type.name,
19794 type: wrapReference(declaration.type),
19795 typeSourceSpan,
19796 internalType: new WrappedNodeExpr(declaration.type),
19797 selector: declaration.selector ?? null,
19798 inputs: declaration.inputs ?? {},
19799 outputs: declaration.outputs ?? {},
19800 host: convertHostDeclarationToMetadata(declaration.host),
19801 queries: (declaration.queries ?? []).map(convertQueryDeclarationToMetadata),
19802 viewQueries: (declaration.viewQueries ?? []).map(convertQueryDeclarationToMetadata),
19803 providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) :
19804 null,
19805 exportAs: declaration.exportAs ?? null,
19806 usesInheritance: declaration.usesInheritance ?? false,
19807 lifecycle: { usesOnChanges: declaration.usesOnChanges ?? false },
19808 deps: null,
19809 typeArgumentCount: 0,
19810 fullInheritance: false,
19811 };
19812 }
19813 function convertHostDeclarationToMetadata(host = {}) {
19814 return {
19815 attributes: convertOpaqueValuesToExpressions(host.attributes ?? {}),
19816 listeners: host.listeners ?? {},
19817 properties: host.properties ?? {},
19818 specialAttributes: {
19819 classAttr: host.classAttribute,
19820 styleAttr: host.styleAttribute,
19821 },
19822 };
19823 }
19824 function convertOpaqueValuesToExpressions(obj) {
19825 const result = {};
19826 for (const key of Object.keys(obj)) {
19827 result[key] = new WrappedNodeExpr(obj[key]);
19828 }
19829 return result;
19830 }
19831 function convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl) {
19832 const { template, interpolation } = parseJitTemplate(declaration.template, declaration.type.name, sourceMapUrl, declaration.preserveWhitespaces ?? false, declaration.interpolation);
19833 return {
19834 ...convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan),
19835 template,
19836 styles: declaration.styles ?? [],
19837 directives: (declaration.components ?? [])
19838 .concat(declaration.directives ?? [])
19839 .map(convertUsedDirectiveDeclarationToMetadata),
19840 pipes: convertUsedPipesToMetadata(declaration.pipes),
19841 viewProviders: declaration.viewProviders !== undefined ?
19842 new WrappedNodeExpr(declaration.viewProviders) :
19843 null,
19844 animations: declaration.animations !== undefined ? new WrappedNodeExpr(declaration.animations) :
19845 null,
19846 changeDetection: declaration.changeDetection ?? ChangeDetectionStrategy.Default,
19847 encapsulation: declaration.encapsulation ?? ViewEncapsulation.Emulated,
19848 interpolation,
19849 declarationListEmitMode: 2 /* ClosureResolved */,
19850 relativeContextFilePath: '',
19851 i18nUseExternalIds: true,
19852 };
19853 }
19854 function convertUsedDirectiveDeclarationToMetadata(declaration) {
19855 return {
19856 selector: declaration.selector,
19857 type: new WrappedNodeExpr(declaration.type),
19858 inputs: declaration.inputs ?? [],
19859 outputs: declaration.outputs ?? [],
19860 exportAs: declaration.exportAs ?? null,
19861 };
19862 }
19863 function convertUsedPipesToMetadata(declaredPipes) {
19864 const pipes = new Map();
19865 if (declaredPipes === undefined) {
19866 return pipes;
19867 }
19868 for (const pipeName of Object.keys(declaredPipes)) {
19869 const pipeType = declaredPipes[pipeName];
19870 pipes.set(pipeName, new WrappedNodeExpr(pipeType));
19871 }
19872 return pipes;
19873 }
19874 function parseJitTemplate(template, typeName, sourceMapUrl, preserveWhitespaces, interpolation) {
19875 const interpolationConfig = interpolation ? InterpolationConfig.fromArray(interpolation) : DEFAULT_INTERPOLATION_CONFIG;
19876 // Parse the template and check for errors.
19877 const parsed = parseTemplate(template, sourceMapUrl, { preserveWhitespaces, interpolationConfig });
19878 if (parsed.errors !== null) {
19879 const errors = parsed.errors.map(err => err.toString()).join(', ');
19880 throw new Error(`Errors during JIT compilation of template for ${typeName}: ${errors}`);
19881 }
19882 return { template: parsed, interpolation: interpolationConfig };
19883 }
19884 /**
19885 * Convert the expression, if present to an `R3ProviderExpression`.
19886 *
19887 * In JIT mode we do not want the compiler to wrap the expression in a `forwardRef()` call because,
19888 * if it is referencing a type that has not yet been defined, it will have already been wrapped in
19889 * a `forwardRef()` - either by the application developer or during partial-compilation. Thus we can
19890 * use `ForwardRefHandling.None`.
19891 */
19892 function convertToProviderExpression(obj, property) {
19893 if (obj.hasOwnProperty(property)) {
19894 return createMayBeForwardRefExpression(new WrappedNodeExpr(obj[property]), 0 /* None */);
19895 }
19896 else {
19897 return undefined;
19898 }
19899 }
19900 function wrapExpression(obj, property) {
19901 if (obj.hasOwnProperty(property)) {
19902 return new WrappedNodeExpr(obj[property]);
19903 }
19904 else {
19905 return undefined;
19906 }
19907 }
19908 function computeProvidedIn(providedIn) {
19909 const expression = typeof providedIn === 'function' ? new WrappedNodeExpr(providedIn) :
19910 new LiteralExpr(providedIn ?? null);
19911 // See `convertToProviderExpression()` for why this uses `ForwardRefHandling.None`.
19912 return createMayBeForwardRefExpression(expression, 0 /* None */);
19913 }
19914 function convertR3DependencyMetadataArray(facades) {
19915 return facades == null ? null : facades.map(convertR3DependencyMetadata);
19916 }
19917 function convertR3DependencyMetadata(facade) {
19918 const isAttributeDep = facade.attribute != null; // both `null` and `undefined`
19919 const rawToken = facade.token === null ? null : new WrappedNodeExpr(facade.token);
19920 // In JIT mode, if the dep is an `@Attribute()` then we use the attribute name given in
19921 // `attribute` rather than the `token`.
19922 const token = isAttributeDep ? new WrappedNodeExpr(facade.attribute) : rawToken;
19923 return createR3DependencyMetadata(token, isAttributeDep, facade.host, facade.optional, facade.self, facade.skipSelf);
19924 }
19925 function convertR3DeclareDependencyMetadata(facade) {
19926 const isAttributeDep = facade.attribute ?? false;
19927 const token = facade.token === null ? null : new WrappedNodeExpr(facade.token);
19928 return createR3DependencyMetadata(token, isAttributeDep, facade.host ?? false, facade.optional ?? false, facade.self ?? false, facade.skipSelf ?? false);
19929 }
19930 function createR3DependencyMetadata(token, isAttributeDep, host, optional, self, skipSelf) {
19931 // If the dep is an `@Attribute()` the `attributeNameType` ought to be the `unknown` type.
19932 // But types are not available at runtime so we just use a literal `"<unknown>"` string as a dummy
19933 // marker.
19934 const attributeNameType = isAttributeDep ? literal$1('unknown') : null;
19935 return { token, attributeNameType, host, optional, self, skipSelf };
19936 }
19937 function extractHostBindings$1(propMetadata, sourceSpan, host) {
19938 // First parse the declarations from the metadata.
19939 const bindings = parseHostBindings(host || {});
19940 // After that check host bindings for errors
19941 const errors = verifyHostBindings(bindings, sourceSpan);
19942 if (errors.length) {
19943 throw new Error(errors.map((error) => error.msg).join('\n'));
19944 }
19945 // Next, loop over the properties of the object, looking for @HostBinding and @HostListener.
19946 for (const field in propMetadata) {
19947 if (propMetadata.hasOwnProperty(field)) {
19948 propMetadata[field].forEach(ann => {
19949 if (isHostBinding(ann)) {
19950 // Since this is a decorator, we know that the value is a class member. Always access it
19951 // through `this` so that further down the line it can't be confused for a literal value
19952 // (e.g. if there's a property called `true`).
19953 bindings.properties[ann.hostPropertyName || field] =
19954 getSafePropertyAccessString('this', field);
19955 }
19956 else if (isHostListener(ann)) {
19957 bindings.listeners[ann.eventName || field] = `${field}(${(ann.args || []).join(',')})`;
19958 }
19959 });
19960 }
19961 }
19962 return bindings;
19963 }
19964 function isHostBinding(value) {
19965 return value.ngMetadataName === 'HostBinding';
19966 }
19967 function isHostListener(value) {
19968 return value.ngMetadataName === 'HostListener';
19969 }
19970 function isInput(value) {
19971 return value.ngMetadataName === 'Input';
19972 }
19973 function isOutput(value) {
19974 return value.ngMetadataName === 'Output';
19975 }
19976 function parseInputOutputs(values) {
19977 return values.reduce((map, value) => {
19978 const [field, property] = value.split(',').map(piece => piece.trim());
19979 map[field] = property || field;
19980 return map;
19981 }, {});
19982 }
19983 function convertDeclarePipeFacadeToMetadata(declaration) {
19984 return {
19985 name: declaration.type.name,
19986 type: wrapReference(declaration.type),
19987 internalType: new WrappedNodeExpr(declaration.type),
19988 typeArgumentCount: 0,
19989 pipeName: declaration.name,
19990 deps: null,
19991 pure: declaration.pure ?? true,
19992 };
19993 }
19994 function convertDeclareInjectorFacadeToMetadata(declaration) {
19995 return {
19996 name: declaration.type.name,
19997 type: wrapReference(declaration.type),
19998 internalType: new WrappedNodeExpr(declaration.type),
19999 providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) :
20000 null,
20001 imports: declaration.imports !== undefined ?
20002 declaration.imports.map(i => new WrappedNodeExpr(i)) :
20003 [],
20004 };
20005 }
20006 function publishFacade(global) {
20007 const ng = global.ng || (global.ng = {});
20008 ng.ɵcompilerFacade = new CompilerFacadeImpl();
20009 }
20010
20011 /**
20012 * @license
20013 * Copyright Google LLC All Rights Reserved.
20014 *
20015 * Use of this source code is governed by an MIT-style license that can be
20016 * found in the LICENSE file at https://angular.io/license
20017 */
20018 new Version('13.0.2');
20019
20020 /**
20021 * @license
20022 * Copyright Google LLC All Rights Reserved.
20023 *
20024 * Use of this source code is governed by an MIT-style license that can be
20025 * found in the LICENSE file at https://angular.io/license
20026 */
20027 var _VisitorMode;
20028 (function (_VisitorMode) {
20029 _VisitorMode[_VisitorMode["Extract"] = 0] = "Extract";
20030 _VisitorMode[_VisitorMode["Merge"] = 1] = "Merge";
20031 })(_VisitorMode || (_VisitorMode = {}));
20032
20033 /**
20034 * @license
20035 * Copyright Google LLC All Rights Reserved.
20036 *
20037 * Use of this source code is governed by an MIT-style license that can be
20038 * found in the LICENSE file at https://angular.io/license
20039 */
20040 var LifecycleHooks;
20041 (function (LifecycleHooks) {
20042 LifecycleHooks[LifecycleHooks["OnInit"] = 0] = "OnInit";
20043 LifecycleHooks[LifecycleHooks["OnDestroy"] = 1] = "OnDestroy";
20044 LifecycleHooks[LifecycleHooks["DoCheck"] = 2] = "DoCheck";
20045 LifecycleHooks[LifecycleHooks["OnChanges"] = 3] = "OnChanges";
20046 LifecycleHooks[LifecycleHooks["AfterContentInit"] = 4] = "AfterContentInit";
20047 LifecycleHooks[LifecycleHooks["AfterContentChecked"] = 5] = "AfterContentChecked";
20048 LifecycleHooks[LifecycleHooks["AfterViewInit"] = 6] = "AfterViewInit";
20049 LifecycleHooks[LifecycleHooks["AfterViewChecked"] = 7] = "AfterViewChecked";
20050 })(LifecycleHooks || (LifecycleHooks = {}));
20051 [
20052 LifecycleHooks.OnInit, LifecycleHooks.OnDestroy, LifecycleHooks.DoCheck, LifecycleHooks.OnChanges,
20053 LifecycleHooks.AfterContentInit, LifecycleHooks.AfterContentChecked, LifecycleHooks.AfterViewInit,
20054 LifecycleHooks.AfterViewChecked
20055 ];
20056
20057 /**
20058 * @license
20059 * Copyright Google LLC All Rights Reserved.
20060 *
20061 * Use of this source code is governed by an MIT-style license that can be
20062 * found in the LICENSE file at https://angular.io/license
20063 */
20064 variable('_l');
20065
20066 /**
20067 * @license
20068 * Copyright Google LLC All Rights Reserved.
20069 *
20070 * Use of this source code is governed by an MIT-style license that can be
20071 * found in the LICENSE file at https://angular.io/license
20072 */
20073 variable('_l');
20074 variable('_v');
20075 variable('_ck');
20076 variable('_co');
20077 variable('en');
20078 variable(`ad`);
20079
20080 /**
20081 * @license
20082 * Copyright Google LLC All Rights Reserved.
20083 *
20084 * Use of this source code is governed by an MIT-style license that can be
20085 * found in the LICENSE file at https://angular.io/license
20086 */
20087 /**
20088 * The index of each URI component in the return value of goog.uri.utils.split.
20089 * @enum {number}
20090 */
20091 var _ComponentIndex;
20092 (function (_ComponentIndex) {
20093 _ComponentIndex[_ComponentIndex["Scheme"] = 1] = "Scheme";
20094 _ComponentIndex[_ComponentIndex["UserInfo"] = 2] = "UserInfo";
20095 _ComponentIndex[_ComponentIndex["Domain"] = 3] = "Domain";
20096 _ComponentIndex[_ComponentIndex["Port"] = 4] = "Port";
20097 _ComponentIndex[_ComponentIndex["Path"] = 5] = "Path";
20098 _ComponentIndex[_ComponentIndex["QueryData"] = 6] = "QueryData";
20099 _ComponentIndex[_ComponentIndex["Fragment"] = 7] = "Fragment";
20100 })(_ComponentIndex || (_ComponentIndex = {}));
20101
20102 var FactoryTarget;
20103 (function (FactoryTarget) {
20104 FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive";
20105 FactoryTarget[FactoryTarget["Component"] = 1] = "Component";
20106 FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable";
20107 FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe";
20108 FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule";
20109 })(FactoryTarget || (FactoryTarget = {}));
20110
20111 /**
20112 * @license
20113 * Copyright Google LLC All Rights Reserved.
20114 *
20115 * Use of this source code is governed by an MIT-style license that can be
20116 * found in the LICENSE file at https://angular.io/license
20117 */
20118 /**
20119 * Processes `Target`s with a given set of directives and performs a binding operation, which
20120 * returns an object similar to TypeScript's `ts.TypeChecker` that contains knowledge about the
20121 * target.
20122 */
20123 class R3TargetBinder {
20124 constructor(directiveMatcher) {
20125 this.directiveMatcher = directiveMatcher;
20126 }
20127 /**
20128 * Perform a binding operation on the given `Target` and return a `BoundTarget` which contains
20129 * metadata about the types referenced in the template.
20130 */
20131 bind(target) {
20132 if (!target.template) {
20133 // TODO(alxhub): handle targets which contain things like HostBindings, etc.
20134 throw new Error('Binding without a template not yet supported');
20135 }
20136 // First, parse the template into a `Scope` structure. This operation captures the syntactic
20137 // scopes in the template and makes them available for later use.
20138 const scope = Scope$1.apply(target.template);
20139 // Use the `Scope` to extract the entities present at every level of the template.
20140 const templateEntities = extractTemplateEntities(scope);
20141 // Next, perform directive matching on the template using the `DirectiveBinder`. This returns:
20142 // - directives: Map of nodes (elements & ng-templates) to the directives on them.
20143 // - bindings: Map of inputs, outputs, and attributes to the directive/element that claims
20144 // them. TODO(alxhub): handle multiple directives claiming an input/output/etc.
20145 // - references: Map of #references to their targets.
20146 const { directives, bindings, references } = DirectiveBinder.apply(target.template, this.directiveMatcher);
20147 // Finally, run the TemplateBinder to bind references, variables, and other entities within the
20148 // template. This extracts all the metadata that doesn't depend on directive matching.
20149 const { expressions, symbols, nestingLevel, usedPipes } = TemplateBinder.applyWithScope(target.template, scope);
20150 return new R3BoundTarget(target, directives, bindings, references, expressions, symbols, nestingLevel, templateEntities, usedPipes);
20151 }
20152 }
20153 /**
20154 * Represents a binding scope within a template.
20155 *
20156 * Any variables, references, or other named entities declared within the template will
20157 * be captured and available by name in `namedEntities`. Additionally, child templates will
20158 * be analyzed and have their child `Scope`s available in `childScopes`.
20159 */
20160 class Scope$1 {
20161 constructor(parentScope, template) {
20162 this.parentScope = parentScope;
20163 this.template = template;
20164 /**
20165 * Named members of the `Scope`, such as `Reference`s or `Variable`s.
20166 */
20167 this.namedEntities = new Map();
20168 /**
20169 * Child `Scope`s for immediately nested `Template`s.
20170 */
20171 this.childScopes = new Map();
20172 }
20173 static newRootScope() {
20174 return new Scope$1(null, null);
20175 }
20176 /**
20177 * Process a template (either as a `Template` sub-template with variables, or a plain array of
20178 * template `Node`s) and construct its `Scope`.
20179 */
20180 static apply(template) {
20181 const scope = Scope$1.newRootScope();
20182 scope.ingest(template);
20183 return scope;
20184 }
20185 /**
20186 * Internal method to process the template and populate the `Scope`.
20187 */
20188 ingest(template) {
20189 if (template instanceof Template) {
20190 // Variables on an <ng-template> are defined in the inner scope.
20191 template.variables.forEach(node => this.visitVariable(node));
20192 // Process the nodes of the template.
20193 template.children.forEach(node => node.visit(this));
20194 }
20195 else {
20196 // No overarching `Template` instance, so process the nodes directly.
20197 template.forEach(node => node.visit(this));
20198 }
20199 }
20200 visitElement(element) {
20201 // `Element`s in the template may have `Reference`s which are captured in the scope.
20202 element.references.forEach(node => this.visitReference(node));
20203 // Recurse into the `Element`'s children.
20204 element.children.forEach(node => node.visit(this));
20205 }
20206 visitTemplate(template) {
20207 // References on a <ng-template> are defined in the outer scope, so capture them before
20208 // processing the template's child scope.
20209 template.references.forEach(node => this.visitReference(node));
20210 // Next, create an inner scope and process the template within it.
20211 const scope = new Scope$1(this, template);
20212 scope.ingest(template);
20213 this.childScopes.set(template, scope);
20214 }
20215 visitVariable(variable) {
20216 // Declare the variable if it's not already.
20217 this.maybeDeclare(variable);
20218 }
20219 visitReference(reference) {
20220 // Declare the variable if it's not already.
20221 this.maybeDeclare(reference);
20222 }
20223 // Unused visitors.
20224 visitContent(content) { }
20225 visitBoundAttribute(attr) { }
20226 visitBoundEvent(event) { }
20227 visitBoundText(text) { }
20228 visitText(text) { }
20229 visitTextAttribute(attr) { }
20230 visitIcu(icu) { }
20231 maybeDeclare(thing) {
20232 // Declare something with a name, as long as that name isn't taken.
20233 if (!this.namedEntities.has(thing.name)) {
20234 this.namedEntities.set(thing.name, thing);
20235 }
20236 }
20237 /**
20238 * Look up a variable within this `Scope`.
20239 *
20240 * This can recurse into a parent `Scope` if it's available.
20241 */
20242 lookup(name) {
20243 if (this.namedEntities.has(name)) {
20244 // Found in the local scope.
20245 return this.namedEntities.get(name);
20246 }
20247 else if (this.parentScope !== null) {
20248 // Not in the local scope, but there's a parent scope so check there.
20249 return this.parentScope.lookup(name);
20250 }
20251 else {
20252 // At the top level and it wasn't found.
20253 return null;
20254 }
20255 }
20256 /**
20257 * Get the child scope for a `Template`.
20258 *
20259 * This should always be defined.
20260 */
20261 getChildScope(template) {
20262 const res = this.childScopes.get(template);
20263 if (res === undefined) {
20264 throw new Error(`Assertion error: child scope for ${template} not found`);
20265 }
20266 return res;
20267 }
20268 }
20269 /**
20270 * Processes a template and matches directives on nodes (elements and templates).
20271 *
20272 * Usually used via the static `apply()` method.
20273 */
20274 class DirectiveBinder {
20275 constructor(matcher, directives, bindings, references) {
20276 this.matcher = matcher;
20277 this.directives = directives;
20278 this.bindings = bindings;
20279 this.references = references;
20280 }
20281 /**
20282 * Process a template (list of `Node`s) and perform directive matching against each node.
20283 *
20284 * @param template the list of template `Node`s to match (recursively).
20285 * @param selectorMatcher a `SelectorMatcher` containing the directives that are in scope for
20286 * this template.
20287 * @returns three maps which contain information about directives in the template: the
20288 * `directives` map which lists directives matched on each node, the `bindings` map which
20289 * indicates which directives claimed which bindings (inputs, outputs, etc), and the `references`
20290 * map which resolves #references (`Reference`s) within the template to the named directive or
20291 * template node.
20292 */
20293 static apply(template, selectorMatcher) {
20294 const directives = new Map();
20295 const bindings = new Map();
20296 const references = new Map();
20297 const matcher = new DirectiveBinder(selectorMatcher, directives, bindings, references);
20298 matcher.ingest(template);
20299 return { directives, bindings, references };
20300 }
20301 ingest(template) {
20302 template.forEach(node => node.visit(this));
20303 }
20304 visitElement(element) {
20305 this.visitElementOrTemplate(element.name, element);
20306 }
20307 visitTemplate(template) {
20308 this.visitElementOrTemplate('ng-template', template);
20309 }
20310 visitElementOrTemplate(elementName, node) {
20311 // First, determine the HTML shape of the node for the purpose of directive matching.
20312 // Do this by building up a `CssSelector` for the node.
20313 const cssSelector = createCssSelector(elementName, getAttrsForDirectiveMatching(node));
20314 // Next, use the `SelectorMatcher` to get the list of directives on the node.
20315 const directives = [];
20316 this.matcher.match(cssSelector, (_, directive) => directives.push(directive));
20317 if (directives.length > 0) {
20318 this.directives.set(node, directives);
20319 }
20320 // Resolve any references that are created on this node.
20321 node.references.forEach(ref => {
20322 let dirTarget = null;
20323 // If the reference expression is empty, then it matches the "primary" directive on the node
20324 // (if there is one). Otherwise it matches the host node itself (either an element or
20325 // <ng-template> node).
20326 if (ref.value.trim() === '') {
20327 // This could be a reference to a component if there is one.
20328 dirTarget = directives.find(dir => dir.isComponent) || null;
20329 }
20330 else {
20331 // This should be a reference to a directive exported via exportAs.
20332 dirTarget =
20333 directives.find(dir => dir.exportAs !== null && dir.exportAs.some(value => value === ref.value)) ||
20334 null;
20335 // Check if a matching directive was found.
20336 if (dirTarget === null) {
20337 // No matching directive was found - this reference points to an unknown target. Leave it
20338 // unmapped.
20339 return;
20340 }
20341 }
20342 if (dirTarget !== null) {
20343 // This reference points to a directive.
20344 this.references.set(ref, { directive: dirTarget, node });
20345 }
20346 else {
20347 // This reference points to the node itself.
20348 this.references.set(ref, node);
20349 }
20350 });
20351 const setAttributeBinding = (attribute, ioType) => {
20352 const dir = directives.find(dir => dir[ioType].hasBindingPropertyName(attribute.name));
20353 const binding = dir !== undefined ? dir : node;
20354 this.bindings.set(attribute, binding);
20355 };
20356 // Node inputs (bound attributes) and text attributes can be bound to an
20357 // input on a directive.
20358 node.inputs.forEach(input => setAttributeBinding(input, 'inputs'));
20359 node.attributes.forEach(attr => setAttributeBinding(attr, 'inputs'));
20360 if (node instanceof Template) {
20361 node.templateAttrs.forEach(attr => setAttributeBinding(attr, 'inputs'));
20362 }
20363 // Node outputs (bound events) can be bound to an output on a directive.
20364 node.outputs.forEach(output => setAttributeBinding(output, 'outputs'));
20365 // Recurse into the node's children.
20366 node.children.forEach(child => child.visit(this));
20367 }
20368 // Unused visitors.
20369 visitContent(content) { }
20370 visitVariable(variable) { }
20371 visitReference(reference) { }
20372 visitTextAttribute(attribute) { }
20373 visitBoundAttribute(attribute) { }
20374 visitBoundEvent(attribute) { }
20375 visitBoundAttributeOrEvent(node) { }
20376 visitText(text) { }
20377 visitBoundText(text) { }
20378 visitIcu(icu) { }
20379 }
20380 /**
20381 * Processes a template and extract metadata about expressions and symbols within.
20382 *
20383 * This is a companion to the `DirectiveBinder` that doesn't require knowledge of directives matched
20384 * within the template in order to operate.
20385 *
20386 * Expressions are visited by the superclass `RecursiveAstVisitor`, with custom logic provided
20387 * by overridden methods from that visitor.
20388 */
20389 class TemplateBinder extends RecursiveAstVisitor {
20390 constructor(bindings, symbols, usedPipes, nestingLevel, scope, template, level) {
20391 super();
20392 this.bindings = bindings;
20393 this.symbols = symbols;
20394 this.usedPipes = usedPipes;
20395 this.nestingLevel = nestingLevel;
20396 this.scope = scope;
20397 this.template = template;
20398 this.level = level;
20399 this.pipesUsed = [];
20400 // Save a bit of processing time by constructing this closure in advance.
20401 this.visitNode = (node) => node.visit(this);
20402 }
20403 // This method is defined to reconcile the type of TemplateBinder since both
20404 // RecursiveAstVisitor and Visitor define the visit() method in their
20405 // interfaces.
20406 visit(node, context) {
20407 if (node instanceof AST) {
20408 node.visit(this, context);
20409 }
20410 else {
20411 node.visit(this);
20412 }
20413 }
20414 /**
20415 * Process a template and extract metadata about expressions and symbols within.
20416 *
20417 * @param template the nodes of the template to process
20418 * @param scope the `Scope` of the template being processed.
20419 * @returns three maps which contain metadata about the template: `expressions` which interprets
20420 * special `AST` nodes in expressions as pointing to references or variables declared within the
20421 * template, `symbols` which maps those variables and references to the nested `Template` which
20422 * declares them, if any, and `nestingLevel` which associates each `Template` with a integer
20423 * nesting level (how many levels deep within the template structure the `Template` is), starting
20424 * at 1.
20425 */
20426 static applyWithScope(template, scope) {
20427 const expressions = new Map();
20428 const symbols = new Map();
20429 const nestingLevel = new Map();
20430 const usedPipes = new Set();
20431 // The top-level template has nesting level 0.
20432 const binder = new TemplateBinder(expressions, symbols, usedPipes, nestingLevel, scope, template instanceof Template ? template : null, 0);
20433 binder.ingest(template);
20434 return { expressions, symbols, nestingLevel, usedPipes };
20435 }
20436 ingest(template) {
20437 if (template instanceof Template) {
20438 // For <ng-template>s, process only variables and child nodes. Inputs, outputs, templateAttrs,
20439 // and references were all processed in the scope of the containing template.
20440 template.variables.forEach(this.visitNode);
20441 template.children.forEach(this.visitNode);
20442 // Set the nesting level.
20443 this.nestingLevel.set(template, this.level);
20444 }
20445 else {
20446 // Visit each node from the top-level template.
20447 template.forEach(this.visitNode);
20448 }
20449 }
20450 visitElement(element) {
20451 // Visit the inputs, outputs, and children of the element.
20452 element.inputs.forEach(this.visitNode);
20453 element.outputs.forEach(this.visitNode);
20454 element.children.forEach(this.visitNode);
20455 }
20456 visitTemplate(template) {
20457 // First, visit inputs, outputs and template attributes of the template node.
20458 template.inputs.forEach(this.visitNode);
20459 template.outputs.forEach(this.visitNode);
20460 template.templateAttrs.forEach(this.visitNode);
20461 // References are also evaluated in the outer context.
20462 template.references.forEach(this.visitNode);
20463 // Next, recurse into the template using its scope, and bumping the nesting level up by one.
20464 const childScope = this.scope.getChildScope(template);
20465 const binder = new TemplateBinder(this.bindings, this.symbols, this.usedPipes, this.nestingLevel, childScope, template, this.level + 1);
20466 binder.ingest(template);
20467 }
20468 visitVariable(variable) {
20469 // Register the `Variable` as a symbol in the current `Template`.
20470 if (this.template !== null) {
20471 this.symbols.set(variable, this.template);
20472 }
20473 }
20474 visitReference(reference) {
20475 // Register the `Reference` as a symbol in the current `Template`.
20476 if (this.template !== null) {
20477 this.symbols.set(reference, this.template);
20478 }
20479 }
20480 // Unused template visitors
20481 visitText(text) { }
20482 visitContent(content) { }
20483 visitTextAttribute(attribute) { }
20484 visitIcu(icu) {
20485 Object.keys(icu.vars).forEach(key => icu.vars[key].visit(this));
20486 Object.keys(icu.placeholders).forEach(key => icu.placeholders[key].visit(this));
20487 }
20488 // The remaining visitors are concerned with processing AST expressions within template bindings
20489 visitBoundAttribute(attribute) {
20490 attribute.value.visit(this);
20491 }
20492 visitBoundEvent(event) {
20493 event.handler.visit(this);
20494 }
20495 visitBoundText(text) {
20496 text.value.visit(this);
20497 }
20498 visitPipe(ast, context) {
20499 this.usedPipes.add(ast.name);
20500 return super.visitPipe(ast, context);
20501 }
20502 // These five types of AST expressions can refer to expression roots, which could be variables
20503 // or references in the current scope.
20504 visitPropertyRead(ast, context) {
20505 this.maybeMap(context, ast, ast.name);
20506 return super.visitPropertyRead(ast, context);
20507 }
20508 visitSafePropertyRead(ast, context) {
20509 this.maybeMap(context, ast, ast.name);
20510 return super.visitSafePropertyRead(ast, context);
20511 }
20512 visitPropertyWrite(ast, context) {
20513 this.maybeMap(context, ast, ast.name);
20514 return super.visitPropertyWrite(ast, context);
20515 }
20516 maybeMap(scope, ast, name) {
20517 // If the receiver of the expression isn't the `ImplicitReceiver`, this isn't the root of an
20518 // `AST` expression that maps to a `Variable` or `Reference`.
20519 if (!(ast.receiver instanceof ImplicitReceiver)) {
20520 return;
20521 }
20522 // Check whether the name exists in the current scope. If so, map it. Otherwise, the name is
20523 // probably a property on the top-level component context.
20524 let target = this.scope.lookup(name);
20525 if (target !== null) {
20526 this.bindings.set(ast, target);
20527 }
20528 }
20529 }
20530 /**
20531 * Metadata container for a `Target` that allows queries for specific bits of metadata.
20532 *
20533 * See `BoundTarget` for documentation on the individual methods.
20534 */
20535 class R3BoundTarget {
20536 constructor(target, directives, bindings, references, exprTargets, symbols, nestingLevel, templateEntities, usedPipes) {
20537 this.target = target;
20538 this.directives = directives;
20539 this.bindings = bindings;
20540 this.references = references;
20541 this.exprTargets = exprTargets;
20542 this.symbols = symbols;
20543 this.nestingLevel = nestingLevel;
20544 this.templateEntities = templateEntities;
20545 this.usedPipes = usedPipes;
20546 }
20547 getEntitiesInTemplateScope(template) {
20548 return this.templateEntities.get(template) ?? new Set();
20549 }
20550 getDirectivesOfNode(node) {
20551 return this.directives.get(node) || null;
20552 }
20553 getReferenceTarget(ref) {
20554 return this.references.get(ref) || null;
20555 }
20556 getConsumerOfBinding(binding) {
20557 return this.bindings.get(binding) || null;
20558 }
20559 getExpressionTarget(expr) {
20560 return this.exprTargets.get(expr) || null;
20561 }
20562 getTemplateOfSymbol(symbol) {
20563 return this.symbols.get(symbol) || null;
20564 }
20565 getNestingLevel(template) {
20566 return this.nestingLevel.get(template) || 0;
20567 }
20568 getUsedDirectives() {
20569 const set = new Set();
20570 this.directives.forEach(dirs => dirs.forEach(dir => set.add(dir)));
20571 return Array.from(set.values());
20572 }
20573 getUsedPipes() {
20574 return Array.from(this.usedPipes);
20575 }
20576 }
20577 function extractTemplateEntities(rootScope) {
20578 const entityMap = new Map();
20579 function extractScopeEntities(scope) {
20580 if (entityMap.has(scope.template)) {
20581 return entityMap.get(scope.template);
20582 }
20583 const currentEntities = scope.namedEntities;
20584 let templateEntities;
20585 if (scope.parentScope !== null) {
20586 templateEntities = new Map([...extractScopeEntities(scope.parentScope), ...currentEntities]);
20587 }
20588 else {
20589 templateEntities = new Map(currentEntities);
20590 }
20591 entityMap.set(scope.template, templateEntities);
20592 return templateEntities;
20593 }
20594 const scopesToProcess = [rootScope];
20595 while (scopesToProcess.length > 0) {
20596 const scope = scopesToProcess.pop();
20597 for (const childScope of scope.childScopes.values()) {
20598 scopesToProcess.push(childScope);
20599 }
20600 extractScopeEntities(scope);
20601 }
20602 const templateEntities = new Map();
20603 for (const [template, entities] of entityMap) {
20604 templateEntities.set(template, new Set(entities.values()));
20605 }
20606 return templateEntities;
20607 }
20608
20609 /**
20610 * @license
20611 * Copyright Google LLC All Rights Reserved.
20612 *
20613 * Use of this source code is governed by an MIT-style license that can be
20614 * found in the LICENSE file at https://angular.io/license
20615 */
20616 function compileClassMetadata(metadata) {
20617 // Generate an ngDevMode guarded call to setClassMetadata with the class identifier and its
20618 // metadata.
20619 const fnCall = importExpr(Identifiers$1.setClassMetadata).callFn([
20620 metadata.type,
20621 metadata.decorators,
20622 metadata.ctorParameters ?? literal$1(null),
20623 metadata.propDecorators ?? literal$1(null),
20624 ]);
20625 const iife = fn([], [devOnlyGuardedExpression(fnCall).toStmt()]);
20626 return iife.callFn([]);
20627 }
20628
20629 /**
20630 * @license
20631 * Copyright Google LLC All Rights Reserved.
20632 *
20633 * Use of this source code is governed by an MIT-style license that can be
20634 * found in the LICENSE file at https://angular.io/license
20635 */
20636 /**
20637 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
20638 * must update this constant to prevent old partial-linkers from incorrectly processing the
20639 * declaration.
20640 *
20641 * Do not include any prerelease in these versions as they are ignored.
20642 */
20643 const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
20644 function compileDeclareClassMetadata(metadata) {
20645 const definitionMap = new DefinitionMap();
20646 definitionMap.set('minVersion', literal$1(MINIMUM_PARTIAL_LINKER_VERSION$6));
20647 definitionMap.set('version', literal$1('13.0.2'));
20648 definitionMap.set('ngImport', importExpr(Identifiers$1.core));
20649 definitionMap.set('type', metadata.type);
20650 definitionMap.set('decorators', metadata.decorators);
20651 definitionMap.set('ctorParameters', metadata.ctorParameters);
20652 definitionMap.set('propDecorators', metadata.propDecorators);
20653 return importExpr(Identifiers$1.declareClassMetadata).callFn([definitionMap.toLiteralMap()]);
20654 }
20655
20656 /**
20657 * @license
20658 * Copyright Google LLC All Rights Reserved.
20659 *
20660 * Use of this source code is governed by an MIT-style license that can be
20661 * found in the LICENSE file at https://angular.io/license
20662 */
20663 /**
20664 * Creates an array literal expression from the given array, mapping all values to an expression
20665 * using the provided mapping function. If the array is empty or null, then null is returned.
20666 *
20667 * @param values The array to transfer into literal array expression.
20668 * @param mapper The logic to use for creating an expression for the array's values.
20669 * @returns An array literal expression representing `values`, or null if `values` is empty or
20670 * is itself null.
20671 */
20672 function toOptionalLiteralArray(values, mapper) {
20673 if (values === null || values.length === 0) {
20674 return null;
20675 }
20676 return literalArr(values.map(value => mapper(value)));
20677 }
20678 /**
20679 * Creates an object literal expression from the given object, mapping all values to an expression
20680 * using the provided mapping function. If the object has no keys, then null is returned.
20681 *
20682 * @param object The object to transfer into an object literal expression.
20683 * @param mapper The logic to use for creating an expression for the object's values.
20684 * @returns An object literal expression representing `object`, or null if `object` does not have
20685 * any keys.
20686 */
20687 function toOptionalLiteralMap(object, mapper) {
20688 const entries = Object.keys(object).map(key => {
20689 const value = object[key];
20690 return { key, value: mapper(value), quoted: true };
20691 });
20692 if (entries.length > 0) {
20693 return literalMap(entries);
20694 }
20695 else {
20696 return null;
20697 }
20698 }
20699 function compileDependencies(deps) {
20700 if (deps === 'invalid') {
20701 // The `deps` can be set to the string "invalid" by the `unwrapConstructorDependencies()`
20702 // function, which tries to convert `ConstructorDeps` into `R3DependencyMetadata[]`.
20703 return literal$1('invalid');
20704 }
20705 else if (deps === null) {
20706 return literal$1(null);
20707 }
20708 else {
20709 return literalArr(deps.map(compileDependency));
20710 }
20711 }
20712 function compileDependency(dep) {
20713 const depMeta = new DefinitionMap();
20714 depMeta.set('token', dep.token);
20715 if (dep.attributeNameType !== null) {
20716 depMeta.set('attribute', literal$1(true));
20717 }
20718 if (dep.host) {
20719 depMeta.set('host', literal$1(true));
20720 }
20721 if (dep.optional) {
20722 depMeta.set('optional', literal$1(true));
20723 }
20724 if (dep.self) {
20725 depMeta.set('self', literal$1(true));
20726 }
20727 if (dep.skipSelf) {
20728 depMeta.set('skipSelf', literal$1(true));
20729 }
20730 return depMeta.toLiteralMap();
20731 }
20732
20733 /**
20734 * @license
20735 * Copyright Google LLC All Rights Reserved.
20736 *
20737 * Use of this source code is governed by an MIT-style license that can be
20738 * found in the LICENSE file at https://angular.io/license
20739 */
20740 /**
20741 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
20742 * must update this constant to prevent old partial-linkers from incorrectly processing the
20743 * declaration.
20744 *
20745 * Do not include any prerelease in these versions as they are ignored.
20746 */
20747 const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
20748 /**
20749 * Compile a directive declaration defined by the `R3DirectiveMetadata`.
20750 */
20751 function compileDeclareDirectiveFromMetadata(meta) {
20752 const definitionMap = createDirectiveDefinitionMap(meta);
20753 const expression = importExpr(Identifiers$1.declareDirective).callFn([definitionMap.toLiteralMap()]);
20754 const type = createDirectiveType(meta);
20755 return { expression, type, statements: [] };
20756 }
20757 /**
20758 * Gathers the declaration fields for a directive into a `DefinitionMap`. This allows for reusing
20759 * this logic for components, as they extend the directive metadata.
20760 */
20761 function createDirectiveDefinitionMap(meta) {
20762 const definitionMap = new DefinitionMap();
20763 definitionMap.set('minVersion', literal$1(MINIMUM_PARTIAL_LINKER_VERSION$5));
20764 definitionMap.set('version', literal$1('13.0.2'));
20765 // e.g. `type: MyDirective`
20766 definitionMap.set('type', meta.internalType);
20767 // e.g. `selector: 'some-dir'`
20768 if (meta.selector !== null) {
20769 definitionMap.set('selector', literal$1(meta.selector));
20770 }
20771 definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
20772 definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
20773 definitionMap.set('host', compileHostMetadata(meta.host));
20774 definitionMap.set('providers', meta.providers);
20775 if (meta.queries.length > 0) {
20776 definitionMap.set('queries', literalArr(meta.queries.map(compileQuery)));
20777 }
20778 if (meta.viewQueries.length > 0) {
20779 definitionMap.set('viewQueries', literalArr(meta.viewQueries.map(compileQuery)));
20780 }
20781 if (meta.exportAs !== null) {
20782 definitionMap.set('exportAs', asLiteral(meta.exportAs));
20783 }
20784 if (meta.usesInheritance) {
20785 definitionMap.set('usesInheritance', literal$1(true));
20786 }
20787 if (meta.lifecycle.usesOnChanges) {
20788 definitionMap.set('usesOnChanges', literal$1(true));
20789 }
20790 definitionMap.set('ngImport', importExpr(Identifiers$1.core));
20791 return definitionMap;
20792 }
20793 /**
20794 * Compiles the metadata of a single query into its partial declaration form as declared
20795 * by `R3DeclareQueryMetadata`.
20796 */
20797 function compileQuery(query) {
20798 const meta = new DefinitionMap();
20799 meta.set('propertyName', literal$1(query.propertyName));
20800 if (query.first) {
20801 meta.set('first', literal$1(true));
20802 }
20803 meta.set('predicate', Array.isArray(query.predicate) ? asLiteral(query.predicate) :
20804 convertFromMaybeForwardRefExpression(query.predicate));
20805 if (!query.emitDistinctChangesOnly) {
20806 // `emitDistinctChangesOnly` is special because we expect it to be `true`.
20807 // Therefore we explicitly emit the field, and explicitly place it only when it's `false`.
20808 meta.set('emitDistinctChangesOnly', literal$1(false));
20809 }
20810 if (query.descendants) {
20811 meta.set('descendants', literal$1(true));
20812 }
20813 meta.set('read', query.read);
20814 if (query.static) {
20815 meta.set('static', literal$1(true));
20816 }
20817 return meta.toLiteralMap();
20818 }
20819 /**
20820 * Compiles the host metadata into its partial declaration form as declared
20821 * in `R3DeclareDirectiveMetadata['host']`
20822 */
20823 function compileHostMetadata(meta) {
20824 const hostMetadata = new DefinitionMap();
20825 hostMetadata.set('attributes', toOptionalLiteralMap(meta.attributes, expression => expression));
20826 hostMetadata.set('listeners', toOptionalLiteralMap(meta.listeners, literal$1));
20827 hostMetadata.set('properties', toOptionalLiteralMap(meta.properties, literal$1));
20828 if (meta.specialAttributes.styleAttr) {
20829 hostMetadata.set('styleAttribute', literal$1(meta.specialAttributes.styleAttr));
20830 }
20831 if (meta.specialAttributes.classAttr) {
20832 hostMetadata.set('classAttribute', literal$1(meta.specialAttributes.classAttr));
20833 }
20834 if (hostMetadata.values.length > 0) {
20835 return hostMetadata.toLiteralMap();
20836 }
20837 else {
20838 return null;
20839 }
20840 }
20841
20842 /**
20843 * @license
20844 * Copyright Google LLC All Rights Reserved.
20845 *
20846 * Use of this source code is governed by an MIT-style license that can be
20847 * found in the LICENSE file at https://angular.io/license
20848 */
20849 /**
20850 * Compile a component declaration defined by the `R3ComponentMetadata`.
20851 */
20852 function compileDeclareComponentFromMetadata(meta, template, additionalTemplateInfo) {
20853 const definitionMap = createComponentDefinitionMap(meta, template, additionalTemplateInfo);
20854 const expression = importExpr(Identifiers$1.declareComponent).callFn([definitionMap.toLiteralMap()]);
20855 const type = createComponentType(meta);
20856 return { expression, type, statements: [] };
20857 }
20858 /**
20859 * Gathers the declaration fields for a component into a `DefinitionMap`.
20860 */
20861 function createComponentDefinitionMap(meta, template, templateInfo) {
20862 const definitionMap = createDirectiveDefinitionMap(meta);
20863 definitionMap.set('template', getTemplateExpression(template, templateInfo));
20864 if (templateInfo.isInline) {
20865 definitionMap.set('isInline', literal$1(true));
20866 }
20867 definitionMap.set('styles', toOptionalLiteralArray(meta.styles, literal$1));
20868 definitionMap.set('components', compileUsedDirectiveMetadata(meta, directive => directive.isComponent === true));
20869 definitionMap.set('directives', compileUsedDirectiveMetadata(meta, directive => directive.isComponent !== true));
20870 definitionMap.set('pipes', compileUsedPipeMetadata(meta));
20871 definitionMap.set('viewProviders', meta.viewProviders);
20872 definitionMap.set('animations', meta.animations);
20873 if (meta.changeDetection !== undefined) {
20874 definitionMap.set('changeDetection', importExpr(Identifiers$1.ChangeDetectionStrategy)
20875 .prop(ChangeDetectionStrategy[meta.changeDetection]));
20876 }
20877 if (meta.encapsulation !== ViewEncapsulation.Emulated) {
20878 definitionMap.set('encapsulation', importExpr(Identifiers$1.ViewEncapsulation).prop(ViewEncapsulation[meta.encapsulation]));
20879 }
20880 if (meta.interpolation !== DEFAULT_INTERPOLATION_CONFIG) {
20881 definitionMap.set('interpolation', literalArr([literal$1(meta.interpolation.start), literal$1(meta.interpolation.end)]));
20882 }
20883 if (template.preserveWhitespaces === true) {
20884 definitionMap.set('preserveWhitespaces', literal$1(true));
20885 }
20886 return definitionMap;
20887 }
20888 function getTemplateExpression(template, templateInfo) {
20889 // If the template has been defined using a direct literal, we use that expression directly
20890 // without any modifications. This is ensures proper source mapping from the partially
20891 // compiled code to the source file declaring the template. Note that this does not capture
20892 // template literals referenced indirectly through an identifier.
20893 if (templateInfo.inlineTemplateLiteralExpression !== null) {
20894 return templateInfo.inlineTemplateLiteralExpression;
20895 }
20896 // If the template is defined inline but not through a literal, the template has been resolved
20897 // through static interpretation. We create a literal but cannot provide any source span. Note
20898 // that we cannot use the expression defining the template because the linker expects the template
20899 // to be defined as a literal in the declaration.
20900 if (templateInfo.isInline) {
20901 return literal$1(templateInfo.content, null, null);
20902 }
20903 // The template is external so we must synthesize an expression node with
20904 // the appropriate source-span.
20905 const contents = templateInfo.content;
20906 const file = new ParseSourceFile(contents, templateInfo.sourceUrl);
20907 const start = new ParseLocation(file, 0, 0, 0);
20908 const end = computeEndLocation(file, contents);
20909 const span = new ParseSourceSpan(start, end);
20910 return literal$1(contents, null, span);
20911 }
20912 function computeEndLocation(file, contents) {
20913 const length = contents.length;
20914 let lineStart = 0;
20915 let lastLineStart = 0;
20916 let line = 0;
20917 do {
20918 lineStart = contents.indexOf('\n', lastLineStart);
20919 if (lineStart !== -1) {
20920 lastLineStart = lineStart + 1;
20921 line++;
20922 }
20923 } while (lineStart !== -1);
20924 return new ParseLocation(file, length, line, length - lastLineStart);
20925 }
20926 /**
20927 * Compiles the directives as registered in the component metadata into an array literal of the
20928 * individual directives. If the component does not use any directives, then null is returned.
20929 */
20930 function compileUsedDirectiveMetadata(meta, predicate) {
20931 const wrapType = meta.declarationListEmitMode !== 0 /* Direct */ ?
20932 generateForwardRef :
20933 (expr) => expr;
20934 const directives = meta.directives.filter(predicate);
20935 return toOptionalLiteralArray(directives, directive => {
20936 const dirMeta = new DefinitionMap();
20937 dirMeta.set('type', wrapType(directive.type));
20938 dirMeta.set('selector', literal$1(directive.selector));
20939 dirMeta.set('inputs', toOptionalLiteralArray(directive.inputs, literal$1));
20940 dirMeta.set('outputs', toOptionalLiteralArray(directive.outputs, literal$1));
20941 dirMeta.set('exportAs', toOptionalLiteralArray(directive.exportAs, literal$1));
20942 return dirMeta.toLiteralMap();
20943 });
20944 }
20945 /**
20946 * Compiles the pipes as registered in the component metadata into an object literal, where the
20947 * pipe's name is used as key and a reference to its type as value. If the component does not use
20948 * any pipes, then null is returned.
20949 */
20950 function compileUsedPipeMetadata(meta) {
20951 if (meta.pipes.size === 0) {
20952 return null;
20953 }
20954 const wrapType = meta.declarationListEmitMode !== 0 /* Direct */ ?
20955 generateForwardRef :
20956 (expr) => expr;
20957 const entries = [];
20958 for (const [name, pipe] of meta.pipes) {
20959 entries.push({ key: name, value: wrapType(pipe), quoted: true });
20960 }
20961 return literalMap(entries);
20962 }
20963
20964 /**
20965 * @license
20966 * Copyright Google LLC All Rights Reserved.
20967 *
20968 * Use of this source code is governed by an MIT-style license that can be
20969 * found in the LICENSE file at https://angular.io/license
20970 */
20971 /**
20972 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
20973 * must update this constant to prevent old partial-linkers from incorrectly processing the
20974 * declaration.
20975 *
20976 * Do not include any prerelease in these versions as they are ignored.
20977 */
20978 const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
20979 function compileDeclareFactoryFunction(meta) {
20980 const definitionMap = new DefinitionMap();
20981 definitionMap.set('minVersion', literal$1(MINIMUM_PARTIAL_LINKER_VERSION$4));
20982 definitionMap.set('version', literal$1('13.0.2'));
20983 definitionMap.set('ngImport', importExpr(Identifiers$1.core));
20984 definitionMap.set('type', meta.internalType);
20985 definitionMap.set('deps', compileDependencies(meta.deps));
20986 definitionMap.set('target', importExpr(Identifiers$1.FactoryTarget).prop(FactoryTarget$1[meta.target]));
20987 return {
20988 expression: importExpr(Identifiers$1.declareFactory).callFn([definitionMap.toLiteralMap()]),
20989 statements: [],
20990 type: createFactoryType(meta),
20991 };
20992 }
20993
20994 /**
20995 * @license
20996 * Copyright Google LLC All Rights Reserved.
20997 *
20998 * Use of this source code is governed by an MIT-style license that can be
20999 * found in the LICENSE file at https://angular.io/license
21000 */
21001 /**
21002 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
21003 * must update this constant to prevent old partial-linkers from incorrectly processing the
21004 * declaration.
21005 *
21006 * Do not include any prerelease in these versions as they are ignored.
21007 */
21008 const MINIMUM_PARTIAL_LINKER_VERSION$3 = '12.0.0';
21009 /**
21010 * Compile a Injectable declaration defined by the `R3InjectableMetadata`.
21011 */
21012 function compileDeclareInjectableFromMetadata(meta) {
21013 const definitionMap = createInjectableDefinitionMap(meta);
21014 const expression = importExpr(Identifiers$1.declareInjectable).callFn([definitionMap.toLiteralMap()]);
21015 const type = createInjectableType(meta);
21016 return { expression, type, statements: [] };
21017 }
21018 /**
21019 * Gathers the declaration fields for a Injectable into a `DefinitionMap`.
21020 */
21021 function createInjectableDefinitionMap(meta) {
21022 const definitionMap = new DefinitionMap();
21023 definitionMap.set('minVersion', literal$1(MINIMUM_PARTIAL_LINKER_VERSION$3));
21024 definitionMap.set('version', literal$1('13.0.2'));
21025 definitionMap.set('ngImport', importExpr(Identifiers$1.core));
21026 definitionMap.set('type', meta.internalType);
21027 // Only generate providedIn property if it has a non-null value
21028 if (meta.providedIn !== undefined) {
21029 const providedIn = convertFromMaybeForwardRefExpression(meta.providedIn);
21030 if (providedIn.value !== null) {
21031 definitionMap.set('providedIn', providedIn);
21032 }
21033 }
21034 if (meta.useClass !== undefined) {
21035 definitionMap.set('useClass', convertFromMaybeForwardRefExpression(meta.useClass));
21036 }
21037 if (meta.useExisting !== undefined) {
21038 definitionMap.set('useExisting', convertFromMaybeForwardRefExpression(meta.useExisting));
21039 }
21040 if (meta.useValue !== undefined) {
21041 definitionMap.set('useValue', convertFromMaybeForwardRefExpression(meta.useValue));
21042 }
21043 // Factories do not contain `ForwardRef`s since any types are already wrapped in a function call
21044 // so the types will not be eagerly evaluated. Therefore we do not need to process this expression
21045 // with `convertFromProviderExpression()`.
21046 if (meta.useFactory !== undefined) {
21047 definitionMap.set('useFactory', meta.useFactory);
21048 }
21049 if (meta.deps !== undefined) {
21050 definitionMap.set('deps', literalArr(meta.deps.map(compileDependency)));
21051 }
21052 return definitionMap;
21053 }
21054
21055 /**
21056 * @license
21057 * Copyright Google LLC All Rights Reserved.
21058 *
21059 * Use of this source code is governed by an MIT-style license that can be
21060 * found in the LICENSE file at https://angular.io/license
21061 */
21062 /**
21063 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
21064 * must update this constant to prevent old partial-linkers from incorrectly processing the
21065 * declaration.
21066 *
21067 * Do not include any prerelease in these versions as they are ignored.
21068 */
21069 const MINIMUM_PARTIAL_LINKER_VERSION$2 = '12.0.0';
21070 function compileDeclareInjectorFromMetadata(meta) {
21071 const definitionMap = createInjectorDefinitionMap(meta);
21072 const expression = importExpr(Identifiers$1.declareInjector).callFn([definitionMap.toLiteralMap()]);
21073 const type = createInjectorType(meta);
21074 return { expression, type, statements: [] };
21075 }
21076 /**
21077 * Gathers the declaration fields for an Injector into a `DefinitionMap`.
21078 */
21079 function createInjectorDefinitionMap(meta) {
21080 const definitionMap = new DefinitionMap();
21081 definitionMap.set('minVersion', literal$1(MINIMUM_PARTIAL_LINKER_VERSION$2));
21082 definitionMap.set('version', literal$1('13.0.2'));
21083 definitionMap.set('ngImport', importExpr(Identifiers$1.core));
21084 definitionMap.set('type', meta.internalType);
21085 definitionMap.set('providers', meta.providers);
21086 if (meta.imports.length > 0) {
21087 definitionMap.set('imports', literalArr(meta.imports));
21088 }
21089 return definitionMap;
21090 }
21091
21092 /**
21093 * @license
21094 * Copyright Google LLC All Rights Reserved.
21095 *
21096 * Use of this source code is governed by an MIT-style license that can be
21097 * found in the LICENSE file at https://angular.io/license
21098 */
21099 /**
21100 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
21101 * must update this constant to prevent old partial-linkers from incorrectly processing the
21102 * declaration.
21103 *
21104 * Do not include any prerelease in these versions as they are ignored.
21105 */
21106 const MINIMUM_PARTIAL_LINKER_VERSION$1 = '12.0.0';
21107 function compileDeclareNgModuleFromMetadata(meta) {
21108 const definitionMap = createNgModuleDefinitionMap(meta);
21109 const expression = importExpr(Identifiers$1.declareNgModule).callFn([definitionMap.toLiteralMap()]);
21110 const type = createNgModuleType(meta);
21111 return { expression, type, statements: [] };
21112 }
21113 /**
21114 * Gathers the declaration fields for an NgModule into a `DefinitionMap`.
21115 */
21116 function createNgModuleDefinitionMap(meta) {
21117 const definitionMap = new DefinitionMap();
21118 definitionMap.set('minVersion', literal$1(MINIMUM_PARTIAL_LINKER_VERSION$1));
21119 definitionMap.set('version', literal$1('13.0.2'));
21120 definitionMap.set('ngImport', importExpr(Identifiers$1.core));
21121 definitionMap.set('type', meta.internalType);
21122 // We only generate the keys in the metadata if the arrays contain values.
21123 // We must wrap the arrays inside a function if any of the values are a forward reference to a
21124 // not-yet-declared class. This is to support JIT execution of the `ɵɵngDeclareNgModule()` call.
21125 // In the linker these wrappers are stripped and then reapplied for the `ɵɵdefineNgModule()` call.
21126 if (meta.bootstrap.length > 0) {
21127 definitionMap.set('bootstrap', refsToArray(meta.bootstrap, meta.containsForwardDecls));
21128 }
21129 if (meta.declarations.length > 0) {
21130 definitionMap.set('declarations', refsToArray(meta.declarations, meta.containsForwardDecls));
21131 }
21132 if (meta.imports.length > 0) {
21133 definitionMap.set('imports', refsToArray(meta.imports, meta.containsForwardDecls));
21134 }
21135 if (meta.exports.length > 0) {
21136 definitionMap.set('exports', refsToArray(meta.exports, meta.containsForwardDecls));
21137 }
21138 if (meta.schemas !== null && meta.schemas.length > 0) {
21139 definitionMap.set('schemas', literalArr(meta.schemas.map(ref => ref.value)));
21140 }
21141 if (meta.id !== null) {
21142 definitionMap.set('id', meta.id);
21143 }
21144 return definitionMap;
21145 }
21146
21147 /**
21148 * @license
21149 * Copyright Google LLC All Rights Reserved.
21150 *
21151 * Use of this source code is governed by an MIT-style license that can be
21152 * found in the LICENSE file at https://angular.io/license
21153 */
21154 /**
21155 * Every time we make a breaking change to the declaration interface or partial-linker behavior, we
21156 * must update this constant to prevent old partial-linkers from incorrectly processing the
21157 * declaration.
21158 *
21159 * Do not include any prerelease in these versions as they are ignored.
21160 */
21161 const MINIMUM_PARTIAL_LINKER_VERSION = '12.0.0';
21162 /**
21163 * Compile a Pipe declaration defined by the `R3PipeMetadata`.
21164 */
21165 function compileDeclarePipeFromMetadata(meta) {
21166 const definitionMap = createPipeDefinitionMap(meta);
21167 const expression = importExpr(Identifiers$1.declarePipe).callFn([definitionMap.toLiteralMap()]);
21168 const type = createPipeType(meta);
21169 return { expression, type, statements: [] };
21170 }
21171 /**
21172 * Gathers the declaration fields for a Pipe into a `DefinitionMap`.
21173 */
21174 function createPipeDefinitionMap(meta) {
21175 const definitionMap = new DefinitionMap();
21176 definitionMap.set('minVersion', literal$1(MINIMUM_PARTIAL_LINKER_VERSION));
21177 definitionMap.set('version', literal$1('13.0.2'));
21178 definitionMap.set('ngImport', importExpr(Identifiers$1.core));
21179 // e.g. `type: MyPipe`
21180 definitionMap.set('type', meta.internalType);
21181 // e.g. `name: "myPipe"`
21182 definitionMap.set('name', literal$1(meta.pipeName));
21183 if (meta.pure === false) {
21184 // e.g. `pure: false`
21185 definitionMap.set('pure', literal$1(meta.pure));
21186 }
21187 return definitionMap;
21188 }
21189
21190 /**
21191 * @license
21192 * Copyright Google LLC All Rights Reserved.
21193 *
21194 * Use of this source code is governed by an MIT-style license that can be
21195 * found in the LICENSE file at https://angular.io/license
21196 */
21197 // This file only reexports content of the `src` folder. Keep it that way.
21198 // This function call has a global side effects and publishes the compiler into global namespace for
21199 // the late binding of the Compiler to the @angular/core for jit compilation.
21200 publishFacade(_global);
21201
21202 /**
21203 * @license
21204 * Copyright Google LLC All Rights Reserved.
21205 *
21206 * Use of this source code is governed by an MIT-style license that can be
21207 * found in the LICENSE file at https://angular.io/license
21208 */
21209 new Version('13.0.2');
21210
21211 /**
21212 * @license
21213 * Copyright Google LLC All Rights Reserved.
21214 *
21215 * Use of this source code is governed by an MIT-style license that can be
21216 * found in the LICENSE file at https://angular.io/license
21217 */
21218 // In TypeScript 2.1 the spread element kind was renamed.
21219 ts__default["default"].SyntaxKind.SpreadElement || ts__default["default"].SyntaxKind.SpreadElementExpression;
21220 ts__default["default"].createNodeArray();
21221
21222 /**
21223 * @license
21224 * Copyright Google LLC All Rights Reserved.
21225 *
21226 * Use of this source code is governed by an MIT-style license that can be
21227 * found in the LICENSE file at https://angular.io/license
21228 */
21229 const UNKNOWN_ERROR_CODE = 500;
21230 var EmitFlags;
21231 (function (EmitFlags) {
21232 EmitFlags[EmitFlags["DTS"] = 1] = "DTS";
21233 EmitFlags[EmitFlags["JS"] = 2] = "JS";
21234 EmitFlags[EmitFlags["Metadata"] = 4] = "Metadata";
21235 EmitFlags[EmitFlags["I18nBundle"] = 8] = "I18nBundle";
21236 EmitFlags[EmitFlags["Codegen"] = 16] = "Codegen";
21237 EmitFlags[EmitFlags["Default"] = 19] = "Default";
21238 EmitFlags[EmitFlags["All"] = 31] = "All";
21239 })(EmitFlags || (EmitFlags = {}));
21240
21241 /**
21242 * @license
21243 * Copyright Google LLC All Rights Reserved.
21244 *
21245 * Use of this source code is governed by an MIT-style license that can be
21246 * found in the LICENSE file at https://angular.io/license
21247 */
21248 /**
21249 * @publicApi
21250 */
21251 var ErrorCode;
21252 (function (ErrorCode) {
21253 ErrorCode[ErrorCode["DECORATOR_ARG_NOT_LITERAL"] = 1001] = "DECORATOR_ARG_NOT_LITERAL";
21254 ErrorCode[ErrorCode["DECORATOR_ARITY_WRONG"] = 1002] = "DECORATOR_ARITY_WRONG";
21255 ErrorCode[ErrorCode["DECORATOR_NOT_CALLED"] = 1003] = "DECORATOR_NOT_CALLED";
21256 ErrorCode[ErrorCode["DECORATOR_ON_ANONYMOUS_CLASS"] = 1004] = "DECORATOR_ON_ANONYMOUS_CLASS";
21257 ErrorCode[ErrorCode["DECORATOR_UNEXPECTED"] = 1005] = "DECORATOR_UNEXPECTED";
21258 /**
21259 * This error code indicates that there are incompatible decorators on a type or a class field.
21260 */
21261 ErrorCode[ErrorCode["DECORATOR_COLLISION"] = 1006] = "DECORATOR_COLLISION";
21262 ErrorCode[ErrorCode["VALUE_HAS_WRONG_TYPE"] = 1010] = "VALUE_HAS_WRONG_TYPE";
21263 ErrorCode[ErrorCode["VALUE_NOT_LITERAL"] = 1011] = "VALUE_NOT_LITERAL";
21264 ErrorCode[ErrorCode["COMPONENT_MISSING_TEMPLATE"] = 2001] = "COMPONENT_MISSING_TEMPLATE";
21265 ErrorCode[ErrorCode["PIPE_MISSING_NAME"] = 2002] = "PIPE_MISSING_NAME";
21266 ErrorCode[ErrorCode["PARAM_MISSING_TOKEN"] = 2003] = "PARAM_MISSING_TOKEN";
21267 ErrorCode[ErrorCode["DIRECTIVE_MISSING_SELECTOR"] = 2004] = "DIRECTIVE_MISSING_SELECTOR";
21268 /** Raised when an undecorated class is passed in as a provider to a module or a directive. */
21269 ErrorCode[ErrorCode["UNDECORATED_PROVIDER"] = 2005] = "UNDECORATED_PROVIDER";
21270 /**
21271 * Raised when a Directive inherits its constructor from a base class without an Angular
21272 * decorator.
21273 */
21274 ErrorCode[ErrorCode["DIRECTIVE_INHERITS_UNDECORATED_CTOR"] = 2006] = "DIRECTIVE_INHERITS_UNDECORATED_CTOR";
21275 /**
21276 * Raised when an undecorated class that is using Angular features
21277 * has been discovered.
21278 */
21279 ErrorCode[ErrorCode["UNDECORATED_CLASS_USING_ANGULAR_FEATURES"] = 2007] = "UNDECORATED_CLASS_USING_ANGULAR_FEATURES";
21280 /**
21281 * Raised when an component cannot resolve an external resource, such as a template or a style
21282 * sheet.
21283 */
21284 ErrorCode[ErrorCode["COMPONENT_RESOURCE_NOT_FOUND"] = 2008] = "COMPONENT_RESOURCE_NOT_FOUND";
21285 /**
21286 * Raised when a component uses `ShadowDom` view encapsulation, but its selector
21287 * does not match the shadow DOM tag name requirements.
21288 */
21289 ErrorCode[ErrorCode["COMPONENT_INVALID_SHADOW_DOM_SELECTOR"] = 2009] = "COMPONENT_INVALID_SHADOW_DOM_SELECTOR";
21290 ErrorCode[ErrorCode["SYMBOL_NOT_EXPORTED"] = 3001] = "SYMBOL_NOT_EXPORTED";
21291 ErrorCode[ErrorCode["SYMBOL_EXPORTED_UNDER_DIFFERENT_NAME"] = 3002] = "SYMBOL_EXPORTED_UNDER_DIFFERENT_NAME";
21292 /**
21293 * Raised when a relationship between directives and/or pipes would cause a cyclic import to be
21294 * created that cannot be handled, such as in partial compilation mode.
21295 */
21296 ErrorCode[ErrorCode["IMPORT_CYCLE_DETECTED"] = 3003] = "IMPORT_CYCLE_DETECTED";
21297 ErrorCode[ErrorCode["CONFIG_FLAT_MODULE_NO_INDEX"] = 4001] = "CONFIG_FLAT_MODULE_NO_INDEX";
21298 ErrorCode[ErrorCode["CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK"] = 4002] = "CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK";
21299 /**
21300 * Raised when a host expression has a parse error, such as a host listener or host binding
21301 * expression containing a pipe.
21302 */
21303 ErrorCode[ErrorCode["HOST_BINDING_PARSE_ERROR"] = 5001] = "HOST_BINDING_PARSE_ERROR";
21304 /**
21305 * Raised when the compiler cannot parse a component's template.
21306 */
21307 ErrorCode[ErrorCode["TEMPLATE_PARSE_ERROR"] = 5002] = "TEMPLATE_PARSE_ERROR";
21308 /**
21309 * Raised when an NgModule contains an invalid reference in `declarations`.
21310 */
21311 ErrorCode[ErrorCode["NGMODULE_INVALID_DECLARATION"] = 6001] = "NGMODULE_INVALID_DECLARATION";
21312 /**
21313 * Raised when an NgModule contains an invalid type in `imports`.
21314 */
21315 ErrorCode[ErrorCode["NGMODULE_INVALID_IMPORT"] = 6002] = "NGMODULE_INVALID_IMPORT";
21316 /**
21317 * Raised when an NgModule contains an invalid type in `exports`.
21318 */
21319 ErrorCode[ErrorCode["NGMODULE_INVALID_EXPORT"] = 6003] = "NGMODULE_INVALID_EXPORT";
21320 /**
21321 * Raised when an NgModule contains a type in `exports` which is neither in `declarations` nor
21322 * otherwise imported.
21323 */
21324 ErrorCode[ErrorCode["NGMODULE_INVALID_REEXPORT"] = 6004] = "NGMODULE_INVALID_REEXPORT";
21325 /**
21326 * Raised when a `ModuleWithProviders` with a missing
21327 * generic type argument is passed into an `NgModule`.
21328 */
21329 ErrorCode[ErrorCode["NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC"] = 6005] = "NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC";
21330 /**
21331 * Raised when an NgModule exports multiple directives/pipes of the same name and the compiler
21332 * attempts to generate private re-exports within the NgModule file.
21333 */
21334 ErrorCode[ErrorCode["NGMODULE_REEXPORT_NAME_COLLISION"] = 6006] = "NGMODULE_REEXPORT_NAME_COLLISION";
21335 /**
21336 * Raised when a directive/pipe is part of the declarations of two or more NgModules.
21337 */
21338 ErrorCode[ErrorCode["NGMODULE_DECLARATION_NOT_UNIQUE"] = 6007] = "NGMODULE_DECLARATION_NOT_UNIQUE";
21339 /**
21340 * Not actually raised by the compiler, but reserved for documentation of a View Engine error when
21341 * a View Engine build depends on an Ivy-compiled NgModule.
21342 */
21343 ErrorCode[ErrorCode["NGMODULE_VE_DEPENDENCY_ON_IVY_LIB"] = 6999] = "NGMODULE_VE_DEPENDENCY_ON_IVY_LIB";
21344 /**
21345 * An element name failed validation against the DOM schema.
21346 */
21347 ErrorCode[ErrorCode["SCHEMA_INVALID_ELEMENT"] = 8001] = "SCHEMA_INVALID_ELEMENT";
21348 /**
21349 * An element's attribute name failed validation against the DOM schema.
21350 */
21351 ErrorCode[ErrorCode["SCHEMA_INVALID_ATTRIBUTE"] = 8002] = "SCHEMA_INVALID_ATTRIBUTE";
21352 /**
21353 * No matching directive was found for a `#ref="target"` expression.
21354 */
21355 ErrorCode[ErrorCode["MISSING_REFERENCE_TARGET"] = 8003] = "MISSING_REFERENCE_TARGET";
21356 /**
21357 * No matching pipe was found for a
21358 */
21359 ErrorCode[ErrorCode["MISSING_PIPE"] = 8004] = "MISSING_PIPE";
21360 /**
21361 * The left-hand side of an assignment expression was a template variable. Effectively, the
21362 * template looked like:
21363 *
21364 * ```
21365 * <ng-template let-something>
21366 * <button (click)="something = ...">...</button>
21367 * </ng-template>
21368 * ```
21369 *
21370 * Template variables are read-only.
21371 */
21372 ErrorCode[ErrorCode["WRITE_TO_READ_ONLY_VARIABLE"] = 8005] = "WRITE_TO_READ_ONLY_VARIABLE";
21373 /**
21374 * A template variable was declared twice. For example:
21375 *
21376 * ```html
21377 * <div *ngFor="let i of items; let i = index">
21378 * </div>
21379 * ```
21380 */
21381 ErrorCode[ErrorCode["DUPLICATE_VARIABLE_DECLARATION"] = 8006] = "DUPLICATE_VARIABLE_DECLARATION";
21382 /**
21383 * A template has a two way binding (two bindings created by a single syntactial element)
21384 * in which the input and output are going to different places.
21385 */
21386 ErrorCode[ErrorCode["SPLIT_TWO_WAY_BINDING"] = 8007] = "SPLIT_TWO_WAY_BINDING";
21387 /**
21388 * A two way binding in a template has an incorrect syntax,
21389 * parentheses outside brackets. For example:
21390 *
21391 * ```
21392 * <div ([foo])="bar" />
21393 * ```
21394 */
21395 ErrorCode[ErrorCode["INVALID_BANANA_IN_BOX"] = 8101] = "INVALID_BANANA_IN_BOX";
21396 /**
21397 * The left side of a nullish coalescing operation is not nullable.
21398 *
21399 * ```
21400 * {{ foo ?? bar }}
21401 * ```
21402 * When the type of foo doesn't include `null` or `undefined`.
21403 */
21404 ErrorCode[ErrorCode["NULLISH_COALESCING_NOT_NULLABLE"] = 8102] = "NULLISH_COALESCING_NOT_NULLABLE";
21405 /**
21406 * The template type-checking engine would need to generate an inline type check block for a
21407 * component, but the current type-checking environment doesn't support it.
21408 */
21409 ErrorCode[ErrorCode["INLINE_TCB_REQUIRED"] = 8900] = "INLINE_TCB_REQUIRED";
21410 /**
21411 * The template type-checking engine would need to generate an inline type constructor for a
21412 * directive or component, but the current type-checking environment doesn't support it.
21413 */
21414 ErrorCode[ErrorCode["INLINE_TYPE_CTOR_REQUIRED"] = 8901] = "INLINE_TYPE_CTOR_REQUIRED";
21415 /**
21416 * An injectable already has a `ɵprov` property.
21417 */
21418 ErrorCode[ErrorCode["INJECTABLE_DUPLICATE_PROV"] = 9001] = "INJECTABLE_DUPLICATE_PROV";
21419 // 10XXX error codes are reserved for diagnostics with categories other than
21420 // `ts.DiagnosticCategory.Error`. These diagnostics are generated by the compiler when configured
21421 // to do so by a tool such as the Language Service, or by the Language Service itself.
21422 /**
21423 * Suggest users to enable `strictTemplates` to make use of full capabilities
21424 * provided by Angular language service.
21425 */
21426 ErrorCode[ErrorCode["SUGGEST_STRICT_TEMPLATES"] = 10001] = "SUGGEST_STRICT_TEMPLATES";
21427 /**
21428 * Indicates that a particular structural directive provides advanced type narrowing
21429 * functionality, but the current template type-checking configuration does not allow its usage in
21430 * type inference.
21431 */
21432 ErrorCode[ErrorCode["SUGGEST_SUBOPTIMAL_TYPE_INFERENCE"] = 10002] = "SUGGEST_SUBOPTIMAL_TYPE_INFERENCE";
21433 })(ErrorCode || (ErrorCode = {}));
21434
21435 /**
21436 * @license
21437 * Copyright Google LLC All Rights Reserved.
21438 *
21439 * Use of this source code is governed by an MIT-style license that can be
21440 * found in the LICENSE file at https://angular.io/license
21441 */
21442 /**
21443 * Contains a set of error messages that have detailed guides at angular.io.
21444 * Full list of available error guides can be found at https://angular.io/errors
21445 */
21446 const COMPILER_ERRORS_WITH_GUIDES = new Set([
21447 ErrorCode.DECORATOR_ARG_NOT_LITERAL,
21448 ErrorCode.IMPORT_CYCLE_DETECTED,
21449 ErrorCode.PARAM_MISSING_TOKEN,
21450 ErrorCode.SCHEMA_INVALID_ELEMENT,
21451 ErrorCode.SCHEMA_INVALID_ATTRIBUTE,
21452 ErrorCode.MISSING_REFERENCE_TARGET,
21453 ErrorCode.COMPONENT_INVALID_SHADOW_DOM_SELECTOR,
21454 ]);
21455
21456 /**
21457 * @license
21458 * Copyright Google LLC All Rights Reserved.
21459 *
21460 * Use of this source code is governed by an MIT-style license that can be
21461 * found in the LICENSE file at https://angular.io/license
21462 */
21463 function ngErrorCode(code) {
21464 return parseInt('-99' + code);
21465 }
21466
21467 /**
21468 * @license
21469 * Copyright Google LLC All Rights Reserved.
21470 *
21471 * Use of this source code is governed by an MIT-style license that can be
21472 * found in the LICENSE file at https://angular.io/license
21473 */
21474 class FatalDiagnosticError {
21475 constructor(code, node, message, relatedInformation) {
21476 this.code = code;
21477 this.node = node;
21478 this.message = message;
21479 this.relatedInformation = relatedInformation;
21480 /**
21481 * @internal
21482 */
21483 this._isFatalDiagnosticError = true;
21484 }
21485 toDiagnostic() {
21486 return makeDiagnostic(this.code, this.node, this.message, this.relatedInformation);
21487 }
21488 }
21489 function makeDiagnostic(code, node, messageText, relatedInformation) {
21490 node = ts__default["default"].getOriginalNode(node);
21491 return {
21492 category: ts__default["default"].DiagnosticCategory.Error,
21493 code: ngErrorCode(code),
21494 file: ts__default["default"].getOriginalNode(node).getSourceFile(),
21495 start: node.getStart(undefined, false),
21496 length: node.getWidth(),
21497 messageText,
21498 relatedInformation,
21499 };
21500 }
21501 function makeRelatedInformation(node, messageText) {
21502 node = ts__default["default"].getOriginalNode(node);
21503 return {
21504 category: ts__default["default"].DiagnosticCategory.Message,
21505 code: 0,
21506 file: node.getSourceFile(),
21507 start: node.getStart(),
21508 length: node.getWidth(),
21509 messageText,
21510 };
21511 }
21512
21513 /**
21514 * @license
21515 * Copyright Google LLC All Rights Reserved.
21516 *
21517 * Use of this source code is governed by an MIT-style license that can be
21518 * found in the LICENSE file at https://angular.io/license
21519 */
21520 /**
21521 * Base URL for the error details page.
21522 *
21523 * Keep the files below in full sync:
21524 * - packages/compiler-cli/src/ngtsc/diagnostics/src/error_details_base_url.ts
21525 * - packages/core/src/render3/error_details_base_url.ts
21526 */
21527 const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
21528
21529 /**
21530 * @license
21531 * Copyright Google LLC All Rights Reserved.
21532 *
21533 * Use of this source code is governed by an MIT-style license that can be
21534 * found in the LICENSE file at https://angular.io/license
21535 */
21536 const D_TS = /\.d\.ts$/i;
21537 function isSymbolWithValueDeclaration(symbol) {
21538 // If there is a value declaration set, then the `declarations` property is never undefined. We
21539 // still check for the property to exist as this matches with the type that `symbol` is narrowed
21540 // to.
21541 return symbol != null && symbol.valueDeclaration !== undefined &&
21542 symbol.declarations !== undefined;
21543 }
21544 function isDtsPath(filePath) {
21545 return D_TS.test(filePath);
21546 }
21547 function nodeNameForError(node) {
21548 if (node.name !== undefined && ts__default["default"].isIdentifier(node.name)) {
21549 return node.name.text;
21550 }
21551 else {
21552 const kind = ts__default["default"].SyntaxKind[node.kind];
21553 const { line, character } = ts__default["default"].getLineAndCharacterOfPosition(node.getSourceFile(), node.getStart());
21554 return `${kind}@${line}:${character}`;
21555 }
21556 }
21557 function getSourceFile(node) {
21558 // In certain transformation contexts, `ts.Node.getSourceFile()` can actually return `undefined`,
21559 // despite the type signature not allowing it. In that event, get the `ts.SourceFile` via the
21560 // original node instead (which works).
21561 const directSf = node.getSourceFile();
21562 return directSf !== undefined ? directSf : ts__default["default"].getOriginalNode(node).getSourceFile();
21563 }
21564 function getSourceFileOrNull(program, fileName) {
21565 return program.getSourceFile(fileName) || null;
21566 }
21567 function getTokenAtPosition(sf, pos) {
21568 // getTokenAtPosition is part of TypeScript's private API.
21569 return ts__default["default"].getTokenAtPosition(sf, pos);
21570 }
21571 function identifierOfNode(decl) {
21572 if (decl.name !== undefined && ts__default["default"].isIdentifier(decl.name)) {
21573 return decl.name;
21574 }
21575 else {
21576 return null;
21577 }
21578 }
21579 function isDeclaration(node) {
21580 return isValueDeclaration(node) || isTypeDeclaration(node);
21581 }
21582 function isValueDeclaration(node) {
21583 return ts__default["default"].isClassDeclaration(node) || ts__default["default"].isFunctionDeclaration(node) ||
21584 ts__default["default"].isVariableDeclaration(node);
21585 }
21586 function isTypeDeclaration(node) {
21587 return ts__default["default"].isEnumDeclaration(node) || ts__default["default"].isTypeAliasDeclaration(node) ||
21588 ts__default["default"].isInterfaceDeclaration(node);
21589 }
21590 function isNamedDeclaration(node) {
21591 const namedNode = node;
21592 return namedNode.name !== undefined && ts__default["default"].isIdentifier(namedNode.name);
21593 }
21594 function getRootDirs(host, options) {
21595 const rootDirs = [];
21596 const cwd = host.getCurrentDirectory();
21597 const fs = getFileSystem();
21598 if (options.rootDirs !== undefined) {
21599 rootDirs.push(...options.rootDirs);
21600 }
21601 else if (options.rootDir !== undefined) {
21602 rootDirs.push(options.rootDir);
21603 }
21604 else {
21605 rootDirs.push(cwd);
21606 }
21607 // In Windows the above might not always return posix separated paths
21608 // See:
21609 // https://github.com/Microsoft/TypeScript/blob/3f7357d37f66c842d70d835bc925ec2a873ecfec/src/compiler/sys.ts#L650
21610 // Also compiler options might be set via an API which doesn't normalize paths
21611 return rootDirs.map(rootDir => fs.resolve(cwd, host.getCanonicalFileName(rootDir)));
21612 }
21613 function nodeDebugInfo(node) {
21614 const sf = getSourceFile(node);
21615 const { line, character } = ts__default["default"].getLineAndCharacterOfPosition(sf, node.pos);
21616 return `[${sf.fileName}: ${ts__default["default"].SyntaxKind[node.kind]} @ ${line}:${character}]`;
21617 }
21618 /**
21619 * Resolve the specified `moduleName` using the given `compilerOptions` and `compilerHost`.
21620 *
21621 * This helper will attempt to use the `CompilerHost.resolveModuleNames()` method if available.
21622 * Otherwise it will fallback on the `ts.ResolveModuleName()` function.
21623 */
21624 function resolveModuleName(moduleName, containingFile, compilerOptions, compilerHost, moduleResolutionCache) {
21625 if (compilerHost.resolveModuleNames) {
21626 return compilerHost.resolveModuleNames([moduleName], containingFile, undefined, // reusedNames
21627 undefined, // redirectedReference
21628 compilerOptions)[0];
21629 }
21630 else {
21631 return ts__default["default"]
21632 .resolveModuleName(moduleName, containingFile, compilerOptions, compilerHost, moduleResolutionCache !== null ? moduleResolutionCache : undefined)
21633 .resolvedModule;
21634 }
21635 }
21636 /** Returns true if the node is an assignment expression. */
21637 function isAssignment(node) {
21638 return ts__default["default"].isBinaryExpression(node) && node.operatorToken.kind === ts__default["default"].SyntaxKind.EqualsToken;
21639 }
21640 /**
21641 * Obtains the non-redirected source file for `sf`.
21642 */
21643 function toUnredirectedSourceFile(sf) {
21644 const redirectInfo = sf.redirectInfo;
21645 if (redirectInfo === undefined) {
21646 return sf;
21647 }
21648 return redirectInfo.unredirected;
21649 }
21650
21651 /**
21652 * @license
21653 * Copyright Google LLC All Rights Reserved.
21654 *
21655 * Use of this source code is governed by an MIT-style license that can be
21656 * found in the LICENSE file at https://angular.io/license
21657 */
21658 /**
21659 * Find the name, if any, by which a node is exported from a given file.
21660 */
21661 function findExportedNameOfNode(target, file, reflector) {
21662 const exports = reflector.getExportsOfModule(file);
21663 if (exports === null) {
21664 return null;
21665 }
21666 const declaredName = isNamedDeclaration(target) ? target.name.text : null;
21667 // Look for the export which declares the node.
21668 let foundExportName = null;
21669 for (const [exportName, declaration] of exports) {
21670 if (declaration.node !== target) {
21671 continue;
21672 }
21673 if (exportName === declaredName) {
21674 // A non-alias export exists which is always preferred, so use that one.
21675 return exportName;
21676 }
21677 foundExportName = exportName;
21678 }
21679 if (foundExportName === null) {
21680 throw new Error(`Failed to find exported name of node (${target.getText()}) in '${file.fileName}'.`);
21681 }
21682 return foundExportName;
21683 }
21684
21685 /**
21686 * @license
21687 * Copyright Google LLC All Rights Reserved.
21688 *
21689 * Use of this source code is governed by an MIT-style license that can be
21690 * found in the LICENSE file at https://angular.io/license
21691 */
21692 /**
21693 * Flags which alter the imports generated by the `ReferenceEmitter`.
21694 */
21695 var ImportFlags;
21696 (function (ImportFlags) {
21697 ImportFlags[ImportFlags["None"] = 0] = "None";
21698 /**
21699 * Force the generation of a new import when generating a reference, even if an identifier already
21700 * exists in the target file which could be used instead.
21701 *
21702 * This is sometimes required if there's a risk TypeScript might remove imports during emit.
21703 */
21704 ImportFlags[ImportFlags["ForceNewImport"] = 1] = "ForceNewImport";
21705 /**
21706 * Don't make use of any aliasing information when emitting a reference.
21707 *
21708 * This is sometimes required if emitting into a context where generated references will be fed
21709 * into TypeScript and type-checked (such as in template type-checking).
21710 */
21711 ImportFlags[ImportFlags["NoAliasing"] = 2] = "NoAliasing";
21712 /**
21713 * Indicates that an import to a type-only declaration is allowed.
21714 *
21715 * For references that occur in type-positions, the referred declaration may be a type-only
21716 * declaration that is not retained during emit. Including this flag allows to emit references to
21717 * type-only declarations as used in e.g. template type-checking.
21718 */
21719 ImportFlags[ImportFlags["AllowTypeImports"] = 4] = "AllowTypeImports";
21720 })(ImportFlags || (ImportFlags = {}));
21721 /**
21722 * Generates `Expression`s which refer to `Reference`s in a given context.
21723 *
21724 * A `ReferenceEmitter` uses one or more `ReferenceEmitStrategy` implementations to produce an
21725 * `Expression` which refers to a `Reference` in the context of a particular file.
21726 */
21727 class ReferenceEmitter {
21728 constructor(strategies) {
21729 this.strategies = strategies;
21730 }
21731 emit(ref, context, importFlags = ImportFlags.None) {
21732 for (const strategy of this.strategies) {
21733 const emitted = strategy.emit(ref, context, importFlags);
21734 if (emitted !== null) {
21735 return emitted;
21736 }
21737 }
21738 throw new Error(`Unable to write a reference to ${nodeNameForError(ref.node)} in ${ref.node.getSourceFile().fileName} from ${context.fileName}`);
21739 }
21740 }
21741 /**
21742 * A `ReferenceEmitStrategy` which will refer to declarations by any local `ts.Identifier`s, if
21743 * such identifiers are available.
21744 */
21745 class LocalIdentifierStrategy {
21746 emit(ref, context, importFlags) {
21747 const refSf = getSourceFile(ref.node);
21748 // If the emitter has specified ForceNewImport, then LocalIdentifierStrategy should not use a
21749 // local identifier at all, *except* in the source file where the node is actually declared.
21750 if (importFlags & ImportFlags.ForceNewImport && refSf !== context) {
21751 return null;
21752 }
21753 // If referenced node is not an actual TS declaration (e.g. `class Foo` or `function foo() {}`,
21754 // etc) and it is in the current file then just use it directly.
21755 // This is important because the reference could be a property access (e.g. `exports.foo`). In
21756 // such a case, the reference's `identities` property would be `[foo]`, which would result in an
21757 // invalid emission of a free-standing `foo` identifier, rather than `exports.foo`.
21758 if (!isDeclaration(ref.node) && refSf === context) {
21759 return {
21760 expression: new WrappedNodeExpr(ref.node),
21761 importedFile: null,
21762 };
21763 }
21764 // A Reference can have multiple identities in different files, so it may already have an
21765 // Identifier in the requested context file.
21766 const identifier = ref.getIdentityIn(context);
21767 if (identifier !== null) {
21768 return {
21769 expression: new WrappedNodeExpr(identifier),
21770 importedFile: null,
21771 };
21772 }
21773 else {
21774 return null;
21775 }
21776 }
21777 }
21778 /**
21779 * A `ReferenceEmitStrategy` which will refer to declarations that come from `node_modules` using
21780 * an absolute import.
21781 *
21782 * Part of this strategy involves looking at the target entry point and identifying the exported
21783 * name of the targeted declaration, as it might be different from the declared name (e.g. a
21784 * directive might be declared as FooDirImpl, but exported as FooDir). If no export can be found
21785 * which maps back to the original directive, an error is thrown.
21786 */
21787 class AbsoluteModuleStrategy {
21788 constructor(program, checker, moduleResolver, reflectionHost) {
21789 this.program = program;
21790 this.checker = checker;
21791 this.moduleResolver = moduleResolver;
21792 this.reflectionHost = reflectionHost;
21793 /**
21794 * A cache of the exports of specific modules, because resolving a module to its exports is a
21795 * costly operation.
21796 */
21797 this.moduleExportsCache = new Map();
21798 }
21799 emit(ref, context, importFlags) {
21800 if (ref.bestGuessOwningModule === null) {
21801 // There is no module name available for this Reference, meaning it was arrived at via a
21802 // relative path.
21803 return null;
21804 }
21805 else if (!isDeclaration(ref.node)) {
21806 // It's not possible to import something which isn't a declaration.
21807 throw new Error(`Debug assert: unable to import a Reference to non-declaration of type ${ts__default["default"].SyntaxKind[ref.node.kind]}.`);
21808 }
21809 else if ((importFlags & ImportFlags.AllowTypeImports) === 0 && isTypeDeclaration(ref.node)) {
21810 throw new Error(`Importing a type-only declaration of type ${ts__default["default"].SyntaxKind[ref.node.kind]} in a value position is not allowed.`);
21811 }
21812 // Try to find the exported name of the declaration, if one is available.
21813 const { specifier, resolutionContext } = ref.bestGuessOwningModule;
21814 const exports = this.getExportsOfModule(specifier, resolutionContext);
21815 if (exports === null || !exports.exportMap.has(ref.node)) {
21816 // TODO(alxhub): make this error a ts.Diagnostic pointing at whatever caused this import to be
21817 // triggered.
21818 throw new Error(`Symbol ${ref.debugName} declared in ${getSourceFile(ref.node).fileName} is not exported from ${specifier} (import into ${context.fileName})`);
21819 }
21820 const symbolName = exports.exportMap.get(ref.node);
21821 return {
21822 expression: new ExternalExpr(new ExternalReference(specifier, symbolName)),
21823 importedFile: exports.module,
21824 };
21825 }
21826 getExportsOfModule(moduleName, fromFile) {
21827 if (!this.moduleExportsCache.has(moduleName)) {
21828 this.moduleExportsCache.set(moduleName, this.enumerateExportsOfModule(moduleName, fromFile));
21829 }
21830 return this.moduleExportsCache.get(moduleName);
21831 }
21832 enumerateExportsOfModule(specifier, fromFile) {
21833 // First, resolve the module specifier to its entry point, and get the ts.Symbol for it.
21834 const entryPointFile = this.moduleResolver.resolveModule(specifier, fromFile);
21835 if (entryPointFile === null) {
21836 return null;
21837 }
21838 const exports = this.reflectionHost.getExportsOfModule(entryPointFile);
21839 if (exports === null) {
21840 return null;
21841 }
21842 const exportMap = new Map();
21843 for (const [name, declaration] of exports) {
21844 if (exportMap.has(declaration.node)) {
21845 // An export for this declaration has already been registered. We prefer an export that
21846 // has the same name as the declared name, i.e. is not an aliased export. This is relevant
21847 // for partial compilations where emitted references should import symbols using a stable
21848 // name. This is particularly relevant for declarations inside VE-generated libraries, as
21849 // such libraries contain private, unstable reexports of symbols.
21850 const existingExport = exportMap.get(declaration.node);
21851 if (isNamedDeclaration(declaration.node) && declaration.node.name.text === existingExport) {
21852 continue;
21853 }
21854 }
21855 exportMap.set(declaration.node, name);
21856 }
21857 return { module: entryPointFile, exportMap };
21858 }
21859 }
21860 /**
21861 * A `ReferenceEmitStrategy` which will refer to declarations via relative paths, provided they're
21862 * both in the logical project "space" of paths.
21863 *
21864 * This is trickier than it sounds, as the two files may be in different root directories in the
21865 * project. Simply calculating a file system relative path between the two is not sufficient.
21866 * Instead, `LogicalProjectPath`s are used.
21867 */
21868 class LogicalProjectStrategy {
21869 constructor(reflector, logicalFs) {
21870 this.reflector = reflector;
21871 this.logicalFs = logicalFs;
21872 }
21873 emit(ref, context) {
21874 const destSf = getSourceFile(ref.node);
21875 // Compute the relative path from the importing file to the file being imported. This is done
21876 // as a logical path computation, because the two files might be in different rootDirs.
21877 const destPath = this.logicalFs.logicalPathOfSf(destSf);
21878 if (destPath === null) {
21879 // The imported file is not within the logical project filesystem.
21880 return null;
21881 }
21882 const originPath = this.logicalFs.logicalPathOfSf(context);
21883 if (originPath === null) {
21884 throw new Error(`Debug assert: attempt to import from ${context.fileName} but it's outside the program?`);
21885 }
21886 // There's no way to emit a relative reference from a file to itself.
21887 if (destPath === originPath) {
21888 return null;
21889 }
21890 const name = findExportedNameOfNode(ref.node, destSf, this.reflector);
21891 if (name === null) {
21892 // The target declaration isn't exported from the file it's declared in. This is an issue!
21893 return null;
21894 }
21895 // With both files expressed as LogicalProjectPaths, getting the module specifier as a relative
21896 // path is now straightforward.
21897 const moduleName = LogicalProjectPath.relativePathBetween(originPath, destPath);
21898 return {
21899 expression: new ExternalExpr({ moduleName, name }),
21900 importedFile: destSf,
21901 };
21902 }
21903 }
21904 /**
21905 * A `ReferenceEmitStrategy` which constructs relatives paths between `ts.SourceFile`s.
21906 *
21907 * This strategy can be used if there is no `rootDir`/`rootDirs` structure for the project which
21908 * necessitates the stronger logic of `LogicalProjectStrategy`.
21909 */
21910 class RelativePathStrategy {
21911 constructor(reflector) {
21912 this.reflector = reflector;
21913 }
21914 emit(ref, context) {
21915 const destSf = getSourceFile(ref.node);
21916 const relativePath = relative(dirname(absoluteFromSourceFile(context)), absoluteFromSourceFile(destSf));
21917 const moduleName = toRelativeImport(stripExtension(relativePath));
21918 const name = findExportedNameOfNode(ref.node, destSf, this.reflector);
21919 return { expression: new ExternalExpr({ moduleName, name }), importedFile: destSf };
21920 }
21921 }
21922 /**
21923 * A `ReferenceEmitStrategy` which uses a `UnifiedModulesHost` to generate absolute import
21924 * references.
21925 */
21926 class UnifiedModulesStrategy {
21927 constructor(reflector, unifiedModulesHost) {
21928 this.reflector = reflector;
21929 this.unifiedModulesHost = unifiedModulesHost;
21930 }
21931 emit(ref, context) {
21932 const destSf = getSourceFile(ref.node);
21933 const name = findExportedNameOfNode(ref.node, destSf, this.reflector);
21934 if (name === null) {
21935 return null;
21936 }
21937 const moduleName = this.unifiedModulesHost.fileNameToModuleName(destSf.fileName, context.fileName);
21938 return {
21939 expression: new ExternalExpr({ moduleName, name }),
21940 importedFile: destSf,
21941 };
21942 }
21943 }
21944
21945 /**
21946 * @license
21947 * Copyright Google LLC All Rights Reserved.
21948 *
21949 * Use of this source code is governed by an MIT-style license that can be
21950 * found in the LICENSE file at https://angular.io/license
21951 */
21952 // Escape anything that isn't alphanumeric, '/' or '_'.
21953 const CHARS_TO_ESCAPE = /[^a-zA-Z0-9/_]/g;
21954 /**
21955 * An `AliasingHost` which generates and consumes alias re-exports when module names for each file
21956 * are determined by a `UnifiedModulesHost`.
21957 *
21958 * When using a `UnifiedModulesHost`, aliasing prevents issues with transitive dependencies. See the
21959 * README.md for more details.
21960 */
21961 class UnifiedModulesAliasingHost {
21962 constructor(unifiedModulesHost) {
21963 this.unifiedModulesHost = unifiedModulesHost;
21964 /**
21965 * With a `UnifiedModulesHost`, aliases are chosen automatically without the need to look through
21966 * the exports present in a .d.ts file, so we can avoid cluttering the .d.ts files.
21967 */
21968 this.aliasExportsInDts = false;
21969 }
21970 maybeAliasSymbolAs(ref, context, ngModuleName, isReExport) {
21971 if (!isReExport) {
21972 // Aliasing is used with a UnifiedModulesHost to prevent transitive dependencies. Thus,
21973 // aliases
21974 // only need to be created for directives/pipes which are not direct declarations of an
21975 // NgModule which exports them.
21976 return null;
21977 }
21978 return this.aliasName(ref.node, context);
21979 }
21980 /**
21981 * Generates an `Expression` to import `decl` from `via`, assuming an export was added when `via`
21982 * was compiled per `maybeAliasSymbolAs` above.
21983 */
21984 getAliasIn(decl, via, isReExport) {
21985 if (!isReExport) {
21986 // Directly exported directives/pipes don't require an alias, per the logic in
21987 // `maybeAliasSymbolAs`.
21988 return null;
21989 }
21990 // viaModule is the module it'll actually be imported from.
21991 const moduleName = this.unifiedModulesHost.fileNameToModuleName(via.fileName, via.fileName);
21992 return new ExternalExpr({ moduleName, name: this.aliasName(decl, via) });
21993 }
21994 /**
21995 * Generates an alias name based on the full module name of the file which declares the aliased
21996 * directive/pipe.
21997 */
21998 aliasName(decl, context) {
21999 // The declared module is used to get the name of the alias.
22000 const declModule = this.unifiedModulesHost.fileNameToModuleName(decl.getSourceFile().fileName, context.fileName);
22001 const replaced = declModule.replace(CHARS_TO_ESCAPE, '_').replace(/\//g, '$');
22002 return 'ɵng$' + replaced + '$$' + decl.name.text;
22003 }
22004 }
22005 /**
22006 * An `AliasingHost` which exports directives from any file containing an NgModule in which they're
22007 * declared/exported, under a private symbol name.
22008 *
22009 * These exports support cases where an NgModule is imported deeply from an absolute module path
22010 * (that is, it's not part of an Angular Package Format entrypoint), and the compiler needs to
22011 * import any matched directives/pipes from the same path (to the NgModule file). See README.md for
22012 * more details.
22013 */
22014 class PrivateExportAliasingHost {
22015 constructor(host) {
22016 this.host = host;
22017 /**
22018 * Under private export aliasing, the `AbsoluteModuleStrategy` used for emitting references will
22019 * will select aliased exports that it finds in the .d.ts file for an NgModule's file. Thus,
22020 * emitting these exports in .d.ts is a requirement for the `PrivateExportAliasingHost` to
22021 * function correctly.
22022 */
22023 this.aliasExportsInDts = true;
22024 }
22025 maybeAliasSymbolAs(ref, context, ngModuleName) {
22026 if (ref.hasOwningModuleGuess) {
22027 // Skip nodes that already have an associated absolute module specifier, since they can be
22028 // safely imported from that specifier.
22029 return null;
22030 }
22031 // Look for a user-provided export of `decl` in `context`. If one exists, then an alias export
22032 // is not needed.
22033 // TODO(alxhub): maybe add a host method to check for the existence of an export without going
22034 // through the entire list of exports.
22035 const exports = this.host.getExportsOfModule(context);
22036 if (exports === null) {
22037 // Something went wrong, and no exports were available at all. Bail rather than risk creating
22038 // re-exports when they're not needed.
22039 throw new Error(`Could not determine the exports of: ${context.fileName}`);
22040 }
22041 let found = false;
22042 exports.forEach(value => {
22043 if (value.node === ref.node) {
22044 found = true;
22045 }
22046 });
22047 if (found) {
22048 // The module exports the declared class directly, no alias is necessary.
22049 return null;
22050 }
22051 return `ɵngExportɵ${ngModuleName}ɵ${ref.node.name.text}`;
22052 }
22053 /**
22054 * A `PrivateExportAliasingHost` only generates re-exports and does not direct the compiler to
22055 * directly consume the aliases it creates.
22056 *
22057 * Instead, they're consumed indirectly: `AbsoluteModuleStrategy` `ReferenceEmitterStrategy` will
22058 * select these alias exports automatically when looking for an export of the directive/pipe from
22059 * the same path as the NgModule was imported.
22060 *
22061 * Thus, `getAliasIn` always returns `null`.
22062 */
22063 getAliasIn() {
22064 return null;
22065 }
22066 }
22067 /**
22068 * A `ReferenceEmitStrategy` which will consume the alias attached to a particular `Reference` to a
22069 * directive or pipe, if it exists.
22070 */
22071 class AliasStrategy {
22072 emit(ref, context, importMode) {
22073 if (importMode & ImportFlags.NoAliasing || ref.alias === null) {
22074 return null;
22075 }
22076 return { expression: ref.alias, importedFile: 'unknown' };
22077 }
22078 }
22079
22080 /**
22081 * @license
22082 * Copyright Google LLC All Rights Reserved.
22083 *
22084 * Use of this source code is governed by an MIT-style license that can be
22085 * found in the LICENSE file at https://angular.io/license
22086 */
22087 function relativePathBetween(from, to) {
22088 const relativePath = stripExtension(relative(dirname(resolve(from)), resolve(to)));
22089 return relativePath !== '' ? toRelativeImport(relativePath) : null;
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 /**
22100 * `ImportRewriter` that does no rewriting.
22101 */
22102 class NoopImportRewriter {
22103 shouldImportSymbol(symbol, specifier) {
22104 return true;
22105 }
22106 rewriteSymbol(symbol, specifier) {
22107 return symbol;
22108 }
22109 rewriteSpecifier(specifier, inContextOfFile) {
22110 return specifier;
22111 }
22112 }
22113 /**
22114 * A mapping of supported symbols that can be imported from within @angular/core, and the names by
22115 * which they're exported from r3_symbols.
22116 */
22117 const CORE_SUPPORTED_SYMBOLS = new Map([
22118 ['ɵɵdefineInjectable', 'ɵɵdefineInjectable'],
22119 ['ɵɵdefineInjector', 'ɵɵdefineInjector'],
22120 ['ɵɵdefineNgModule', 'ɵɵdefineNgModule'],
22121 ['ɵɵsetNgModuleScope', 'ɵɵsetNgModuleScope'],
22122 ['ɵɵinject', 'ɵɵinject'],
22123 ['ɵɵFactoryDeclaration', 'ɵɵFactoryDeclaration'],
22124 ['ɵsetClassMetadata', 'setClassMetadata'],
22125 ['ɵɵInjectableDeclaration', 'ɵɵInjectableDeclaration'],
22126 ['ɵɵInjectorDeclaration', 'ɵɵInjectorDeclaration'],
22127 ['ɵɵNgModuleDeclaration', 'ɵɵNgModuleDeclaration'],
22128 ['ɵNgModuleFactory', 'NgModuleFactory'],
22129 ['ɵnoSideEffects', 'ɵnoSideEffects'],
22130 ]);
22131 const CORE_MODULE = '@angular/core';
22132 /**
22133 * `ImportRewriter` that rewrites imports from '@angular/core' to be imported from the r3_symbols.ts
22134 * file instead.
22135 */
22136 class R3SymbolsImportRewriter {
22137 constructor(r3SymbolsPath) {
22138 this.r3SymbolsPath = r3SymbolsPath;
22139 }
22140 shouldImportSymbol(symbol, specifier) {
22141 return true;
22142 }
22143 rewriteSymbol(symbol, specifier) {
22144 if (specifier !== CORE_MODULE) {
22145 // This import isn't from core, so ignore it.
22146 return symbol;
22147 }
22148 return validateAndRewriteCoreSymbol(symbol);
22149 }
22150 rewriteSpecifier(specifier, inContextOfFile) {
22151 if (specifier !== CORE_MODULE) {
22152 // This module isn't core, so ignore it.
22153 return specifier;
22154 }
22155 const relativePathToR3Symbols = relativePathBetween(inContextOfFile, this.r3SymbolsPath);
22156 if (relativePathToR3Symbols === null) {
22157 throw new Error(`Failed to rewrite import inside ${CORE_MODULE}: ${inContextOfFile} -> ${this.r3SymbolsPath}`);
22158 }
22159 return relativePathToR3Symbols;
22160 }
22161 }
22162 function validateAndRewriteCoreSymbol(name) {
22163 if (!CORE_SUPPORTED_SYMBOLS.has(name)) {
22164 throw new Error(`Importing unexpected symbol ${name} while compiling ${CORE_MODULE}`);
22165 }
22166 return CORE_SUPPORTED_SYMBOLS.get(name);
22167 }
22168
22169 /**
22170 * @license
22171 * Copyright Google LLC All Rights Reserved.
22172 *
22173 * Use of this source code is governed by an MIT-style license that can be
22174 * found in the LICENSE file at https://angular.io/license
22175 */
22176 const DefaultImportDeclaration = Symbol('DefaultImportDeclaration');
22177 /**
22178 * Attaches a default import declaration to `expr` to indicate the dependency of `expr` on the
22179 * default import.
22180 */
22181 function attachDefaultImportDeclaration(expr, importDecl) {
22182 expr[DefaultImportDeclaration] = importDecl;
22183 }
22184 /**
22185 * Obtains the default import declaration that `expr` depends on, or `null` if there is no such
22186 * dependency.
22187 */
22188 function getDefaultImportDeclaration(expr) {
22189 return expr[DefaultImportDeclaration] ?? null;
22190 }
22191 /**
22192 * TypeScript has trouble with generating default imports inside of transformers for some module
22193 * formats. The issue is that for the statement:
22194 *
22195 * import X from 'some/module';
22196 * console.log(X);
22197 *
22198 * TypeScript will not use the "X" name in generated code. For normal user code, this is fine
22199 * because references to X will also be renamed. However, if both the import and any references are
22200 * added in a transformer, TypeScript does not associate the two, and will leave the "X" references
22201 * dangling while renaming the import variable. The generated code looks something like:
22202 *
22203 * const module_1 = require('some/module');
22204 * console.log(X); // now X is a dangling reference.
22205 *
22206 * Therefore, we cannot synthetically add default imports, and must reuse the imports that users
22207 * include. Doing this poses a challenge for imports that are only consumed in the type position in
22208 * the user's code. If Angular reuses the imported symbol in a value position (for example, we
22209 * see a constructor parameter of type Foo and try to write "inject(Foo)") we will also end up with
22210 * a dangling reference, as TS will elide the import because it was only used in the type position
22211 * originally.
22212 *
22213 * To avoid this, the compiler must "touch" the imports with `ts.getMutableClone`, and should
22214 * only do this for imports which are actually consumed. The `DefaultImportTracker` keeps track of
22215 * these imports as they're encountered and emitted, and implements a transform which can correctly
22216 * flag the imports as required.
22217 *
22218 * This problem does not exist for non-default imports as the compiler can easily insert
22219 * "import * as X" style imports for those, and the "X" identifier survives transformation.
22220 */
22221 class DefaultImportTracker {
22222 constructor() {
22223 /**
22224 * A `Map` which tracks the `Set` of `ts.ImportDeclaration`s for default imports that were used in
22225 * a given `ts.SourceFile` and need to be preserved.
22226 */
22227 this.sourceFileToUsedImports = new Map();
22228 }
22229 recordUsedImport(importDecl) {
22230 const sf = getSourceFile(importDecl);
22231 // Add the default import declaration to the set of used import declarations for the file.
22232 if (!this.sourceFileToUsedImports.has(sf)) {
22233 this.sourceFileToUsedImports.set(sf, new Set());
22234 }
22235 this.sourceFileToUsedImports.get(sf).add(importDecl);
22236 }
22237 /**
22238 * Get a `ts.TransformerFactory` which will preserve default imports that were previously marked
22239 * as used.
22240 *
22241 * This transformer must run after any other transformers which call `recordUsedImport`.
22242 */
22243 importPreservingTransformer() {
22244 return (context) => {
22245 return (sf) => {
22246 return this.transformSourceFile(sf);
22247 };
22248 };
22249 }
22250 /**
22251 * Process a `ts.SourceFile` and replace any `ts.ImportDeclaration`s.
22252 */
22253 transformSourceFile(sf) {
22254 const originalSf = ts__default["default"].getOriginalNode(sf);
22255 // Take a fast path if no import declarations need to be preserved in the file.
22256 if (!this.sourceFileToUsedImports.has(originalSf)) {
22257 return sf;
22258 }
22259 // There are declarations that need to be preserved.
22260 const importsToPreserve = this.sourceFileToUsedImports.get(originalSf);
22261 // Generate a new statement list which preserves any imports present in `importsToPreserve`.
22262 const statements = sf.statements.map(stmt => {
22263 if (ts__default["default"].isImportDeclaration(stmt) && importsToPreserve.has(stmt)) {
22264 // Preserving an import that's marked as unreferenced (type-only) is tricky in TypeScript.
22265 //
22266 // Various approaches have been tried, with mixed success:
22267 //
22268 // 1. Using `ts.updateImportDeclaration` does not cause the import to be retained.
22269 //
22270 // 2. Using `ts.createImportDeclaration` with the same `ts.ImportClause` causes the import
22271 // to correctly be retained, but when emitting CommonJS module format code, references
22272 // to the imported value will not match the import variable.
22273 //
22274 // 3. Emitting "import * as" imports instead generates the correct import variable, but
22275 // references are missing the ".default" access. This happens to work for tsickle code
22276 // with goog.module transformations as tsickle strips the ".default" anyway.
22277 //
22278 // 4. It's possible to trick TypeScript by setting `ts.NodeFlag.Synthesized` on the import
22279 // declaration. This causes the import to be correctly retained and generated, but can
22280 // violate invariants elsewhere in the compiler and cause crashes.
22281 //
22282 // 5. Using `ts.getMutableClone` seems to correctly preserve the import and correctly
22283 // generate references to the import variable across all module types.
22284 //
22285 // Therefore, option 5 is the one used here. It seems to be implemented as the correct way
22286 // to perform option 4, which preserves all the compiler's invariants.
22287 //
22288 // TODO(alxhub): discuss with the TypeScript team and determine if there's a better way to
22289 // deal with this issue.
22290 stmt = ts__default["default"].getMutableClone(stmt);
22291 }
22292 return stmt;
22293 });
22294 // Save memory - there's no need to keep these around once the transform has run for the given
22295 // file.
22296 this.sourceFileToUsedImports.delete(originalSf);
22297 return ts__default["default"].updateSourceFileNode(sf, statements);
22298 }
22299 }
22300
22301 /**
22302 * @license
22303 * Copyright Google LLC All Rights Reserved.
22304 *
22305 * Use of this source code is governed by an MIT-style license that can be
22306 * found in the LICENSE file at https://angular.io/license
22307 */
22308 /**
22309 * A `ts.Node` plus the context in which it was discovered.
22310 *
22311 * A `Reference` is a pointer to a `ts.Node` that was extracted from the program somehow. It
22312 * contains not only the node itself, but the information regarding how the node was located. In
22313 * particular, it might track different identifiers by which the node is exposed, as well as
22314 * potentially a module specifier which might expose the node.
22315 *
22316 * The Angular compiler uses `Reference`s instead of `ts.Node`s when tracking classes or generating
22317 * imports.
22318 */
22319 class Reference {
22320 constructor(node, bestGuessOwningModule = null) {
22321 this.node = node;
22322 this.identifiers = [];
22323 /**
22324 * Indicates that the Reference was created synthetically, not as a result of natural value
22325 * resolution.
22326 *
22327 * This is used to avoid misinterpreting the Reference in certain contexts.
22328 */
22329 this.synthetic = false;
22330 this._alias = null;
22331 this.bestGuessOwningModule = bestGuessOwningModule;
22332 const id = identifierOfNode(node);
22333 if (id !== null) {
22334 this.identifiers.push(id);
22335 }
22336 }
22337 /**
22338 * The best guess at which module specifier owns this particular reference, or `null` if there
22339 * isn't one.
22340 */
22341 get ownedByModuleGuess() {
22342 if (this.bestGuessOwningModule !== null) {
22343 return this.bestGuessOwningModule.specifier;
22344 }
22345 else {
22346 return null;
22347 }
22348 }
22349 /**
22350 * Whether this reference has a potential owning module or not.
22351 *
22352 * See `bestGuessOwningModule`.
22353 */
22354 get hasOwningModuleGuess() {
22355 return this.bestGuessOwningModule !== null;
22356 }
22357 /**
22358 * A name for the node, if one is available.
22359 *
22360 * This is only suited for debugging. Any actual references to this node should be made with
22361 * `ts.Identifier`s (see `getIdentityIn`).
22362 */
22363 get debugName() {
22364 const id = identifierOfNode(this.node);
22365 return id !== null ? id.text : null;
22366 }
22367 get alias() {
22368 return this._alias;
22369 }
22370 /**
22371 * Record a `ts.Identifier` by which it's valid to refer to this node, within the context of this
22372 * `Reference`.
22373 */
22374 addIdentifier(identifier) {
22375 this.identifiers.push(identifier);
22376 }
22377 /**
22378 * Get a `ts.Identifier` within this `Reference` that can be used to refer within the context of a
22379 * given `ts.SourceFile`, if any.
22380 */
22381 getIdentityIn(context) {
22382 return this.identifiers.find(id => id.getSourceFile() === context) || null;
22383 }
22384 /**
22385 * Get a `ts.Identifier` for this `Reference` that exists within the given expression.
22386 *
22387 * This is very useful for producing `ts.Diagnostic`s that reference `Reference`s that were
22388 * extracted from some larger expression, as it can be used to pinpoint the `ts.Identifier` within
22389 * the expression from which the `Reference` originated.
22390 */
22391 getIdentityInExpression(expr) {
22392 const sf = expr.getSourceFile();
22393 return this.identifiers.find(id => {
22394 if (id.getSourceFile() !== sf) {
22395 return false;
22396 }
22397 // This identifier is a match if its position lies within the given expression.
22398 return id.pos >= expr.pos && id.end <= expr.end;
22399 }) ||
22400 null;
22401 }
22402 /**
22403 * Given the 'container' expression from which this `Reference` was extracted, produce a
22404 * `ts.Expression` to use in a diagnostic which best indicates the position within the container
22405 * expression that generated the `Reference`.
22406 *
22407 * For example, given a `Reference` to the class 'Bar' and the containing expression:
22408 * `[Foo, Bar, Baz]`, this function would attempt to return the `ts.Identifier` for `Bar` within
22409 * the array. This could be used to produce a nice diagnostic context:
22410 *
22411 * ```text
22412 * [Foo, Bar, Baz]
22413 * ~~~
22414 * ```
22415 *
22416 * If no specific node can be found, then the `fallback` expression is used, which defaults to the
22417 * entire containing expression.
22418 */
22419 getOriginForDiagnostics(container, fallback = container) {
22420 const id = this.getIdentityInExpression(container);
22421 return id !== null ? id : fallback;
22422 }
22423 cloneWithAlias(alias) {
22424 const ref = new Reference(this.node, this.bestGuessOwningModule);
22425 ref.identifiers = [...this.identifiers];
22426 ref._alias = alias;
22427 return ref;
22428 }
22429 cloneWithNoIdentifiers() {
22430 const ref = new Reference(this.node, this.bestGuessOwningModule);
22431 ref._alias = this._alias;
22432 ref.identifiers = [];
22433 return ref;
22434 }
22435 }
22436
22437 /**
22438 * Used by `RouterEntryPointManager` and `NgModuleRouteAnalyzer` (which is in turn is used by
22439 * `NgModuleDecoratorHandler`) for resolving the module source-files references in lazy-loaded
22440 * routes (relative to the source-file containing the `NgModule` that provides the route
22441 * definitions).
22442 */
22443 class ModuleResolver {
22444 constructor(program, compilerOptions, host, moduleResolutionCache) {
22445 this.program = program;
22446 this.compilerOptions = compilerOptions;
22447 this.host = host;
22448 this.moduleResolutionCache = moduleResolutionCache;
22449 }
22450 resolveModule(moduleName, containingFile) {
22451 const resolved = resolveModuleName(moduleName, containingFile, this.compilerOptions, this.host, this.moduleResolutionCache);
22452 if (resolved === undefined) {
22453 return null;
22454 }
22455 return getSourceFileOrNull(this.program, absoluteFrom(resolved.resolvedFileName));
22456 }
22457 }
22458
22459 /**
22460 * @license
22461 * Copyright Google LLC All Rights Reserved.
22462 *
22463 * Use of this source code is governed by an MIT-style license that can be
22464 * found in the LICENSE file at https://angular.io/license
22465 */
22466 /**
22467 * Represents a symbol that is recognizable across incremental rebuilds, which enables the captured
22468 * metadata to be compared to the prior compilation. This allows for semantic understanding of
22469 * the changes that have been made in a rebuild, which potentially enables more reuse of work
22470 * from the prior compilation.
22471 */
22472 class SemanticSymbol {
22473 constructor(
22474 /**
22475 * The declaration for this symbol.
22476 */
22477 decl) {
22478 this.decl = decl;
22479 this.path = absoluteFromSourceFile(decl.getSourceFile());
22480 this.identifier = getSymbolIdentifier(decl);
22481 }
22482 }
22483 function getSymbolIdentifier(decl) {
22484 if (!ts__default["default"].isSourceFile(decl.parent)) {
22485 return null;
22486 }
22487 // If this is a top-level class declaration, the class name is used as unique identifier.
22488 // Other scenarios are currently not supported and causes the symbol not to be identified
22489 // across rebuilds, unless the declaration node has not changed.
22490 return decl.name.text;
22491 }
22492
22493 /**
22494 * @license
22495 * Copyright Google LLC All Rights Reserved.
22496 *
22497 * Use of this source code is governed by an MIT-style license that can be
22498 * found in the LICENSE file at https://angular.io/license
22499 */
22500 /**
22501 * Represents a declaration for which no semantic symbol has been registered. For example,
22502 * declarations from external dependencies have not been explicitly registered and are represented
22503 * by this symbol. This allows the unresolved symbol to still be compared to a symbol from a prior
22504 * compilation.
22505 */
22506 class OpaqueSymbol extends SemanticSymbol {
22507 isPublicApiAffected() {
22508 return false;
22509 }
22510 isTypeCheckApiAffected() {
22511 return false;
22512 }
22513 }
22514 /**
22515 * The semantic dependency graph of a single compilation.
22516 */
22517 class SemanticDepGraph {
22518 constructor() {
22519 this.files = new Map();
22520 this.symbolByDecl = new Map();
22521 }
22522 /**
22523 * Registers a symbol in the graph. The symbol is given a unique identifier if possible, such that
22524 * its equivalent symbol can be obtained from a prior graph even if its declaration node has
22525 * changed across rebuilds. Symbols without an identifier are only able to find themselves in a
22526 * prior graph if their declaration node is identical.
22527 */
22528 registerSymbol(symbol) {
22529 this.symbolByDecl.set(symbol.decl, symbol);
22530 if (symbol.identifier !== null) {
22531 // If the symbol has a unique identifier, record it in the file that declares it. This enables
22532 // the symbol to be requested by its unique name.
22533 if (!this.files.has(symbol.path)) {
22534 this.files.set(symbol.path, new Map());
22535 }
22536 this.files.get(symbol.path).set(symbol.identifier, symbol);
22537 }
22538 }
22539 /**
22540 * Attempts to resolve a symbol in this graph that represents the given symbol from another graph.
22541 * If no matching symbol could be found, null is returned.
22542 *
22543 * @param symbol The symbol from another graph for which its equivalent in this graph should be
22544 * found.
22545 */
22546 getEquivalentSymbol(symbol) {
22547 // First lookup the symbol by its declaration. It is typical for the declaration to not have
22548 // changed across rebuilds, so this is likely to find the symbol. Using the declaration also
22549 // allows to diff symbols for which no unique identifier could be determined.
22550 let previousSymbol = this.getSymbolByDecl(symbol.decl);
22551 if (previousSymbol === null && symbol.identifier !== null) {
22552 // The declaration could not be resolved to a symbol in a prior compilation, which may
22553 // happen because the file containing the declaration has changed. In that case we want to
22554 // lookup the symbol based on its unique identifier, as that allows us to still compare the
22555 // changed declaration to the prior compilation.
22556 previousSymbol = this.getSymbolByName(symbol.path, symbol.identifier);
22557 }
22558 return previousSymbol;
22559 }
22560 /**
22561 * Attempts to find the symbol by its identifier.
22562 */
22563 getSymbolByName(path, identifier) {
22564 if (!this.files.has(path)) {
22565 return null;
22566 }
22567 const file = this.files.get(path);
22568 if (!file.has(identifier)) {
22569 return null;
22570 }
22571 return file.get(identifier);
22572 }
22573 /**
22574 * Attempts to resolve the declaration to its semantic symbol.
22575 */
22576 getSymbolByDecl(decl) {
22577 if (!this.symbolByDecl.has(decl)) {
22578 return null;
22579 }
22580 return this.symbolByDecl.get(decl);
22581 }
22582 }
22583 /**
22584 * Implements the logic to go from a previous dependency graph to a new one, along with information
22585 * on which files have been affected.
22586 */
22587 class SemanticDepGraphUpdater {
22588 constructor(
22589 /**
22590 * The semantic dependency graph of the most recently succeeded compilation, or null if this
22591 * is the initial build.
22592 */
22593 priorGraph) {
22594 this.priorGraph = priorGraph;
22595 this.newGraph = new SemanticDepGraph();
22596 /**
22597 * Contains opaque symbols that were created for declarations for which there was no symbol
22598 * registered, which happens for e.g. external declarations.
22599 */
22600 this.opaqueSymbols = new Map();
22601 }
22602 /**
22603 * Registers the symbol in the new graph that is being created.
22604 */
22605 registerSymbol(symbol) {
22606 this.newGraph.registerSymbol(symbol);
22607 }
22608 /**
22609 * Takes all facts that have been gathered to create a new semantic dependency graph. In this
22610 * process, the semantic impact of the changes is determined which results in a set of files that
22611 * need to be emitted and/or type-checked.
22612 */
22613 finalize() {
22614 if (this.priorGraph === null) {
22615 // If no prior dependency graph is available then this was the initial build, in which case
22616 // we don't need to determine the semantic impact as everything is already considered
22617 // logically changed.
22618 return {
22619 needsEmit: new Set(),
22620 needsTypeCheckEmit: new Set(),
22621 newGraph: this.newGraph,
22622 };
22623 }
22624 const needsEmit = this.determineInvalidatedFiles(this.priorGraph);
22625 const needsTypeCheckEmit = this.determineInvalidatedTypeCheckFiles(this.priorGraph);
22626 return {
22627 needsEmit,
22628 needsTypeCheckEmit,
22629 newGraph: this.newGraph,
22630 };
22631 }
22632 determineInvalidatedFiles(priorGraph) {
22633 const isPublicApiAffected = new Set();
22634 // The first phase is to collect all symbols which have their public API affected. Any symbols
22635 // that cannot be matched up with a symbol from the prior graph are considered affected.
22636 for (const symbol of this.newGraph.symbolByDecl.values()) {
22637 const previousSymbol = priorGraph.getEquivalentSymbol(symbol);
22638 if (previousSymbol === null || symbol.isPublicApiAffected(previousSymbol)) {
22639 isPublicApiAffected.add(symbol);
22640 }
22641 }
22642 // The second phase is to find all symbols for which the emit result is affected, either because
22643 // their used declarations have changed or any of those used declarations has had its public API
22644 // affected as determined in the first phase.
22645 const needsEmit = new Set();
22646 for (const symbol of this.newGraph.symbolByDecl.values()) {
22647 if (symbol.isEmitAffected === undefined) {
22648 continue;
22649 }
22650 const previousSymbol = priorGraph.getEquivalentSymbol(symbol);
22651 if (previousSymbol === null || symbol.isEmitAffected(previousSymbol, isPublicApiAffected)) {
22652 needsEmit.add(symbol.path);
22653 }
22654 }
22655 return needsEmit;
22656 }
22657 determineInvalidatedTypeCheckFiles(priorGraph) {
22658 const isTypeCheckApiAffected = new Set();
22659 // The first phase is to collect all symbols which have their public API affected. Any symbols
22660 // that cannot be matched up with a symbol from the prior graph are considered affected.
22661 for (const symbol of this.newGraph.symbolByDecl.values()) {
22662 const previousSymbol = priorGraph.getEquivalentSymbol(symbol);
22663 if (previousSymbol === null || symbol.isTypeCheckApiAffected(previousSymbol)) {
22664 isTypeCheckApiAffected.add(symbol);
22665 }
22666 }
22667 // The second phase is to find all symbols for which the emit result is affected, either because
22668 // their used declarations have changed or any of those used declarations has had its public API
22669 // affected as determined in the first phase.
22670 const needsTypeCheckEmit = new Set();
22671 for (const symbol of this.newGraph.symbolByDecl.values()) {
22672 if (symbol.isTypeCheckBlockAffected === undefined) {
22673 continue;
22674 }
22675 const previousSymbol = priorGraph.getEquivalentSymbol(symbol);
22676 if (previousSymbol === null ||
22677 symbol.isTypeCheckBlockAffected(previousSymbol, isTypeCheckApiAffected)) {
22678 needsTypeCheckEmit.add(symbol.path);
22679 }
22680 }
22681 return needsTypeCheckEmit;
22682 }
22683 /**
22684 * Creates a `SemanticReference` for the reference to `decl` using the expression `expr`. See
22685 * the documentation of `SemanticReference` for details.
22686 */
22687 getSemanticReference(decl, expr) {
22688 return {
22689 symbol: this.getSymbol(decl),
22690 importPath: getImportPath(expr),
22691 };
22692 }
22693 /**
22694 * Gets the `SemanticSymbol` that was registered for `decl` during the current compilation, or
22695 * returns an opaque symbol that represents `decl`.
22696 */
22697 getSymbol(decl) {
22698 const symbol = this.newGraph.getSymbolByDecl(decl);
22699 if (symbol === null) {
22700 // No symbol has been recorded for the provided declaration, which would be the case if the
22701 // declaration is external. Return an opaque symbol in that case, to allow the external
22702 // declaration to be compared to a prior compilation.
22703 return this.getOpaqueSymbol(decl);
22704 }
22705 return symbol;
22706 }
22707 /**
22708 * Gets or creates an `OpaqueSymbol` for the provided class declaration.
22709 */
22710 getOpaqueSymbol(decl) {
22711 if (this.opaqueSymbols.has(decl)) {
22712 return this.opaqueSymbols.get(decl);
22713 }
22714 const symbol = new OpaqueSymbol(decl);
22715 this.opaqueSymbols.set(decl, symbol);
22716 return symbol;
22717 }
22718 }
22719 function getImportPath(expr) {
22720 if (expr instanceof ExternalExpr) {
22721 return `${expr.value.moduleName}\$${expr.value.name}`;
22722 }
22723 else {
22724 return null;
22725 }
22726 }
22727
22728 /**
22729 * Determines whether the provided symbols represent the same declaration.
22730 */
22731 function isSymbolEqual(a, b) {
22732 if (a.decl === b.decl) {
22733 // If the declaration is identical then it must represent the same symbol.
22734 return true;
22735 }
22736 if (a.identifier === null || b.identifier === null) {
22737 // Unidentifiable symbols are assumed to be different.
22738 return false;
22739 }
22740 return a.path === b.path && a.identifier === b.identifier;
22741 }
22742 /**
22743 * Determines whether the provided references to a semantic symbol are still equal, i.e. represent
22744 * the same symbol and are imported by the same path.
22745 */
22746 function isReferenceEqual(a, b) {
22747 if (!isSymbolEqual(a.symbol, b.symbol)) {
22748 // If the reference's target symbols are different, the reference itself is different.
22749 return false;
22750 }
22751 // The reference still corresponds with the same symbol, now check that the path by which it is
22752 // imported has not changed.
22753 return a.importPath === b.importPath;
22754 }
22755 function referenceEquality(a, b) {
22756 return a === b;
22757 }
22758 /**
22759 * Determines if the provided arrays are equal to each other, using the provided equality tester
22760 * that is called for all entries in the array.
22761 */
22762 function isArrayEqual(a, b, equalityTester = referenceEquality) {
22763 if (a === null || b === null) {
22764 return a === b;
22765 }
22766 if (a.length !== b.length) {
22767 return false;
22768 }
22769 return !a.some((item, index) => !equalityTester(item, b[index]));
22770 }
22771 /**
22772 * Determines if the provided sets are equal to each other, using the provided equality tester.
22773 * Sets that only differ in ordering are considered equal.
22774 */
22775 function isSetEqual(a, b, equalityTester = referenceEquality) {
22776 if (a === null || b === null) {
22777 return a === b;
22778 }
22779 if (a.size !== b.size) {
22780 return false;
22781 }
22782 for (const itemA of a) {
22783 let found = false;
22784 for (const itemB of b) {
22785 if (equalityTester(itemA, itemB)) {
22786 found = true;
22787 break;
22788 }
22789 }
22790 if (!found) {
22791 return false;
22792 }
22793 }
22794 return true;
22795 }
22796
22797 /**
22798 * @license
22799 * Copyright Google LLC All Rights Reserved.
22800 *
22801 * Use of this source code is governed by an MIT-style license that can be
22802 * found in the LICENSE file at https://angular.io/license
22803 */
22804 /**
22805 * Converts the type parameters of the given class into their semantic representation. If the class
22806 * does not have any type parameters, then `null` is returned.
22807 */
22808 function extractSemanticTypeParameters(node) {
22809 if (!ts__default["default"].isClassDeclaration(node) || node.typeParameters === undefined) {
22810 return null;
22811 }
22812 return node.typeParameters.map(typeParam => ({ hasGenericTypeBound: typeParam.constraint !== undefined }));
22813 }
22814 /**
22815 * Compares the list of type parameters to determine if they can be considered equal.
22816 */
22817 function areTypeParametersEqual(current, previous) {
22818 // First compare all type parameters one-to-one; any differences mean that the list of type
22819 // parameters has changed.
22820 if (!isArrayEqual(current, previous, isTypeParameterEqual)) {
22821 return false;
22822 }
22823 // If there is a current list of type parameters and if any of them has a generic type constraint,
22824 // then the meaning of that type parameter may have changed without us being aware; as such we
22825 // have to assume that the type parameters have in fact changed.
22826 if (current !== null && current.some(typeParam => typeParam.hasGenericTypeBound)) {
22827 return false;
22828 }
22829 return true;
22830 }
22831 function isTypeParameterEqual(a, b) {
22832 return a.hasGenericTypeBound === b.hasGenericTypeBound;
22833 }
22834
22835 /**
22836 * @license
22837 * Copyright Google LLC All Rights Reserved.
22838 *
22839 * Use of this source code is governed by an MIT-style license that can be
22840 * found in the LICENSE file at https://angular.io/license
22841 */
22842 var MetaType;
22843 (function (MetaType) {
22844 MetaType[MetaType["Pipe"] = 0] = "Pipe";
22845 MetaType[MetaType["Directive"] = 1] = "Directive";
22846 })(MetaType || (MetaType = {}));
22847
22848 /**
22849 * @license
22850 * Copyright Google LLC All Rights Reserved.
22851 *
22852 * Use of this source code is governed by an MIT-style license that can be
22853 * found in the LICENSE file at https://angular.io/license
22854 */
22855 const Decorator = {
22856 nodeForError: (decorator) => {
22857 if (decorator.node !== null) {
22858 return decorator.node;
22859 }
22860 else {
22861 // TODO(alxhub): we can't rely on narrowing until TS 3.6 is in g3.
22862 return decorator.synthesizedFor;
22863 }
22864 },
22865 };
22866 function isDecoratorIdentifier(exp) {
22867 return ts__default["default"].isIdentifier(exp) ||
22868 ts__default["default"].isPropertyAccessExpression(exp) && ts__default["default"].isIdentifier(exp.expression) &&
22869 ts__default["default"].isIdentifier(exp.name);
22870 }
22871 /**
22872 * An enumeration of possible kinds of class members.
22873 */
22874 var ClassMemberKind;
22875 (function (ClassMemberKind) {
22876 ClassMemberKind[ClassMemberKind["Constructor"] = 0] = "Constructor";
22877 ClassMemberKind[ClassMemberKind["Getter"] = 1] = "Getter";
22878 ClassMemberKind[ClassMemberKind["Setter"] = 2] = "Setter";
22879 ClassMemberKind[ClassMemberKind["Property"] = 3] = "Property";
22880 ClassMemberKind[ClassMemberKind["Method"] = 4] = "Method";
22881 })(ClassMemberKind || (ClassMemberKind = {}));
22882 /**
22883 * Possible declarations of known values, such as built-in objects/functions or TypeScript helpers.
22884 */
22885 var KnownDeclaration;
22886 (function (KnownDeclaration) {
22887 /**
22888 * Indicates the JavaScript global `Object` class.
22889 */
22890 KnownDeclaration[KnownDeclaration["JsGlobalObject"] = 0] = "JsGlobalObject";
22891 /**
22892 * Indicates the `__assign` TypeScript helper function.
22893 */
22894 KnownDeclaration[KnownDeclaration["TsHelperAssign"] = 1] = "TsHelperAssign";
22895 /**
22896 * Indicates the `__spread` TypeScript helper function.
22897 */
22898 KnownDeclaration[KnownDeclaration["TsHelperSpread"] = 2] = "TsHelperSpread";
22899 /**
22900 * Indicates the `__spreadArrays` TypeScript helper function.
22901 */
22902 KnownDeclaration[KnownDeclaration["TsHelperSpreadArrays"] = 3] = "TsHelperSpreadArrays";
22903 /**
22904 * Indicates the `__spreadArray` TypeScript helper function.
22905 */
22906 KnownDeclaration[KnownDeclaration["TsHelperSpreadArray"] = 4] = "TsHelperSpreadArray";
22907 /**
22908 * Indicates the `__read` TypeScript helper function.
22909 */
22910 KnownDeclaration[KnownDeclaration["TsHelperRead"] = 5] = "TsHelperRead";
22911 })(KnownDeclaration || (KnownDeclaration = {}));
22912 /**
22913 * Returns true if the `decl` is a `ConcreteDeclaration` (ie. that its `node` property is a
22914 * `ts.Declaration`).
22915 */
22916 function isConcreteDeclaration(decl) {
22917 return decl.kind === 0 /* Concrete */;
22918 }
22919
22920 /**
22921 * @license
22922 * Copyright Google LLC All Rights Reserved.
22923 *
22924 * Use of this source code is governed by an MIT-style license that can be
22925 * found in the LICENSE file at https://angular.io/license
22926 */
22927 /**
22928 * Potentially convert a `ts.TypeNode` to a `TypeValueReference`, which indicates how to use the
22929 * type given in the `ts.TypeNode` in a value position.
22930 *
22931 * This can return `null` if the `typeNode` is `null`, if it does not refer to a symbol with a value
22932 * declaration, or if it is not possible to statically understand.
22933 */
22934 function typeToValue(typeNode, checker) {
22935 // It's not possible to get a value expression if the parameter doesn't even have a type.
22936 if (typeNode === null) {
22937 return missingType();
22938 }
22939 if (!ts__default["default"].isTypeReferenceNode(typeNode)) {
22940 return unsupportedType(typeNode);
22941 }
22942 const symbols = resolveTypeSymbols(typeNode, checker);
22943 if (symbols === null) {
22944 return unknownReference(typeNode);
22945 }
22946 const { local, decl } = symbols;
22947 // It's only valid to convert a type reference to a value reference if the type actually
22948 // has a value declaration associated with it. Note that const enums are an exception,
22949 // because while they do have a value declaration, they don't exist at runtime.
22950 if (decl.valueDeclaration === undefined || decl.flags & ts__default["default"].SymbolFlags.ConstEnum) {
22951 let typeOnlyDecl = null;
22952 if (decl.declarations !== undefined && decl.declarations.length > 0) {
22953 typeOnlyDecl = decl.declarations[0];
22954 }
22955 return noValueDeclaration(typeNode, typeOnlyDecl);
22956 }
22957 // The type points to a valid value declaration. Rewrite the TypeReference into an
22958 // Expression which references the value pointed to by the TypeReference, if possible.
22959 // Look at the local `ts.Symbol`'s declarations and see if it comes from an import
22960 // statement. If so, extract the module specifier and the name of the imported type.
22961 const firstDecl = local.declarations && local.declarations[0];
22962 if (firstDecl !== undefined) {
22963 if (ts__default["default"].isImportClause(firstDecl) && firstDecl.name !== undefined) {
22964 // This is a default import.
22965 // import Foo from 'foo';
22966 if (firstDecl.isTypeOnly) {
22967 // Type-only imports cannot be represented as value.
22968 return typeOnlyImport(typeNode, firstDecl);
22969 }
22970 return {
22971 kind: 0 /* LOCAL */,
22972 expression: firstDecl.name,
22973 defaultImportStatement: firstDecl.parent,
22974 };
22975 }
22976 else if (ts__default["default"].isImportSpecifier(firstDecl)) {
22977 // The symbol was imported by name
22978 // import {Foo} from 'foo';
22979 // or
22980 // import {Foo as Bar} from 'foo';
22981 if (firstDecl.parent.parent.isTypeOnly) {
22982 // Type-only imports cannot be represented as value.
22983 return typeOnlyImport(typeNode, firstDecl.parent.parent);
22984 }
22985 // Determine the name to import (`Foo`) from the import specifier, as the symbol names of
22986 // the imported type could refer to a local alias (like `Bar` in the example above).
22987 const importedName = (firstDecl.propertyName || firstDecl.name).text;
22988 // The first symbol name refers to the local name, which is replaced by `importedName` above.
22989 // Any remaining symbol names make up the complete path to the value.
22990 const [_localName, ...nestedPath] = symbols.symbolNames;
22991 const moduleName = extractModuleName(firstDecl.parent.parent.parent);
22992 return {
22993 kind: 1 /* IMPORTED */,
22994 valueDeclaration: decl.valueDeclaration,
22995 moduleName,
22996 importedName,
22997 nestedPath
22998 };
22999 }
23000 else if (ts__default["default"].isNamespaceImport(firstDecl)) {
23001 // The import is a namespace import
23002 // import * as Foo from 'foo';
23003 if (firstDecl.parent.isTypeOnly) {
23004 // Type-only imports cannot be represented as value.
23005 return typeOnlyImport(typeNode, firstDecl.parent);
23006 }
23007 if (symbols.symbolNames.length === 1) {
23008 // The type refers to the namespace itself, which cannot be represented as a value.
23009 return namespaceImport(typeNode, firstDecl.parent);
23010 }
23011 // The first symbol name refers to the local name of the namespace, which is is discarded
23012 // as a new namespace import will be generated. This is followed by the symbol name that needs
23013 // to be imported and any remaining names that constitute the complete path to the value.
23014 const [_ns, importedName, ...nestedPath] = symbols.symbolNames;
23015 const moduleName = extractModuleName(firstDecl.parent.parent);
23016 return {
23017 kind: 1 /* IMPORTED */,
23018 valueDeclaration: decl.valueDeclaration,
23019 moduleName,
23020 importedName,
23021 nestedPath
23022 };
23023 }
23024 }
23025 // If the type is not imported, the type reference can be converted into an expression as is.
23026 const expression = typeNodeToValueExpr(typeNode);
23027 if (expression !== null) {
23028 return {
23029 kind: 0 /* LOCAL */,
23030 expression,
23031 defaultImportStatement: null,
23032 };
23033 }
23034 else {
23035 return unsupportedType(typeNode);
23036 }
23037 }
23038 function unsupportedType(typeNode) {
23039 return {
23040 kind: 2 /* UNAVAILABLE */,
23041 reason: { kind: 5 /* UNSUPPORTED */, typeNode },
23042 };
23043 }
23044 function noValueDeclaration(typeNode, decl) {
23045 return {
23046 kind: 2 /* UNAVAILABLE */,
23047 reason: { kind: 1 /* NO_VALUE_DECLARATION */, typeNode, decl },
23048 };
23049 }
23050 function typeOnlyImport(typeNode, importClause) {
23051 return {
23052 kind: 2 /* UNAVAILABLE */,
23053 reason: { kind: 2 /* TYPE_ONLY_IMPORT */, typeNode, importClause },
23054 };
23055 }
23056 function unknownReference(typeNode) {
23057 return {
23058 kind: 2 /* UNAVAILABLE */,
23059 reason: { kind: 3 /* UNKNOWN_REFERENCE */, typeNode },
23060 };
23061 }
23062 function namespaceImport(typeNode, importClause) {
23063 return {
23064 kind: 2 /* UNAVAILABLE */,
23065 reason: { kind: 4 /* NAMESPACE */, typeNode, importClause },
23066 };
23067 }
23068 function missingType() {
23069 return {
23070 kind: 2 /* UNAVAILABLE */,
23071 reason: { kind: 0 /* MISSING_TYPE */ },
23072 };
23073 }
23074 /**
23075 * Attempt to extract a `ts.Expression` that's equivalent to a `ts.TypeNode`, as the two have
23076 * different AST shapes but can reference the same symbols.
23077 *
23078 * This will return `null` if an equivalent expression cannot be constructed.
23079 */
23080 function typeNodeToValueExpr(node) {
23081 if (ts__default["default"].isTypeReferenceNode(node)) {
23082 return entityNameToValue(node.typeName);
23083 }
23084 else {
23085 return null;
23086 }
23087 }
23088 /**
23089 * Resolve a `TypeReference` node to the `ts.Symbol`s for both its declaration and its local source.
23090 *
23091 * In the event that the `TypeReference` refers to a locally declared symbol, these will be the
23092 * same. If the `TypeReference` refers to an imported symbol, then `decl` will be the fully resolved
23093 * `ts.Symbol` of the referenced symbol. `local` will be the `ts.Symbol` of the `ts.Identifier`
23094 * which points to the import statement by which the symbol was imported.
23095 *
23096 * All symbol names that make up the type reference are returned left-to-right into the
23097 * `symbolNames` array, which is guaranteed to include at least one entry.
23098 */
23099 function resolveTypeSymbols(typeRef, checker) {
23100 const typeName = typeRef.typeName;
23101 // typeRefSymbol is the ts.Symbol of the entire type reference.
23102 const typeRefSymbol = checker.getSymbolAtLocation(typeName);
23103 if (typeRefSymbol === undefined) {
23104 return null;
23105 }
23106 // `local` is the `ts.Symbol` for the local `ts.Identifier` for the type.
23107 // If the type is actually locally declared or is imported by name, for example:
23108 // import {Foo} from './foo';
23109 // then it'll be the same as `typeRefSymbol`.
23110 //
23111 // If the type is imported via a namespace import, for example:
23112 // import * as foo from './foo';
23113 // and then referenced as:
23114 // constructor(f: foo.Foo)
23115 // then `local` will be the `ts.Symbol` of `foo`, whereas `typeRefSymbol` will be the `ts.Symbol`
23116 // of `foo.Foo`. This allows tracking of the import behind whatever type reference exists.
23117 let local = typeRefSymbol;
23118 // Destructure a name like `foo.X.Y.Z` as follows:
23119 // - in `leftMost`, the `ts.Identifier` of the left-most name (`foo`) in the qualified name.
23120 // This identifier is used to resolve the `ts.Symbol` for `local`.
23121 // - in `symbolNames`, all names involved in the qualified path, or a single symbol name if the
23122 // type is not qualified.
23123 let leftMost = typeName;
23124 const symbolNames = [];
23125 while (ts__default["default"].isQualifiedName(leftMost)) {
23126 symbolNames.unshift(leftMost.right.text);
23127 leftMost = leftMost.left;
23128 }
23129 symbolNames.unshift(leftMost.text);
23130 if (leftMost !== typeName) {
23131 const localTmp = checker.getSymbolAtLocation(leftMost);
23132 if (localTmp !== undefined) {
23133 local = localTmp;
23134 }
23135 }
23136 // De-alias the top-level type reference symbol to get the symbol of the actual declaration.
23137 let decl = typeRefSymbol;
23138 if (typeRefSymbol.flags & ts__default["default"].SymbolFlags.Alias) {
23139 decl = checker.getAliasedSymbol(typeRefSymbol);
23140 }
23141 return { local, decl, symbolNames };
23142 }
23143 function entityNameToValue(node) {
23144 if (ts__default["default"].isQualifiedName(node)) {
23145 const left = entityNameToValue(node.left);
23146 return left !== null ? ts__default["default"].createPropertyAccess(left, node.right) : null;
23147 }
23148 else if (ts__default["default"].isIdentifier(node)) {
23149 return ts__default["default"].getMutableClone(node);
23150 }
23151 else {
23152 return null;
23153 }
23154 }
23155 function extractModuleName(node) {
23156 if (!ts__default["default"].isStringLiteral(node.moduleSpecifier)) {
23157 throw new Error('not a module specifier');
23158 }
23159 return node.moduleSpecifier.text;
23160 }
23161
23162 /**
23163 * @license
23164 * Copyright Google LLC All Rights Reserved.
23165 *
23166 * Use of this source code is governed by an MIT-style license that can be
23167 * found in the LICENSE file at https://angular.io/license
23168 */
23169 function isNamedClassDeclaration(node) {
23170 return ts__default["default"].isClassDeclaration(node) && isIdentifier(node.name);
23171 }
23172 function isIdentifier(node) {
23173 return node !== undefined && ts__default["default"].isIdentifier(node);
23174 }
23175
23176 /**
23177 * @license
23178 * Copyright Google LLC All Rights Reserved.
23179 *
23180 * Use of this source code is governed by an MIT-style license that can be
23181 * found in the LICENSE file at https://angular.io/license
23182 */
23183 /**
23184 * reflector.ts implements static reflection of declarations using the TypeScript `ts.TypeChecker`.
23185 */
23186 class TypeScriptReflectionHost {
23187 constructor(checker) {
23188 this.checker = checker;
23189 }
23190 getDecoratorsOfDeclaration(declaration) {
23191 if (declaration.decorators === undefined || declaration.decorators.length === 0) {
23192 return null;
23193 }
23194 return declaration.decorators.map(decorator => this._reflectDecorator(decorator))
23195 .filter((dec) => dec !== null);
23196 }
23197 getMembersOfClass(clazz) {
23198 const tsClazz = castDeclarationToClassOrDie(clazz);
23199 return tsClazz.members.map(member => this._reflectMember(member))
23200 .filter((member) => member !== null);
23201 }
23202 getConstructorParameters(clazz) {
23203 const tsClazz = castDeclarationToClassOrDie(clazz);
23204 const isDeclaration = tsClazz.getSourceFile().isDeclarationFile;
23205 // For non-declaration files, we want to find the constructor with a `body`. The constructors
23206 // without a `body` are overloads whereas we want the implementation since it's the one that'll
23207 // be executed and which can have decorators. For declaration files, we take the first one that
23208 // we get.
23209 const ctor = tsClazz.members.find((member) => ts__default["default"].isConstructorDeclaration(member) && (isDeclaration || member.body !== undefined));
23210 if (ctor === undefined) {
23211 return null;
23212 }
23213 return ctor.parameters.map(node => {
23214 // The name of the parameter is easy.
23215 const name = parameterName(node.name);
23216 const decorators = this.getDecoratorsOfDeclaration(node);
23217 // It may or may not be possible to write an expression that refers to the value side of the
23218 // type named for the parameter.
23219 let originalTypeNode = node.type || null;
23220 let typeNode = originalTypeNode;
23221 // Check if we are dealing with a simple nullable union type e.g. `foo: Foo|null`
23222 // and extract the type. More complex union types e.g. `foo: Foo|Bar` are not supported.
23223 // We also don't need to support `foo: Foo|undefined` because Angular's DI injects `null` for
23224 // optional tokes that don't have providers.
23225 if (typeNode && ts__default["default"].isUnionTypeNode(typeNode)) {
23226 let childTypeNodes = typeNode.types.filter(childTypeNode => !(ts__default["default"].isLiteralTypeNode(childTypeNode) &&
23227 childTypeNode.literal.kind === ts__default["default"].SyntaxKind.NullKeyword));
23228 if (childTypeNodes.length === 1) {
23229 typeNode = childTypeNodes[0];
23230 }
23231 }
23232 const typeValueReference = typeToValue(typeNode, this.checker);
23233 return {
23234 name,
23235 nameNode: node.name,
23236 typeValueReference,
23237 typeNode: originalTypeNode,
23238 decorators,
23239 };
23240 });
23241 }
23242 getImportOfIdentifier(id) {
23243 const directImport = this.getDirectImportOfIdentifier(id);
23244 if (directImport !== null) {
23245 return directImport;
23246 }
23247 else if (ts__default["default"].isQualifiedName(id.parent) && id.parent.right === id) {
23248 return this.getImportOfNamespacedIdentifier(id, getQualifiedNameRoot(id.parent));
23249 }
23250 else if (ts__default["default"].isPropertyAccessExpression(id.parent) && id.parent.name === id) {
23251 return this.getImportOfNamespacedIdentifier(id, getFarLeftIdentifier(id.parent));
23252 }
23253 else {
23254 return null;
23255 }
23256 }
23257 getExportsOfModule(node) {
23258 // In TypeScript code, modules are only ts.SourceFiles. Throw if the node isn't a module.
23259 if (!ts__default["default"].isSourceFile(node)) {
23260 throw new Error(`getExportsOfModule() called on non-SourceFile in TS code`);
23261 }
23262 // Reflect the module to a Symbol, and use getExportsOfModule() to get a list of exported
23263 // Symbols.
23264 const symbol = this.checker.getSymbolAtLocation(node);
23265 if (symbol === undefined) {
23266 return null;
23267 }
23268 const map = new Map();
23269 this.checker.getExportsOfModule(symbol).forEach(exportSymbol => {
23270 // Map each exported Symbol to a Declaration and add it to the map.
23271 const decl = this.getDeclarationOfSymbol(exportSymbol, null);
23272 if (decl !== null) {
23273 map.set(exportSymbol.name, decl);
23274 }
23275 });
23276 return map;
23277 }
23278 isClass(node) {
23279 // For our purposes, classes are "named" ts.ClassDeclarations;
23280 // (`node.name` can be undefined in unnamed default exports: `default export class { ... }`).
23281 return isNamedClassDeclaration(node);
23282 }
23283 hasBaseClass(clazz) {
23284 return this.getBaseClassExpression(clazz) !== null;
23285 }
23286 getBaseClassExpression(clazz) {
23287 if (!(ts__default["default"].isClassDeclaration(clazz) || ts__default["default"].isClassExpression(clazz)) ||
23288 clazz.heritageClauses === undefined) {
23289 return null;
23290 }
23291 const extendsClause = clazz.heritageClauses.find(clause => clause.token === ts__default["default"].SyntaxKind.ExtendsKeyword);
23292 if (extendsClause === undefined) {
23293 return null;
23294 }
23295 const extendsType = extendsClause.types[0];
23296 if (extendsType === undefined) {
23297 return null;
23298 }
23299 return extendsType.expression;
23300 }
23301 getDeclarationOfIdentifier(id) {
23302 // Resolve the identifier to a Symbol, and return the declaration of that.
23303 let symbol = this.checker.getSymbolAtLocation(id);
23304 if (symbol === undefined) {
23305 return null;
23306 }
23307 return this.getDeclarationOfSymbol(symbol, id);
23308 }
23309 getDefinitionOfFunction(node) {
23310 if (!ts__default["default"].isFunctionDeclaration(node) && !ts__default["default"].isMethodDeclaration(node) &&
23311 !ts__default["default"].isFunctionExpression(node)) {
23312 return null;
23313 }
23314 return {
23315 node,
23316 body: node.body !== undefined ? Array.from(node.body.statements) : null,
23317 parameters: node.parameters.map(param => {
23318 const name = parameterName(param.name);
23319 const initializer = param.initializer || null;
23320 return { name, node: param, initializer };
23321 }),
23322 };
23323 }
23324 getGenericArityOfClass(clazz) {
23325 if (!ts__default["default"].isClassDeclaration(clazz)) {
23326 return null;
23327 }
23328 return clazz.typeParameters !== undefined ? clazz.typeParameters.length : 0;
23329 }
23330 getVariableValue(declaration) {
23331 return declaration.initializer || null;
23332 }
23333 getDtsDeclaration(_) {
23334 return null;
23335 }
23336 getInternalNameOfClass(clazz) {
23337 return clazz.name;
23338 }
23339 getAdjacentNameOfClass(clazz) {
23340 return clazz.name;
23341 }
23342 isStaticallyExported(decl) {
23343 // First check if there's an `export` modifier directly on the declaration.
23344 let topLevel = decl;
23345 if (ts__default["default"].isVariableDeclaration(decl) && ts__default["default"].isVariableDeclarationList(decl.parent)) {
23346 topLevel = decl.parent.parent;
23347 }
23348 if (topLevel.modifiers !== undefined &&
23349 topLevel.modifiers.some(modifier => modifier.kind === ts__default["default"].SyntaxKind.ExportKeyword)) {
23350 // The node is part of a declaration that's directly exported.
23351 return true;
23352 }
23353 // If `topLevel` is not directly exported via a modifier, then it might be indirectly exported,
23354 // e.g.:
23355 //
23356 // class Foo {}
23357 // export {Foo};
23358 //
23359 // The only way to check this is to look at the module level for exports of the class. As a
23360 // performance optimization, this check is only performed if the class is actually declared at
23361 // the top level of the file and thus eligible for exporting in the first place.
23362 if (topLevel.parent === undefined || !ts__default["default"].isSourceFile(topLevel.parent)) {
23363 return false;
23364 }
23365 const localExports = this.getLocalExportedDeclarationsOfSourceFile(decl.getSourceFile());
23366 return localExports.has(decl);
23367 }
23368 getDirectImportOfIdentifier(id) {
23369 const symbol = this.checker.getSymbolAtLocation(id);
23370 if (symbol === undefined || symbol.declarations === undefined ||
23371 symbol.declarations.length !== 1) {
23372 return null;
23373 }
23374 const decl = symbol.declarations[0];
23375 const importDecl = getContainingImportDeclaration(decl);
23376 // Ignore declarations that are defined locally (not imported).
23377 if (importDecl === null) {
23378 return null;
23379 }
23380 // The module specifier is guaranteed to be a string literal, so this should always pass.
23381 if (!ts__default["default"].isStringLiteral(importDecl.moduleSpecifier)) {
23382 // Not allowed to happen in TypeScript ASTs.
23383 return null;
23384 }
23385 return { from: importDecl.moduleSpecifier.text, name: getExportedName(decl, id) };
23386 }
23387 /**
23388 * Try to get the import info for this identifier as though it is a namespaced import.
23389 *
23390 * For example, if the identifier is the `Directive` part of a qualified type chain like:
23391 *
23392 * ```
23393 * core.Directive
23394 * ```
23395 *
23396 * then it might be that `core` is a namespace import such as:
23397 *
23398 * ```
23399 * import * as core from 'tslib';
23400 * ```
23401 *
23402 * @param id the TypeScript identifier to find the import info for.
23403 * @returns The import info if this is a namespaced import or `null`.
23404 */
23405 getImportOfNamespacedIdentifier(id, namespaceIdentifier) {
23406 if (namespaceIdentifier === null) {
23407 return null;
23408 }
23409 const namespaceSymbol = this.checker.getSymbolAtLocation(namespaceIdentifier);
23410 if (!namespaceSymbol || namespaceSymbol.declarations === undefined) {
23411 return null;
23412 }
23413 const declaration = namespaceSymbol.declarations.length === 1 ? namespaceSymbol.declarations[0] : null;
23414 if (!declaration) {
23415 return null;
23416 }
23417 const namespaceDeclaration = ts__default["default"].isNamespaceImport(declaration) ? declaration : null;
23418 if (!namespaceDeclaration) {
23419 return null;
23420 }
23421 const importDeclaration = namespaceDeclaration.parent.parent;
23422 if (!ts__default["default"].isStringLiteral(importDeclaration.moduleSpecifier)) {
23423 // Should not happen as this would be invalid TypesScript
23424 return null;
23425 }
23426 return {
23427 from: importDeclaration.moduleSpecifier.text,
23428 name: id.text,
23429 };
23430 }
23431 /**
23432 * Resolve a `ts.Symbol` to its declaration, keeping track of the `viaModule` along the way.
23433 */
23434 getDeclarationOfSymbol(symbol, originalId) {
23435 // If the symbol points to a ShorthandPropertyAssignment, resolve it.
23436 let valueDeclaration = undefined;
23437 if (symbol.valueDeclaration !== undefined) {
23438 valueDeclaration = symbol.valueDeclaration;
23439 }
23440 else if (symbol.declarations !== undefined && symbol.declarations.length > 0) {
23441 valueDeclaration = symbol.declarations[0];
23442 }
23443 if (valueDeclaration !== undefined && ts__default["default"].isShorthandPropertyAssignment(valueDeclaration)) {
23444 const shorthandSymbol = this.checker.getShorthandAssignmentValueSymbol(valueDeclaration);
23445 if (shorthandSymbol === undefined) {
23446 return null;
23447 }
23448 return this.getDeclarationOfSymbol(shorthandSymbol, originalId);
23449 }
23450 else if (valueDeclaration !== undefined && ts__default["default"].isExportSpecifier(valueDeclaration)) {
23451 const targetSymbol = this.checker.getExportSpecifierLocalTargetSymbol(valueDeclaration);
23452 if (targetSymbol === undefined) {
23453 return null;
23454 }
23455 return this.getDeclarationOfSymbol(targetSymbol, originalId);
23456 }
23457 const importInfo = originalId && this.getImportOfIdentifier(originalId);
23458 const viaModule = importInfo !== null && importInfo.from !== null && !importInfo.from.startsWith('.') ?
23459 importInfo.from :
23460 null;
23461 // Now, resolve the Symbol to its declaration by following any and all aliases.
23462 while (symbol.flags & ts__default["default"].SymbolFlags.Alias) {
23463 symbol = this.checker.getAliasedSymbol(symbol);
23464 }
23465 // Look at the resolved Symbol's declarations and pick one of them to return. Value declarations
23466 // are given precedence over type declarations.
23467 if (symbol.valueDeclaration !== undefined) {
23468 return {
23469 node: symbol.valueDeclaration,
23470 known: null,
23471 viaModule,
23472 identity: null,
23473 kind: 0 /* Concrete */,
23474 };
23475 }
23476 else if (symbol.declarations !== undefined && symbol.declarations.length > 0) {
23477 return {
23478 node: symbol.declarations[0],
23479 known: null,
23480 viaModule,
23481 identity: null,
23482 kind: 0 /* Concrete */,
23483 };
23484 }
23485 else {
23486 return null;
23487 }
23488 }
23489 _reflectDecorator(node) {
23490 // Attempt to resolve the decorator expression into a reference to a concrete Identifier. The
23491 // expression may contain a call to a function which returns the decorator function, in which
23492 // case we want to return the arguments.
23493 let decoratorExpr = node.expression;
23494 let args = null;
23495 // Check for call expressions.
23496 if (ts__default["default"].isCallExpression(decoratorExpr)) {
23497 args = Array.from(decoratorExpr.arguments);
23498 decoratorExpr = decoratorExpr.expression;
23499 }
23500 // The final resolved decorator should be a `ts.Identifier` - if it's not, then something is
23501 // wrong and the decorator can't be resolved statically.
23502 if (!isDecoratorIdentifier(decoratorExpr)) {
23503 return null;
23504 }
23505 const decoratorIdentifier = ts__default["default"].isIdentifier(decoratorExpr) ? decoratorExpr : decoratorExpr.name;
23506 const importDecl = this.getImportOfIdentifier(decoratorIdentifier);
23507 return {
23508 name: decoratorIdentifier.text,
23509 identifier: decoratorExpr,
23510 import: importDecl,
23511 node,
23512 args,
23513 };
23514 }
23515 _reflectMember(node) {
23516 let kind = null;
23517 let value = null;
23518 let name = null;
23519 let nameNode = null;
23520 if (ts__default["default"].isPropertyDeclaration(node)) {
23521 kind = ClassMemberKind.Property;
23522 value = node.initializer || null;
23523 }
23524 else if (ts__default["default"].isGetAccessorDeclaration(node)) {
23525 kind = ClassMemberKind.Getter;
23526 }
23527 else if (ts__default["default"].isSetAccessorDeclaration(node)) {
23528 kind = ClassMemberKind.Setter;
23529 }
23530 else if (ts__default["default"].isMethodDeclaration(node)) {
23531 kind = ClassMemberKind.Method;
23532 }
23533 else if (ts__default["default"].isConstructorDeclaration(node)) {
23534 kind = ClassMemberKind.Constructor;
23535 }
23536 else {
23537 return null;
23538 }
23539 if (ts__default["default"].isConstructorDeclaration(node)) {
23540 name = 'constructor';
23541 }
23542 else if (ts__default["default"].isIdentifier(node.name)) {
23543 name = node.name.text;
23544 nameNode = node.name;
23545 }
23546 else if (ts__default["default"].isStringLiteral(node.name)) {
23547 name = node.name.text;
23548 nameNode = node.name;
23549 }
23550 else {
23551 return null;
23552 }
23553 const decorators = this.getDecoratorsOfDeclaration(node);
23554 const isStatic = node.modifiers !== undefined &&
23555 node.modifiers.some(mod => mod.kind === ts__default["default"].SyntaxKind.StaticKeyword);
23556 return {
23557 node,
23558 implementation: node,
23559 kind,
23560 type: node.type || null,
23561 name,
23562 nameNode,
23563 decorators,
23564 value,
23565 isStatic,
23566 };
23567 }
23568 /**
23569 * Get the set of declarations declared in `file` which are exported.
23570 */
23571 getLocalExportedDeclarationsOfSourceFile(file) {
23572 const cacheSf = file;
23573 if (cacheSf[LocalExportedDeclarations] !== undefined) {
23574 // TS does not currently narrow symbol-keyed fields, hence the non-null assert is needed.
23575 return cacheSf[LocalExportedDeclarations];
23576 }
23577 const exportSet = new Set();
23578 cacheSf[LocalExportedDeclarations] = exportSet;
23579 const sfSymbol = this.checker.getSymbolAtLocation(cacheSf);
23580 if (sfSymbol === undefined || sfSymbol.exports === undefined) {
23581 return exportSet;
23582 }
23583 // Scan the exported symbol of the `ts.SourceFile` for the original `symbol` of the class
23584 // declaration.
23585 //
23586 // Note: when checking multiple classes declared in the same file, this repeats some operations.
23587 // In theory, this could be expensive if run in the context of a massive input file (like a
23588 // large FESM in ngcc). If performance does become an issue here, it should be possible to
23589 // create a `Set<>`
23590 // Unfortunately, `ts.Iterator` doesn't implement the iterator protocol, so iteration here is
23591 // done manually.
23592 const iter = sfSymbol.exports.values();
23593 let item = iter.next();
23594 while (item.done !== true) {
23595 let exportedSymbol = item.value;
23596 // If this exported symbol comes from an `export {Foo}` statement, then the symbol is actually
23597 // for the export declaration, not the original declaration. Such a symbol will be an alias,
23598 // so unwrap aliasing if necessary.
23599 if (exportedSymbol.flags & ts__default["default"].SymbolFlags.Alias) {
23600 exportedSymbol = this.checker.getAliasedSymbol(exportedSymbol);
23601 }
23602 if (exportedSymbol.valueDeclaration !== undefined &&
23603 exportedSymbol.valueDeclaration.getSourceFile() === file) {
23604 exportSet.add(exportedSymbol.valueDeclaration);
23605 }
23606 item = iter.next();
23607 }
23608 return exportSet;
23609 }
23610 }
23611 function reflectTypeEntityToDeclaration(type, checker) {
23612 let realSymbol = checker.getSymbolAtLocation(type);
23613 if (realSymbol === undefined) {
23614 throw new Error(`Cannot resolve type entity ${type.getText()} to symbol`);
23615 }
23616 while (realSymbol.flags & ts__default["default"].SymbolFlags.Alias) {
23617 realSymbol = checker.getAliasedSymbol(realSymbol);
23618 }
23619 let node = null;
23620 if (realSymbol.valueDeclaration !== undefined) {
23621 node = realSymbol.valueDeclaration;
23622 }
23623 else if (realSymbol.declarations !== undefined && realSymbol.declarations.length === 1) {
23624 node = realSymbol.declarations[0];
23625 }
23626 else {
23627 throw new Error(`Cannot resolve type entity symbol to declaration`);
23628 }
23629 if (ts__default["default"].isQualifiedName(type)) {
23630 if (!ts__default["default"].isIdentifier(type.left)) {
23631 throw new Error(`Cannot handle qualified name with non-identifier lhs`);
23632 }
23633 const symbol = checker.getSymbolAtLocation(type.left);
23634 if (symbol === undefined || symbol.declarations === undefined ||
23635 symbol.declarations.length !== 1) {
23636 throw new Error(`Cannot resolve qualified type entity lhs to symbol`);
23637 }
23638 const decl = symbol.declarations[0];
23639 if (ts__default["default"].isNamespaceImport(decl)) {
23640 const clause = decl.parent;
23641 const importDecl = clause.parent;
23642 if (!ts__default["default"].isStringLiteral(importDecl.moduleSpecifier)) {
23643 throw new Error(`Module specifier is not a string`);
23644 }
23645 return { node, from: importDecl.moduleSpecifier.text };
23646 }
23647 else if (ts__default["default"].isModuleDeclaration(decl)) {
23648 return { node, from: null };
23649 }
23650 else {
23651 throw new Error(`Unknown import type?`);
23652 }
23653 }
23654 else {
23655 return { node, from: null };
23656 }
23657 }
23658 function filterToMembersWithDecorator(members, name, module) {
23659 return members.filter(member => !member.isStatic)
23660 .map(member => {
23661 if (member.decorators === null) {
23662 return null;
23663 }
23664 const decorators = member.decorators.filter(dec => {
23665 if (dec.import !== null) {
23666 return dec.import.name === name && (module === undefined || dec.import.from === module);
23667 }
23668 else {
23669 return dec.name === name && module === undefined;
23670 }
23671 });
23672 if (decorators.length === 0) {
23673 return null;
23674 }
23675 return { member, decorators };
23676 })
23677 .filter((value) => value !== null);
23678 }
23679 function reflectObjectLiteral(node) {
23680 const map = new Map();
23681 node.properties.forEach(prop => {
23682 if (ts__default["default"].isPropertyAssignment(prop)) {
23683 const name = propertyNameToString(prop.name);
23684 if (name === null) {
23685 return;
23686 }
23687 map.set(name, prop.initializer);
23688 }
23689 else if (ts__default["default"].isShorthandPropertyAssignment(prop)) {
23690 map.set(prop.name.text, prop.name);
23691 }
23692 else {
23693 return;
23694 }
23695 });
23696 return map;
23697 }
23698 function castDeclarationToClassOrDie(declaration) {
23699 if (!ts__default["default"].isClassDeclaration(declaration)) {
23700 throw new Error(`Reflecting on a ${ts__default["default"].SyntaxKind[declaration.kind]} instead of a ClassDeclaration.`);
23701 }
23702 return declaration;
23703 }
23704 function parameterName(name) {
23705 if (ts__default["default"].isIdentifier(name)) {
23706 return name.text;
23707 }
23708 else {
23709 return null;
23710 }
23711 }
23712 function propertyNameToString(node) {
23713 if (ts__default["default"].isIdentifier(node) || ts__default["default"].isStringLiteral(node) || ts__default["default"].isNumericLiteral(node)) {
23714 return node.text;
23715 }
23716 else {
23717 return null;
23718 }
23719 }
23720 /**
23721 * Compute the left most identifier in a qualified type chain. E.g. the `a` of `a.b.c.SomeType`.
23722 * @param qualifiedName The starting property access expression from which we want to compute
23723 * the left most identifier.
23724 * @returns the left most identifier in the chain or `null` if it is not an identifier.
23725 */
23726 function getQualifiedNameRoot(qualifiedName) {
23727 while (ts__default["default"].isQualifiedName(qualifiedName.left)) {
23728 qualifiedName = qualifiedName.left;
23729 }
23730 return ts__default["default"].isIdentifier(qualifiedName.left) ? qualifiedName.left : null;
23731 }
23732 /**
23733 * Compute the left most identifier in a property access chain. E.g. the `a` of `a.b.c.d`.
23734 * @param propertyAccess The starting property access expression from which we want to compute
23735 * the left most identifier.
23736 * @returns the left most identifier in the chain or `null` if it is not an identifier.
23737 */
23738 function getFarLeftIdentifier(propertyAccess) {
23739 while (ts__default["default"].isPropertyAccessExpression(propertyAccess.expression)) {
23740 propertyAccess = propertyAccess.expression;
23741 }
23742 return ts__default["default"].isIdentifier(propertyAccess.expression) ? propertyAccess.expression : null;
23743 }
23744 /**
23745 * Return the ImportDeclaration for the given `node` if it is either an `ImportSpecifier` or a
23746 * `NamespaceImport`. If not return `null`.
23747 */
23748 function getContainingImportDeclaration(node) {
23749 return ts__default["default"].isImportSpecifier(node) ? node.parent.parent.parent :
23750 ts__default["default"].isNamespaceImport(node) ? node.parent.parent : null;
23751 }
23752 /**
23753 * Compute the name by which the `decl` was exported, not imported.
23754 * If no such declaration can be found (e.g. it is a namespace import)
23755 * then fallback to the `originalId`.
23756 */
23757 function getExportedName(decl, originalId) {
23758 return ts__default["default"].isImportSpecifier(decl) ?
23759 (decl.propertyName !== undefined ? decl.propertyName : decl.name).text :
23760 originalId.text;
23761 }
23762 const LocalExportedDeclarations = Symbol('LocalExportedDeclarations');
23763
23764 /**
23765 * @license
23766 * Copyright Google LLC All Rights Reserved.
23767 *
23768 * Use of this source code is governed by an MIT-style license that can be
23769 * found in the LICENSE file at https://angular.io/license
23770 */
23771 /**
23772 * A mapping of component property and template binding property names, for example containing the
23773 * inputs of a particular directive or component.
23774 *
23775 * A single component property has exactly one input/output annotation (and therefore one binding
23776 * property name) associated with it, but the same binding property name may be shared across many
23777 * component property names.
23778 *
23779 * Allows bidirectional querying of the mapping - looking up all inputs/outputs with a given
23780 * property name, or mapping from a specific class property to its binding property name.
23781 */
23782 class ClassPropertyMapping {
23783 constructor(forwardMap) {
23784 this.forwardMap = forwardMap;
23785 this.reverseMap = reverseMapFromForwardMap(forwardMap);
23786 }
23787 /**
23788 * Construct a `ClassPropertyMapping` with no entries.
23789 */
23790 static empty() {
23791 return new ClassPropertyMapping(new Map());
23792 }
23793 /**
23794 * Construct a `ClassPropertyMapping` from a primitive JS object which maps class property names
23795 * to either binding property names or an array that contains both names, which is used in on-disk
23796 * metadata formats (e.g. in .d.ts files).
23797 */
23798 static fromMappedObject(obj) {
23799 const forwardMap = new Map();
23800 for (const classPropertyName of Object.keys(obj)) {
23801 const value = obj[classPropertyName];
23802 const bindingPropertyName = Array.isArray(value) ? value[0] : value;
23803 const inputOrOutput = { classPropertyName, bindingPropertyName };
23804 forwardMap.set(classPropertyName, inputOrOutput);
23805 }
23806 return new ClassPropertyMapping(forwardMap);
23807 }
23808 /**
23809 * Merge two mappings into one, with class properties from `b` taking precedence over class
23810 * properties from `a`.
23811 */
23812 static merge(a, b) {
23813 const forwardMap = new Map(a.forwardMap.entries());
23814 for (const [classPropertyName, inputOrOutput] of b.forwardMap) {
23815 forwardMap.set(classPropertyName, inputOrOutput);
23816 }
23817 return new ClassPropertyMapping(forwardMap);
23818 }
23819 /**
23820 * All class property names mapped in this mapping.
23821 */
23822 get classPropertyNames() {
23823 return Array.from(this.forwardMap.keys());
23824 }
23825 /**
23826 * All binding property names mapped in this mapping.
23827 */
23828 get propertyNames() {
23829 return Array.from(this.reverseMap.keys());
23830 }
23831 /**
23832 * Check whether a mapping for the given property name exists.
23833 */
23834 hasBindingPropertyName(propertyName) {
23835 return this.reverseMap.has(propertyName);
23836 }
23837 /**
23838 * Lookup all `InputOrOutput`s that use this `propertyName`.
23839 */
23840 getByBindingPropertyName(propertyName) {
23841 return this.reverseMap.has(propertyName) ? this.reverseMap.get(propertyName) : null;
23842 }
23843 /**
23844 * Lookup the `InputOrOutput` associated with a `classPropertyName`.
23845 */
23846 getByClassPropertyName(classPropertyName) {
23847 return this.forwardMap.has(classPropertyName) ? this.forwardMap.get(classPropertyName) : null;
23848 }
23849 /**
23850 * Convert this mapping to a primitive JS object which maps each class property directly to the
23851 * binding property name associated with it.
23852 */
23853 toDirectMappedObject() {
23854 const obj = {};
23855 for (const [classPropertyName, inputOrOutput] of this.forwardMap) {
23856 obj[classPropertyName] = inputOrOutput.bindingPropertyName;
23857 }
23858 return obj;
23859 }
23860 /**
23861 * Convert this mapping to a primitive JS object which maps each class property either to itself
23862 * (for cases where the binding property name is the same) or to an array which contains both
23863 * names if they differ.
23864 *
23865 * This object format is used when mappings are serialized (for example into .d.ts files).
23866 */
23867 toJointMappedObject() {
23868 const obj = {};
23869 for (const [classPropertyName, inputOrOutput] of this.forwardMap) {
23870 if (inputOrOutput.bindingPropertyName === classPropertyName) {
23871 obj[classPropertyName] = inputOrOutput.bindingPropertyName;
23872 }
23873 else {
23874 obj[classPropertyName] = [inputOrOutput.bindingPropertyName, classPropertyName];
23875 }
23876 }
23877 return obj;
23878 }
23879 /**
23880 * Implement the iterator protocol and return entry objects which contain the class and binding
23881 * property names (and are useful for destructuring).
23882 */
23883 *[Symbol.iterator]() {
23884 for (const [classPropertyName, inputOrOutput] of this.forwardMap.entries()) {
23885 yield [classPropertyName, inputOrOutput.bindingPropertyName];
23886 }
23887 }
23888 }
23889 function reverseMapFromForwardMap(forwardMap) {
23890 const reverseMap = new Map();
23891 for (const [_, inputOrOutput] of forwardMap) {
23892 if (!reverseMap.has(inputOrOutput.bindingPropertyName)) {
23893 reverseMap.set(inputOrOutput.bindingPropertyName, []);
23894 }
23895 reverseMap.get(inputOrOutput.bindingPropertyName).push(inputOrOutput);
23896 }
23897 return reverseMap;
23898 }
23899
23900 /**
23901 * @license
23902 * Copyright Google LLC All Rights Reserved.
23903 *
23904 * Use of this source code is governed by an MIT-style license that can be
23905 * found in the LICENSE file at https://angular.io/license
23906 */
23907 function extractReferencesFromType(checker, def, bestGuessOwningModule) {
23908 if (!ts__default["default"].isTupleTypeNode(def)) {
23909 return [];
23910 }
23911 return def.elements.map(element => {
23912 if (!ts__default["default"].isTypeQueryNode(element)) {
23913 throw new Error(`Expected TypeQueryNode: ${nodeDebugInfo(element)}`);
23914 }
23915 const type = element.exprName;
23916 const { node, from } = reflectTypeEntityToDeclaration(type, checker);
23917 if (!isNamedClassDeclaration(node)) {
23918 throw new Error(`Expected named ClassDeclaration: ${nodeDebugInfo(node)}`);
23919 }
23920 if (from !== null && !from.startsWith('.')) {
23921 // The symbol was imported using an absolute module specifier so return a reference that
23922 // uses that absolute module specifier as its best guess owning module.
23923 return new Reference(node, { specifier: from, resolutionContext: def.getSourceFile().fileName });
23924 }
23925 else {
23926 // For local symbols or symbols that were imported using a relative module import it is
23927 // assumed that the symbol is exported from the provided best guess owning module.
23928 return new Reference(node, bestGuessOwningModule);
23929 }
23930 });
23931 }
23932 function readStringType(type) {
23933 if (!ts__default["default"].isLiteralTypeNode(type) || !ts__default["default"].isStringLiteral(type.literal)) {
23934 return null;
23935 }
23936 return type.literal.text;
23937 }
23938 function readStringMapType(type) {
23939 if (!ts__default["default"].isTypeLiteralNode(type)) {
23940 return {};
23941 }
23942 const obj = {};
23943 type.members.forEach(member => {
23944 if (!ts__default["default"].isPropertySignature(member) || member.type === undefined || member.name === undefined ||
23945 !ts__default["default"].isStringLiteral(member.name)) {
23946 return;
23947 }
23948 const value = readStringType(member.type);
23949 if (value === null) {
23950 return null;
23951 }
23952 obj[member.name.text] = value;
23953 });
23954 return obj;
23955 }
23956 function readStringArrayType(type) {
23957 if (!ts__default["default"].isTupleTypeNode(type)) {
23958 return [];
23959 }
23960 const res = [];
23961 type.elements.forEach(el => {
23962 if (!ts__default["default"].isLiteralTypeNode(el) || !ts__default["default"].isStringLiteral(el.literal)) {
23963 return;
23964 }
23965 res.push(el.literal.text);
23966 });
23967 return res;
23968 }
23969 /**
23970 * Inspects the class' members and extracts the metadata that is used when type-checking templates
23971 * that use the directive. This metadata does not contain information from a base class, if any,
23972 * making this metadata invariant to changes of inherited classes.
23973 */
23974 function extractDirectiveTypeCheckMeta(node, inputs, reflector) {
23975 const members = reflector.getMembersOfClass(node);
23976 const staticMembers = members.filter(member => member.isStatic);
23977 const ngTemplateGuards = staticMembers.map(extractTemplateGuard)
23978 .filter((guard) => guard !== null);
23979 const hasNgTemplateContextGuard = staticMembers.some(member => member.kind === ClassMemberKind.Method && member.name === 'ngTemplateContextGuard');
23980 const coercedInputFields = new Set(staticMembers.map(extractCoercedInput)
23981 .filter((inputName) => inputName !== null));
23982 const restrictedInputFields = new Set();
23983 const stringLiteralInputFields = new Set();
23984 const undeclaredInputFields = new Set();
23985 for (const classPropertyName of inputs.classPropertyNames) {
23986 const field = members.find(member => member.name === classPropertyName);
23987 if (field === undefined || field.node === null) {
23988 undeclaredInputFields.add(classPropertyName);
23989 continue;
23990 }
23991 if (isRestricted(field.node)) {
23992 restrictedInputFields.add(classPropertyName);
23993 }
23994 if (field.nameNode !== null && ts__default["default"].isStringLiteral(field.nameNode)) {
23995 stringLiteralInputFields.add(classPropertyName);
23996 }
23997 }
23998 const arity = reflector.getGenericArityOfClass(node);
23999 return {
24000 hasNgTemplateContextGuard,
24001 ngTemplateGuards,
24002 coercedInputFields,
24003 restrictedInputFields,
24004 stringLiteralInputFields,
24005 undeclaredInputFields,
24006 isGeneric: arity !== null && arity > 0,
24007 };
24008 }
24009 function isRestricted(node) {
24010 if (node.modifiers === undefined) {
24011 return false;
24012 }
24013 return node.modifiers.some(modifier => modifier.kind === ts__default["default"].SyntaxKind.PrivateKeyword ||
24014 modifier.kind === ts__default["default"].SyntaxKind.ProtectedKeyword ||
24015 modifier.kind === ts__default["default"].SyntaxKind.ReadonlyKeyword);
24016 }
24017 function extractTemplateGuard(member) {
24018 if (!member.name.startsWith('ngTemplateGuard_')) {
24019 return null;
24020 }
24021 const inputName = afterUnderscore(member.name);
24022 if (member.kind === ClassMemberKind.Property) {
24023 let type = null;
24024 if (member.type !== null && ts__default["default"].isLiteralTypeNode(member.type) &&
24025 ts__default["default"].isStringLiteral(member.type.literal)) {
24026 type = member.type.literal.text;
24027 }
24028 // Only property members with string literal type 'binding' are considered as template guard.
24029 if (type !== 'binding') {
24030 return null;
24031 }
24032 return { inputName, type };
24033 }
24034 else if (member.kind === ClassMemberKind.Method) {
24035 return { inputName, type: 'invocation' };
24036 }
24037 else {
24038 return null;
24039 }
24040 }
24041 function extractCoercedInput(member) {
24042 if (member.kind !== ClassMemberKind.Property || !member.name.startsWith('ngAcceptInputType_')) {
24043 return null;
24044 }
24045 return afterUnderscore(member.name);
24046 }
24047 /**
24048 * A `MetadataReader` that reads from an ordered set of child readers until it obtains the requested
24049 * metadata.
24050 *
24051 * This is used to combine `MetadataReader`s that read from different sources (e.g. from a registry
24052 * and from .d.ts files).
24053 */
24054 class CompoundMetadataReader {
24055 constructor(readers) {
24056 this.readers = readers;
24057 }
24058 getDirectiveMetadata(node) {
24059 for (const reader of this.readers) {
24060 const meta = reader.getDirectiveMetadata(node);
24061 if (meta !== null) {
24062 return meta;
24063 }
24064 }
24065 return null;
24066 }
24067 getNgModuleMetadata(node) {
24068 for (const reader of this.readers) {
24069 const meta = reader.getNgModuleMetadata(node);
24070 if (meta !== null) {
24071 return meta;
24072 }
24073 }
24074 return null;
24075 }
24076 getPipeMetadata(node) {
24077 for (const reader of this.readers) {
24078 const meta = reader.getPipeMetadata(node);
24079 if (meta !== null) {
24080 return meta;
24081 }
24082 }
24083 return null;
24084 }
24085 }
24086 function afterUnderscore(str) {
24087 const pos = str.indexOf('_');
24088 if (pos === -1) {
24089 throw new Error(`Expected '${str}' to contain '_'`);
24090 }
24091 return str.substr(pos + 1);
24092 }
24093 /** Returns whether a class declaration has the necessary class fields to make it injectable. */
24094 function hasInjectableFields(clazz, host) {
24095 const members = host.getMembersOfClass(clazz);
24096 return members.some(({ isStatic, name }) => isStatic && (name === 'ɵprov' || name === 'ɵfac'));
24097 }
24098
24099 /**
24100 * @license
24101 * Copyright Google LLC All Rights Reserved.
24102 *
24103 * Use of this source code is governed by an MIT-style license that can be
24104 * found in the LICENSE file at https://angular.io/license
24105 */
24106 /**
24107 * A `MetadataReader` that can read metadata from `.d.ts` files, which have static Ivy properties
24108 * from an upstream compilation already.
24109 */
24110 class DtsMetadataReader {
24111 constructor(checker, reflector) {
24112 this.checker = checker;
24113 this.reflector = reflector;
24114 }
24115 /**
24116 * Read the metadata from a class that has already been compiled somehow (either it's in a .d.ts
24117 * file, or in a .ts file with a handwritten definition).
24118 *
24119 * @param ref `Reference` to the class of interest, with the context of how it was obtained.
24120 */
24121 getNgModuleMetadata(ref) {
24122 const clazz = ref.node;
24123 // This operation is explicitly not memoized, as it depends on `ref.ownedByModuleGuess`.
24124 // TODO(alxhub): investigate caching of .d.ts module metadata.
24125 const ngModuleDef = this.reflector.getMembersOfClass(clazz).find(member => member.name === 'ɵmod' && member.isStatic);
24126 if (ngModuleDef === undefined) {
24127 return null;
24128 }
24129 else if (
24130 // Validate that the shape of the ngModuleDef type is correct.
24131 ngModuleDef.type === null || !ts__default["default"].isTypeReferenceNode(ngModuleDef.type) ||
24132 ngModuleDef.type.typeArguments === undefined ||
24133 ngModuleDef.type.typeArguments.length !== 4) {
24134 return null;
24135 }
24136 // Read the ModuleData out of the type arguments.
24137 const [_, declarationMetadata, importMetadata, exportMetadata] = ngModuleDef.type.typeArguments;
24138 return {
24139 ref,
24140 declarations: extractReferencesFromType(this.checker, declarationMetadata, ref.bestGuessOwningModule),
24141 exports: extractReferencesFromType(this.checker, exportMetadata, ref.bestGuessOwningModule),
24142 imports: extractReferencesFromType(this.checker, importMetadata, ref.bestGuessOwningModule),
24143 schemas: [],
24144 rawDeclarations: null,
24145 };
24146 }
24147 /**
24148 * Read directive (or component) metadata from a referenced class in a .d.ts file.
24149 */
24150 getDirectiveMetadata(ref) {
24151 const clazz = ref.node;
24152 const def = this.reflector.getMembersOfClass(clazz).find(field => field.isStatic && (field.name === 'ɵcmp' || field.name === 'ɵdir'));
24153 if (def === undefined) {
24154 // No definition could be found.
24155 return null;
24156 }
24157 else if (def.type === null || !ts__default["default"].isTypeReferenceNode(def.type) ||
24158 def.type.typeArguments === undefined || def.type.typeArguments.length < 2) {
24159 // The type metadata was the wrong shape.
24160 return null;
24161 }
24162 const isComponent = def.name === 'ɵcmp';
24163 const ctorParams = this.reflector.getConstructorParameters(clazz);
24164 // A directive is considered to be structural if:
24165 // 1) it's a directive, not a component, and
24166 // 2) it injects `TemplateRef`
24167 const isStructural = !isComponent && ctorParams !== null && ctorParams.some(param => {
24168 return param.typeValueReference.kind === 1 /* IMPORTED */ &&
24169 param.typeValueReference.moduleName === '@angular/core' &&
24170 param.typeValueReference.importedName === 'TemplateRef';
24171 });
24172 const inputs = ClassPropertyMapping.fromMappedObject(readStringMapType(def.type.typeArguments[3]));
24173 const outputs = ClassPropertyMapping.fromMappedObject(readStringMapType(def.type.typeArguments[4]));
24174 return {
24175 type: MetaType.Directive,
24176 ref,
24177 name: clazz.name.text,
24178 isComponent,
24179 selector: readStringType(def.type.typeArguments[1]),
24180 exportAs: readStringArrayType(def.type.typeArguments[2]),
24181 inputs,
24182 outputs,
24183 queries: readStringArrayType(def.type.typeArguments[5]),
24184 ...extractDirectiveTypeCheckMeta(clazz, inputs, this.reflector),
24185 baseClass: readBaseClass$1(clazz, this.checker, this.reflector),
24186 isPoisoned: false,
24187 isStructural,
24188 };
24189 }
24190 /**
24191 * Read pipe metadata from a referenced class in a .d.ts file.
24192 */
24193 getPipeMetadata(ref) {
24194 const def = this.reflector.getMembersOfClass(ref.node).find(field => field.isStatic && field.name === 'ɵpipe');
24195 if (def === undefined) {
24196 // No definition could be found.
24197 return null;
24198 }
24199 else if (def.type === null || !ts__default["default"].isTypeReferenceNode(def.type) ||
24200 def.type.typeArguments === undefined || def.type.typeArguments.length < 2) {
24201 // The type metadata was the wrong shape.
24202 return null;
24203 }
24204 const type = def.type.typeArguments[1];
24205 if (!ts__default["default"].isLiteralTypeNode(type) || !ts__default["default"].isStringLiteral(type.literal)) {
24206 // The type metadata was the wrong type.
24207 return null;
24208 }
24209 const name = type.literal.text;
24210 return {
24211 type: MetaType.Pipe,
24212 ref,
24213 name,
24214 nameExpr: null,
24215 };
24216 }
24217 }
24218 function readBaseClass$1(clazz, checker, reflector) {
24219 if (!isNamedClassDeclaration(clazz)) {
24220 // Technically this is an error in a .d.ts file, but for the purposes of finding the base class
24221 // it's ignored.
24222 return reflector.hasBaseClass(clazz) ? 'dynamic' : null;
24223 }
24224 if (clazz.heritageClauses !== undefined) {
24225 for (const clause of clazz.heritageClauses) {
24226 if (clause.token === ts__default["default"].SyntaxKind.ExtendsKeyword) {
24227 const baseExpr = clause.types[0].expression;
24228 let symbol = checker.getSymbolAtLocation(baseExpr);
24229 if (symbol === undefined) {
24230 return 'dynamic';
24231 }
24232 else if (symbol.flags & ts__default["default"].SymbolFlags.Alias) {
24233 symbol = checker.getAliasedSymbol(symbol);
24234 }
24235 if (symbol.valueDeclaration !== undefined &&
24236 isNamedClassDeclaration(symbol.valueDeclaration)) {
24237 return new Reference(symbol.valueDeclaration);
24238 }
24239 else {
24240 return 'dynamic';
24241 }
24242 }
24243 }
24244 }
24245 return null;
24246 }
24247
24248 /**
24249 * @license
24250 * Copyright Google LLC All Rights Reserved.
24251 *
24252 * Use of this source code is governed by an MIT-style license that can be
24253 * found in the LICENSE file at https://angular.io/license
24254 */
24255 /**
24256 * Given a reference to a directive, return a flattened version of its `DirectiveMeta` metadata
24257 * which includes metadata from its entire inheritance chain.
24258 *
24259 * The returned `DirectiveMeta` will either have `baseClass: null` if the inheritance chain could be
24260 * fully resolved, or `baseClass: 'dynamic'` if the inheritance chain could not be completely
24261 * followed.
24262 */
24263 function flattenInheritedDirectiveMetadata(reader, dir) {
24264 const topMeta = reader.getDirectiveMetadata(dir);
24265 if (topMeta === null) {
24266 throw new Error(`Metadata not found for directive: ${dir.debugName}`);
24267 }
24268 if (topMeta.baseClass === null) {
24269 return topMeta;
24270 }
24271 const coercedInputFields = new Set();
24272 const undeclaredInputFields = new Set();
24273 const restrictedInputFields = new Set();
24274 const stringLiteralInputFields = new Set();
24275 let isDynamic = false;
24276 let inputs = ClassPropertyMapping.empty();
24277 let outputs = ClassPropertyMapping.empty();
24278 let isStructural = false;
24279 const addMetadata = (meta) => {
24280 if (meta.baseClass === 'dynamic') {
24281 isDynamic = true;
24282 }
24283 else if (meta.baseClass !== null) {
24284 const baseMeta = reader.getDirectiveMetadata(meta.baseClass);
24285 if (baseMeta !== null) {
24286 addMetadata(baseMeta);
24287 }
24288 else {
24289 // Missing metadata for the base class means it's effectively dynamic.
24290 isDynamic = true;
24291 }
24292 }
24293 isStructural = isStructural || meta.isStructural;
24294 inputs = ClassPropertyMapping.merge(inputs, meta.inputs);
24295 outputs = ClassPropertyMapping.merge(outputs, meta.outputs);
24296 for (const coercedInputField of meta.coercedInputFields) {
24297 coercedInputFields.add(coercedInputField);
24298 }
24299 for (const undeclaredInputField of meta.undeclaredInputFields) {
24300 undeclaredInputFields.add(undeclaredInputField);
24301 }
24302 for (const restrictedInputField of meta.restrictedInputFields) {
24303 restrictedInputFields.add(restrictedInputField);
24304 }
24305 for (const field of meta.stringLiteralInputFields) {
24306 stringLiteralInputFields.add(field);
24307 }
24308 };
24309 addMetadata(topMeta);
24310 return {
24311 ...topMeta,
24312 inputs,
24313 outputs,
24314 coercedInputFields,
24315 undeclaredInputFields,
24316 restrictedInputFields,
24317 stringLiteralInputFields,
24318 baseClass: isDynamic ? 'dynamic' : null,
24319 isStructural,
24320 };
24321 }
24322
24323 /**
24324 * @license
24325 * Copyright Google LLC All Rights Reserved.
24326 *
24327 * Use of this source code is governed by an MIT-style license that can be
24328 * found in the LICENSE file at https://angular.io/license
24329 */
24330 /**
24331 * A registry of directive, pipe, and module metadata for types defined in the current compilation
24332 * unit, which supports both reading and registering.
24333 */
24334 class LocalMetadataRegistry {
24335 constructor() {
24336 this.directives = new Map();
24337 this.ngModules = new Map();
24338 this.pipes = new Map();
24339 }
24340 getDirectiveMetadata(ref) {
24341 return this.directives.has(ref.node) ? this.directives.get(ref.node) : null;
24342 }
24343 getNgModuleMetadata(ref) {
24344 return this.ngModules.has(ref.node) ? this.ngModules.get(ref.node) : null;
24345 }
24346 getPipeMetadata(ref) {
24347 return this.pipes.has(ref.node) ? this.pipes.get(ref.node) : null;
24348 }
24349 registerDirectiveMetadata(meta) {
24350 this.directives.set(meta.ref.node, meta);
24351 }
24352 registerNgModuleMetadata(meta) {
24353 this.ngModules.set(meta.ref.node, meta);
24354 }
24355 registerPipeMetadata(meta) {
24356 this.pipes.set(meta.ref.node, meta);
24357 }
24358 }
24359 /**
24360 * A `MetadataRegistry` which registers metdata with multiple delegate `MetadataRegistry` instances.
24361 */
24362 class CompoundMetadataRegistry {
24363 constructor(registries) {
24364 this.registries = registries;
24365 }
24366 registerDirectiveMetadata(meta) {
24367 for (const registry of this.registries) {
24368 registry.registerDirectiveMetadata(meta);
24369 }
24370 }
24371 registerNgModuleMetadata(meta) {
24372 for (const registry of this.registries) {
24373 registry.registerNgModuleMetadata(meta);
24374 }
24375 }
24376 registerPipeMetadata(meta) {
24377 for (const registry of this.registries) {
24378 registry.registerPipeMetadata(meta);
24379 }
24380 }
24381 }
24382 /**
24383 * Registry that keeps track of classes that can be constructed via dependency injection (e.g.
24384 * injectables, directives, pipes).
24385 */
24386 class InjectableClassRegistry {
24387 constructor(host) {
24388 this.host = host;
24389 this.classes = new Set();
24390 }
24391 registerInjectable(declaration) {
24392 this.classes.add(declaration);
24393 }
24394 isInjectable(declaration) {
24395 // Figure out whether the class is injectable based on the registered classes, otherwise
24396 // fall back to looking at its members since we might not have been able register the class
24397 // if it was compiled already.
24398 return this.classes.has(declaration) || hasInjectableFields(declaration, this.host);
24399 }
24400 }
24401
24402 /**
24403 * @license
24404 * Copyright Google LLC All Rights Reserved.
24405 *
24406 * Use of this source code is governed by an MIT-style license that can be
24407 * found in the LICENSE file at https://angular.io/license
24408 */
24409 function isExternalResource(resource) {
24410 return resource.path !== null;
24411 }
24412 /**
24413 * Tracks the mapping between external template/style files and the component(s) which use them.
24414 *
24415 * This information is produced during analysis of the program and is used mainly to support
24416 * external tooling, for which such a mapping is challenging to determine without compiler
24417 * assistance.
24418 */
24419 class ResourceRegistry {
24420 constructor() {
24421 this.externalTemplateToComponentsMap = new Map();
24422 this.componentToTemplateMap = new Map();
24423 this.componentToStylesMap = new Map();
24424 this.externalStyleToComponentsMap = new Map();
24425 }
24426 getComponentsWithTemplate(template) {
24427 if (!this.externalTemplateToComponentsMap.has(template)) {
24428 return new Set();
24429 }
24430 return this.externalTemplateToComponentsMap.get(template);
24431 }
24432 registerResources(resources, component) {
24433 if (resources.template !== null) {
24434 this.registerTemplate(resources.template, component);
24435 }
24436 for (const style of resources.styles) {
24437 this.registerStyle(style, component);
24438 }
24439 }
24440 registerTemplate(templateResource, component) {
24441 const { path } = templateResource;
24442 if (path !== null) {
24443 if (!this.externalTemplateToComponentsMap.has(path)) {
24444 this.externalTemplateToComponentsMap.set(path, new Set());
24445 }
24446 this.externalTemplateToComponentsMap.get(path).add(component);
24447 }
24448 this.componentToTemplateMap.set(component, templateResource);
24449 }
24450 getTemplate(component) {
24451 if (!this.componentToTemplateMap.has(component)) {
24452 return null;
24453 }
24454 return this.componentToTemplateMap.get(component);
24455 }
24456 registerStyle(styleResource, component) {
24457 const { path } = styleResource;
24458 if (!this.componentToStylesMap.has(component)) {
24459 this.componentToStylesMap.set(component, new Set());
24460 }
24461 if (path !== null) {
24462 if (!this.externalStyleToComponentsMap.has(path)) {
24463 this.externalStyleToComponentsMap.set(path, new Set());
24464 }
24465 this.externalStyleToComponentsMap.get(path).add(component);
24466 }
24467 this.componentToStylesMap.get(component).add(styleResource);
24468 }
24469 getStyles(component) {
24470 if (!this.componentToStylesMap.has(component)) {
24471 return new Set();
24472 }
24473 return this.componentToStylesMap.get(component);
24474 }
24475 getComponentsWithStyle(styleUrl) {
24476 if (!this.externalStyleToComponentsMap.has(styleUrl)) {
24477 return new Set();
24478 }
24479 return this.externalStyleToComponentsMap.get(styleUrl);
24480 }
24481 }
24482
24483 /**
24484 * @license
24485 * Copyright Google LLC All Rights Reserved.
24486 *
24487 * Use of this source code is governed by an MIT-style license that can be
24488 * found in the LICENSE file at https://angular.io/license
24489 */
24490 /**
24491 * Represents a value which cannot be determined statically.
24492 */
24493 class DynamicValue {
24494 constructor(node, reason, code) {
24495 this.node = node;
24496 this.reason = reason;
24497 this.code = code;
24498 }
24499 static fromDynamicInput(node, input) {
24500 return new DynamicValue(node, input, 0 /* DYNAMIC_INPUT */);
24501 }
24502 static fromDynamicString(node) {
24503 return new DynamicValue(node, undefined, 1 /* DYNAMIC_STRING */);
24504 }
24505 static fromExternalReference(node, ref) {
24506 return new DynamicValue(node, ref, 2 /* EXTERNAL_REFERENCE */);
24507 }
24508 static fromUnsupportedSyntax(node) {
24509 return new DynamicValue(node, undefined, 3 /* UNSUPPORTED_SYNTAX */);
24510 }
24511 static fromUnknownIdentifier(node) {
24512 return new DynamicValue(node, undefined, 4 /* UNKNOWN_IDENTIFIER */);
24513 }
24514 static fromInvalidExpressionType(node, value) {
24515 return new DynamicValue(node, value, 5 /* INVALID_EXPRESSION_TYPE */);
24516 }
24517 static fromComplexFunctionCall(node, fn) {
24518 return new DynamicValue(node, fn, 6 /* COMPLEX_FUNCTION_CALL */);
24519 }
24520 static fromDynamicType(node) {
24521 return new DynamicValue(node, undefined, 7 /* DYNAMIC_TYPE */);
24522 }
24523 static fromUnknown(node) {
24524 return new DynamicValue(node, undefined, 8 /* UNKNOWN */);
24525 }
24526 isFromDynamicInput() {
24527 return this.code === 0 /* DYNAMIC_INPUT */;
24528 }
24529 isFromDynamicString() {
24530 return this.code === 1 /* DYNAMIC_STRING */;
24531 }
24532 isFromExternalReference() {
24533 return this.code === 2 /* EXTERNAL_REFERENCE */;
24534 }
24535 isFromUnsupportedSyntax() {
24536 return this.code === 3 /* UNSUPPORTED_SYNTAX */;
24537 }
24538 isFromUnknownIdentifier() {
24539 return this.code === 4 /* UNKNOWN_IDENTIFIER */;
24540 }
24541 isFromInvalidExpressionType() {
24542 return this.code === 5 /* INVALID_EXPRESSION_TYPE */;
24543 }
24544 isFromComplexFunctionCall() {
24545 return this.code === 6 /* COMPLEX_FUNCTION_CALL */;
24546 }
24547 isFromDynamicType() {
24548 return this.code === 7 /* DYNAMIC_TYPE */;
24549 }
24550 isFromUnknown() {
24551 return this.code === 8 /* UNKNOWN */;
24552 }
24553 accept(visitor) {
24554 switch (this.code) {
24555 case 0 /* DYNAMIC_INPUT */:
24556 return visitor.visitDynamicInput(this);
24557 case 1 /* DYNAMIC_STRING */:
24558 return visitor.visitDynamicString(this);
24559 case 2 /* EXTERNAL_REFERENCE */:
24560 return visitor.visitExternalReference(this);
24561 case 3 /* UNSUPPORTED_SYNTAX */:
24562 return visitor.visitUnsupportedSyntax(this);
24563 case 4 /* UNKNOWN_IDENTIFIER */:
24564 return visitor.visitUnknownIdentifier(this);
24565 case 5 /* INVALID_EXPRESSION_TYPE */:
24566 return visitor.visitInvalidExpressionType(this);
24567 case 6 /* COMPLEX_FUNCTION_CALL */:
24568 return visitor.visitComplexFunctionCall(this);
24569 case 7 /* DYNAMIC_TYPE */:
24570 return visitor.visitDynamicType(this);
24571 case 8 /* UNKNOWN */:
24572 return visitor.visitUnknown(this);
24573 }
24574 }
24575 }
24576
24577 /**
24578 * @license
24579 * Copyright Google LLC All Rights Reserved.
24580 *
24581 * Use of this source code is governed by an MIT-style license that can be
24582 * found in the LICENSE file at https://angular.io/license
24583 */
24584 /**
24585 * A collection of publicly exported declarations from a module. Each declaration is evaluated
24586 * lazily upon request.
24587 */
24588 class ResolvedModule {
24589 constructor(exports, evaluate) {
24590 this.exports = exports;
24591 this.evaluate = evaluate;
24592 }
24593 getExport(name) {
24594 if (!this.exports.has(name)) {
24595 return undefined;
24596 }
24597 return this.evaluate(this.exports.get(name));
24598 }
24599 getExports() {
24600 const map = new Map();
24601 this.exports.forEach((decl, name) => {
24602 map.set(name, this.evaluate(decl));
24603 });
24604 return map;
24605 }
24606 }
24607 /**
24608 * A value member of an enumeration.
24609 *
24610 * Contains a `Reference` to the enumeration itself, and the name of the referenced member.
24611 */
24612 class EnumValue {
24613 constructor(enumRef, name, resolved) {
24614 this.enumRef = enumRef;
24615 this.name = name;
24616 this.resolved = resolved;
24617 }
24618 }
24619 /**
24620 * An implementation of a known function that can be statically evaluated.
24621 * It could be a built-in function or method (such as `Array.prototype.slice`) or a TypeScript
24622 * helper (such as `__spread`).
24623 */
24624 class KnownFn {
24625 }
24626
24627 /**
24628 * @license
24629 * Copyright Google LLC All Rights Reserved.
24630 *
24631 * Use of this source code is governed by an MIT-style license that can be
24632 * found in the LICENSE file at https://angular.io/license
24633 */
24634 /**
24635 * Derives a type representation from a resolved value to be reported in a diagnostic.
24636 *
24637 * @param value The resolved value for which a type representation should be derived.
24638 * @param maxDepth The maximum nesting depth of objects and arrays, defaults to 1 level.
24639 */
24640 function describeResolvedType(value, maxDepth = 1) {
24641 if (value === null) {
24642 return 'null';
24643 }
24644 else if (value === undefined) {
24645 return 'undefined';
24646 }
24647 else if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'string') {
24648 return typeof value;
24649 }
24650 else if (value instanceof Map) {
24651 if (maxDepth === 0) {
24652 return 'object';
24653 }
24654 const entries = Array.from(value.entries()).map(([key, v]) => {
24655 return `${quoteKey(key)}: ${describeResolvedType(v, maxDepth - 1)}`;
24656 });
24657 return entries.length > 0 ? `{ ${entries.join('; ')} }` : '{}';
24658 }
24659 else if (value instanceof ResolvedModule) {
24660 return '(module)';
24661 }
24662 else if (value instanceof EnumValue) {
24663 return value.enumRef.debugName ?? '(anonymous)';
24664 }
24665 else if (value instanceof Reference) {
24666 return value.debugName ?? '(anonymous)';
24667 }
24668 else if (Array.isArray(value)) {
24669 if (maxDepth === 0) {
24670 return 'Array';
24671 }
24672 return `[${value.map(v => describeResolvedType(v, maxDepth - 1)).join(', ')}]`;
24673 }
24674 else if (value instanceof DynamicValue) {
24675 return '(not statically analyzable)';
24676 }
24677 else if (value instanceof KnownFn) {
24678 return 'Function';
24679 }
24680 else {
24681 return 'unknown';
24682 }
24683 }
24684 function quoteKey(key) {
24685 if (/^[a-z0-9_]+$/i.test(key)) {
24686 return key;
24687 }
24688 else {
24689 return `'${key.replace(/'/g, '\\\'')}'`;
24690 }
24691 }
24692 /**
24693 * Creates an array of related information diagnostics for a `DynamicValue` that describe the trace
24694 * of why an expression was evaluated as dynamic.
24695 *
24696 * @param node The node for which a `ts.Diagnostic` is to be created with the trace.
24697 * @param value The dynamic value for which a trace should be created.
24698 */
24699 function traceDynamicValue(node, value) {
24700 return value.accept(new TraceDynamicValueVisitor(node));
24701 }
24702 class TraceDynamicValueVisitor {
24703 constructor(node) {
24704 this.node = node;
24705 this.currentContainerNode = null;
24706 }
24707 visitDynamicInput(value) {
24708 const trace = value.reason.accept(this);
24709 if (this.shouldTrace(value.node)) {
24710 const info = makeRelatedInformation(value.node, 'Unable to evaluate this expression statically.');
24711 trace.unshift(info);
24712 }
24713 return trace;
24714 }
24715 visitDynamicString(value) {
24716 return [makeRelatedInformation(value.node, 'A string value could not be determined statically.')];
24717 }
24718 visitExternalReference(value) {
24719 const name = value.reason.debugName;
24720 const description = name !== null ? `'${name}'` : 'an anonymous declaration';
24721 return [makeRelatedInformation(value.node, `A value for ${description} cannot be determined statically, as it is an external declaration.`)];
24722 }
24723 visitComplexFunctionCall(value) {
24724 return [
24725 makeRelatedInformation(value.node, 'Unable to evaluate function call of complex function. A function must have exactly one return statement.'),
24726 makeRelatedInformation(value.reason.node, 'Function is declared here.')
24727 ];
24728 }
24729 visitInvalidExpressionType(value) {
24730 return [makeRelatedInformation(value.node, 'Unable to evaluate an invalid expression.')];
24731 }
24732 visitUnknown(value) {
24733 return [makeRelatedInformation(value.node, 'Unable to evaluate statically.')];
24734 }
24735 visitUnknownIdentifier(value) {
24736 return [makeRelatedInformation(value.node, 'Unknown reference.')];
24737 }
24738 visitDynamicType(value) {
24739 return [makeRelatedInformation(value.node, 'Dynamic type.')];
24740 }
24741 visitUnsupportedSyntax(value) {
24742 return [makeRelatedInformation(value.node, 'This syntax is not supported.')];
24743 }
24744 /**
24745 * Determines whether the dynamic value reported for the node should be traced, i.e. if it is not
24746 * part of the container for which the most recent trace was created.
24747 */
24748 shouldTrace(node) {
24749 if (node === this.node) {
24750 // Do not include a dynamic value for the origin node, as the main diagnostic is already
24751 // reported on that node.
24752 return false;
24753 }
24754 const container = getContainerNode(node);
24755 if (container === this.currentContainerNode) {
24756 // The node is part of the same container as the previous trace entry, so this dynamic value
24757 // should not become part of the trace.
24758 return false;
24759 }
24760 this.currentContainerNode = container;
24761 return true;
24762 }
24763 }
24764 /**
24765 * Determines the closest parent node that is to be considered as container, which is used to reduce
24766 * the granularity of tracing the dynamic values to a single entry per container. Currently, full
24767 * statements and destructuring patterns are considered as container.
24768 */
24769 function getContainerNode(node) {
24770 let currentNode = node;
24771 while (currentNode !== undefined) {
24772 switch (currentNode.kind) {
24773 case ts__default["default"].SyntaxKind.ExpressionStatement:
24774 case ts__default["default"].SyntaxKind.VariableStatement:
24775 case ts__default["default"].SyntaxKind.ReturnStatement:
24776 case ts__default["default"].SyntaxKind.IfStatement:
24777 case ts__default["default"].SyntaxKind.SwitchStatement:
24778 case ts__default["default"].SyntaxKind.DoStatement:
24779 case ts__default["default"].SyntaxKind.WhileStatement:
24780 case ts__default["default"].SyntaxKind.ForStatement:
24781 case ts__default["default"].SyntaxKind.ForInStatement:
24782 case ts__default["default"].SyntaxKind.ForOfStatement:
24783 case ts__default["default"].SyntaxKind.ContinueStatement:
24784 case ts__default["default"].SyntaxKind.BreakStatement:
24785 case ts__default["default"].SyntaxKind.ThrowStatement:
24786 case ts__default["default"].SyntaxKind.ObjectBindingPattern:
24787 case ts__default["default"].SyntaxKind.ArrayBindingPattern:
24788 return currentNode;
24789 }
24790 currentNode = currentNode.parent;
24791 }
24792 return node.getSourceFile();
24793 }
24794
24795 /**
24796 * @license
24797 * Copyright Google LLC All Rights Reserved.
24798 *
24799 * Use of this source code is governed by an MIT-style license that can be
24800 * found in the LICENSE file at https://angular.io/license
24801 */
24802 class ArraySliceBuiltinFn extends KnownFn {
24803 constructor(lhs) {
24804 super();
24805 this.lhs = lhs;
24806 }
24807 evaluate(node, args) {
24808 if (args.length === 0) {
24809 return this.lhs;
24810 }
24811 else {
24812 return DynamicValue.fromUnknown(node);
24813 }
24814 }
24815 }
24816 class ArrayConcatBuiltinFn extends KnownFn {
24817 constructor(lhs) {
24818 super();
24819 this.lhs = lhs;
24820 }
24821 evaluate(node, args) {
24822 const result = [...this.lhs];
24823 for (const arg of args) {
24824 if (arg instanceof DynamicValue) {
24825 result.push(DynamicValue.fromDynamicInput(node, arg));
24826 }
24827 else if (Array.isArray(arg)) {
24828 result.push(...arg);
24829 }
24830 else {
24831 result.push(arg);
24832 }
24833 }
24834 return result;
24835 }
24836 }
24837 class ObjectAssignBuiltinFn extends KnownFn {
24838 evaluate(node, args) {
24839 if (args.length === 0) {
24840 return DynamicValue.fromUnsupportedSyntax(node);
24841 }
24842 for (const arg of args) {
24843 if (arg instanceof DynamicValue) {
24844 return DynamicValue.fromDynamicInput(node, arg);
24845 }
24846 else if (!(arg instanceof Map)) {
24847 return DynamicValue.fromUnsupportedSyntax(node);
24848 }
24849 }
24850 const [target, ...sources] = args;
24851 for (const source of sources) {
24852 source.forEach((value, key) => target.set(key, value));
24853 }
24854 return target;
24855 }
24856 }
24857
24858 /**
24859 * @license
24860 * Copyright Google LLC All Rights Reserved.
24861 *
24862 * Use of this source code is governed by an MIT-style license that can be
24863 * found in the LICENSE file at https://angular.io/license
24864 */
24865 // Use the same implementation we use for `Object.assign()`. Semantically these functions are the
24866 // same, so they can also share the same evaluation code.
24867 class AssignHelperFn extends ObjectAssignBuiltinFn {
24868 }
24869 // Used for both `__spread()` and `__spreadArrays()` TypeScript helper functions.
24870 class SpreadHelperFn extends KnownFn {
24871 evaluate(node, args) {
24872 const result = [];
24873 for (const arg of args) {
24874 if (arg instanceof DynamicValue) {
24875 result.push(DynamicValue.fromDynamicInput(node, arg));
24876 }
24877 else if (Array.isArray(arg)) {
24878 result.push(...arg);
24879 }
24880 else {
24881 result.push(arg);
24882 }
24883 }
24884 return result;
24885 }
24886 }
24887 // Used for `__spreadArray` TypeScript helper function.
24888 class SpreadArrayHelperFn extends KnownFn {
24889 evaluate(node, args) {
24890 if (args.length !== 2 && args.length !== 3) {
24891 return DynamicValue.fromUnknown(node);
24892 }
24893 const [to, from] = args;
24894 if (to instanceof DynamicValue) {
24895 return DynamicValue.fromDynamicInput(node, to);
24896 }
24897 else if (from instanceof DynamicValue) {
24898 return DynamicValue.fromDynamicInput(node, from);
24899 }
24900 if (!Array.isArray(to)) {
24901 return DynamicValue.fromInvalidExpressionType(node, to);
24902 }
24903 else if (!Array.isArray(from)) {
24904 return DynamicValue.fromInvalidExpressionType(node, from);
24905 }
24906 return to.concat(from);
24907 }
24908 }
24909 // Used for `__read` TypeScript helper function.
24910 class ReadHelperFn extends KnownFn {
24911 evaluate(node, args) {
24912 if (args.length !== 1) {
24913 // The `__read` helper accepts a second argument `n` but that case is not supported.
24914 return DynamicValue.fromUnknown(node);
24915 }
24916 const [value] = args;
24917 if (value instanceof DynamicValue) {
24918 return DynamicValue.fromDynamicInput(node, value);
24919 }
24920 if (!Array.isArray(value)) {
24921 return DynamicValue.fromInvalidExpressionType(node, value);
24922 }
24923 return value;
24924 }
24925 }
24926
24927 /**
24928 * @license
24929 * Copyright Google LLC All Rights Reserved.
24930 *
24931 * Use of this source code is governed by an MIT-style license that can be
24932 * found in the LICENSE file at https://angular.io/license
24933 */
24934 /** Resolved value for the JavaScript global `Object` declaration. */
24935 const jsGlobalObjectValue = new Map([['assign', new ObjectAssignBuiltinFn()]]);
24936 /** Resolved value for the `__assign()` TypeScript helper declaration. */
24937 const assignTsHelperFn = new AssignHelperFn();
24938 /** Resolved value for the `__spread()` and `__spreadArrays()` TypeScript helper declarations. */
24939 const spreadTsHelperFn = new SpreadHelperFn();
24940 /** Resolved value for the `__spreadArray()` TypeScript helper declarations. */
24941 const spreadArrayTsHelperFn = new SpreadArrayHelperFn();
24942 /** Resolved value for the `__read()` TypeScript helper declarations. */
24943 const readTsHelperFn = new ReadHelperFn();
24944 /**
24945 * Resolves the specified known declaration to a resolved value. For example,
24946 * the known JavaScript global `Object` will resolve to a `Map` that provides the
24947 * `assign` method with a built-in function. This enables evaluation of `Object.assign`.
24948 */
24949 function resolveKnownDeclaration(decl) {
24950 switch (decl) {
24951 case KnownDeclaration.JsGlobalObject:
24952 return jsGlobalObjectValue;
24953 case KnownDeclaration.TsHelperAssign:
24954 return assignTsHelperFn;
24955 case KnownDeclaration.TsHelperSpread:
24956 case KnownDeclaration.TsHelperSpreadArrays:
24957 return spreadTsHelperFn;
24958 case KnownDeclaration.TsHelperSpreadArray:
24959 return spreadArrayTsHelperFn;
24960 case KnownDeclaration.TsHelperRead:
24961 return readTsHelperFn;
24962 default:
24963 throw new Error(`Cannot resolve known declaration. Received: ${KnownDeclaration[decl]}.`);
24964 }
24965 }
24966
24967 /**
24968 * @license
24969 * Copyright Google LLC All Rights Reserved.
24970 *
24971 * Use of this source code is governed by an MIT-style license that can be
24972 * found in the LICENSE file at https://angular.io/license
24973 */
24974 function literalBinaryOp(op) {
24975 return { op, literal: true };
24976 }
24977 function referenceBinaryOp(op) {
24978 return { op, literal: false };
24979 }
24980 const BINARY_OPERATORS$2 = new Map([
24981 [ts__default["default"].SyntaxKind.PlusToken, literalBinaryOp((a, b) => a + b)],
24982 [ts__default["default"].SyntaxKind.MinusToken, literalBinaryOp((a, b) => a - b)],
24983 [ts__default["default"].SyntaxKind.AsteriskToken, literalBinaryOp((a, b) => a * b)],
24984 [ts__default["default"].SyntaxKind.SlashToken, literalBinaryOp((a, b) => a / b)],
24985 [ts__default["default"].SyntaxKind.PercentToken, literalBinaryOp((a, b) => a % b)],
24986 [ts__default["default"].SyntaxKind.AmpersandToken, literalBinaryOp((a, b) => a & b)],
24987 [ts__default["default"].SyntaxKind.BarToken, literalBinaryOp((a, b) => a | b)],
24988 [ts__default["default"].SyntaxKind.CaretToken, literalBinaryOp((a, b) => a ^ b)],
24989 [ts__default["default"].SyntaxKind.LessThanToken, literalBinaryOp((a, b) => a < b)],
24990 [ts__default["default"].SyntaxKind.LessThanEqualsToken, literalBinaryOp((a, b) => a <= b)],
24991 [ts__default["default"].SyntaxKind.GreaterThanToken, literalBinaryOp((a, b) => a > b)],
24992 [ts__default["default"].SyntaxKind.GreaterThanEqualsToken, literalBinaryOp((a, b) => a >= b)],
24993 [ts__default["default"].SyntaxKind.EqualsEqualsToken, literalBinaryOp((a, b) => a == b)],
24994 [ts__default["default"].SyntaxKind.EqualsEqualsEqualsToken, literalBinaryOp((a, b) => a === b)],
24995 [ts__default["default"].SyntaxKind.ExclamationEqualsToken, literalBinaryOp((a, b) => a != b)],
24996 [ts__default["default"].SyntaxKind.ExclamationEqualsEqualsToken, literalBinaryOp((a, b) => a !== b)],
24997 [ts__default["default"].SyntaxKind.LessThanLessThanToken, literalBinaryOp((a, b) => a << b)],
24998 [ts__default["default"].SyntaxKind.GreaterThanGreaterThanToken, literalBinaryOp((a, b) => a >> b)],
24999 [ts__default["default"].SyntaxKind.GreaterThanGreaterThanGreaterThanToken, literalBinaryOp((a, b) => a >>> b)],
25000 [ts__default["default"].SyntaxKind.AsteriskAsteriskToken, literalBinaryOp((a, b) => Math.pow(a, b))],
25001 [ts__default["default"].SyntaxKind.AmpersandAmpersandToken, referenceBinaryOp((a, b) => a && b)],
25002 [ts__default["default"].SyntaxKind.BarBarToken, referenceBinaryOp((a, b) => a || b)]
25003 ]);
25004 const UNARY_OPERATORS$2 = new Map([
25005 [ts__default["default"].SyntaxKind.TildeToken, a => ~a], [ts__default["default"].SyntaxKind.MinusToken, a => -a],
25006 [ts__default["default"].SyntaxKind.PlusToken, a => +a], [ts__default["default"].SyntaxKind.ExclamationToken, a => !a]
25007 ]);
25008 class StaticInterpreter {
25009 constructor(host, checker, dependencyTracker) {
25010 this.host = host;
25011 this.checker = checker;
25012 this.dependencyTracker = dependencyTracker;
25013 }
25014 visit(node, context) {
25015 return this.visitExpression(node, context);
25016 }
25017 visitExpression(node, context) {
25018 let result;
25019 if (node.kind === ts__default["default"].SyntaxKind.TrueKeyword) {
25020 return true;
25021 }
25022 else if (node.kind === ts__default["default"].SyntaxKind.FalseKeyword) {
25023 return false;
25024 }
25025 else if (node.kind === ts__default["default"].SyntaxKind.NullKeyword) {
25026 return null;
25027 }
25028 else if (ts__default["default"].isStringLiteral(node)) {
25029 return node.text;
25030 }
25031 else if (ts__default["default"].isNoSubstitutionTemplateLiteral(node)) {
25032 return node.text;
25033 }
25034 else if (ts__default["default"].isTemplateExpression(node)) {
25035 result = this.visitTemplateExpression(node, context);
25036 }
25037 else if (ts__default["default"].isNumericLiteral(node)) {
25038 return parseFloat(node.text);
25039 }
25040 else if (ts__default["default"].isObjectLiteralExpression(node)) {
25041 result = this.visitObjectLiteralExpression(node, context);
25042 }
25043 else if (ts__default["default"].isIdentifier(node)) {
25044 result = this.visitIdentifier(node, context);
25045 }
25046 else if (ts__default["default"].isPropertyAccessExpression(node)) {
25047 result = this.visitPropertyAccessExpression(node, context);
25048 }
25049 else if (ts__default["default"].isCallExpression(node)) {
25050 result = this.visitCallExpression(node, context);
25051 }
25052 else if (ts__default["default"].isConditionalExpression(node)) {
25053 result = this.visitConditionalExpression(node, context);
25054 }
25055 else if (ts__default["default"].isPrefixUnaryExpression(node)) {
25056 result = this.visitPrefixUnaryExpression(node, context);
25057 }
25058 else if (ts__default["default"].isBinaryExpression(node)) {
25059 result = this.visitBinaryExpression(node, context);
25060 }
25061 else if (ts__default["default"].isArrayLiteralExpression(node)) {
25062 result = this.visitArrayLiteralExpression(node, context);
25063 }
25064 else if (ts__default["default"].isParenthesizedExpression(node)) {
25065 result = this.visitParenthesizedExpression(node, context);
25066 }
25067 else if (ts__default["default"].isElementAccessExpression(node)) {
25068 result = this.visitElementAccessExpression(node, context);
25069 }
25070 else if (ts__default["default"].isAsExpression(node)) {
25071 result = this.visitExpression(node.expression, context);
25072 }
25073 else if (ts__default["default"].isNonNullExpression(node)) {
25074 result = this.visitExpression(node.expression, context);
25075 }
25076 else if (this.host.isClass(node)) {
25077 result = this.visitDeclaration(node, context);
25078 }
25079 else {
25080 return DynamicValue.fromUnsupportedSyntax(node);
25081 }
25082 if (result instanceof DynamicValue && result.node !== node) {
25083 return DynamicValue.fromDynamicInput(node, result);
25084 }
25085 return result;
25086 }
25087 visitArrayLiteralExpression(node, context) {
25088 const array = [];
25089 for (let i = 0; i < node.elements.length; i++) {
25090 const element = node.elements[i];
25091 if (ts__default["default"].isSpreadElement(element)) {
25092 array.push(...this.visitSpreadElement(element, context));
25093 }
25094 else {
25095 array.push(this.visitExpression(element, context));
25096 }
25097 }
25098 return array;
25099 }
25100 visitObjectLiteralExpression(node, context) {
25101 const map = new Map();
25102 for (let i = 0; i < node.properties.length; i++) {
25103 const property = node.properties[i];
25104 if (ts__default["default"].isPropertyAssignment(property)) {
25105 const name = this.stringNameFromPropertyName(property.name, context);
25106 // Check whether the name can be determined statically.
25107 if (name === undefined) {
25108 return DynamicValue.fromDynamicInput(node, DynamicValue.fromDynamicString(property.name));
25109 }
25110 map.set(name, this.visitExpression(property.initializer, context));
25111 }
25112 else if (ts__default["default"].isShorthandPropertyAssignment(property)) {
25113 const symbol = this.checker.getShorthandAssignmentValueSymbol(property);
25114 if (symbol === undefined || symbol.valueDeclaration === undefined) {
25115 map.set(property.name.text, DynamicValue.fromUnknown(property));
25116 }
25117 else {
25118 map.set(property.name.text, this.visitDeclaration(symbol.valueDeclaration, context));
25119 }
25120 }
25121 else if (ts__default["default"].isSpreadAssignment(property)) {
25122 const spread = this.visitExpression(property.expression, context);
25123 if (spread instanceof DynamicValue) {
25124 return DynamicValue.fromDynamicInput(node, spread);
25125 }
25126 else if (spread instanceof Map) {
25127 spread.forEach((value, key) => map.set(key, value));
25128 }
25129 else if (spread instanceof ResolvedModule) {
25130 spread.getExports().forEach((value, key) => map.set(key, value));
25131 }
25132 else {
25133 return DynamicValue.fromDynamicInput(node, DynamicValue.fromInvalidExpressionType(property, spread));
25134 }
25135 }
25136 else {
25137 return DynamicValue.fromUnknown(node);
25138 }
25139 }
25140 return map;
25141 }
25142 visitTemplateExpression(node, context) {
25143 const pieces = [node.head.text];
25144 for (let i = 0; i < node.templateSpans.length; i++) {
25145 const span = node.templateSpans[i];
25146 const value = literal(this.visit(span.expression, context), () => DynamicValue.fromDynamicString(span.expression));
25147 if (value instanceof DynamicValue) {
25148 return DynamicValue.fromDynamicInput(node, value);
25149 }
25150 pieces.push(`${value}`, span.literal.text);
25151 }
25152 return pieces.join('');
25153 }
25154 visitIdentifier(node, context) {
25155 const decl = this.host.getDeclarationOfIdentifier(node);
25156 if (decl === null) {
25157 if (node.originalKeywordKind === ts__default["default"].SyntaxKind.UndefinedKeyword) {
25158 return undefined;
25159 }
25160 else {
25161 // Check if the symbol here is imported.
25162 if (this.dependencyTracker !== null && this.host.getImportOfIdentifier(node) !== null) {
25163 // It was, but no declaration for the node could be found. This means that the dependency
25164 // graph for the current file cannot be properly updated to account for this (broken)
25165 // import. Instead, the originating file is reported as failing dependency analysis,
25166 // ensuring that future compilations will always attempt to re-resolve the previously
25167 // broken identifier.
25168 this.dependencyTracker.recordDependencyAnalysisFailure(context.originatingFile);
25169 }
25170 return DynamicValue.fromUnknownIdentifier(node);
25171 }
25172 }
25173 if (decl.known !== null) {
25174 return resolveKnownDeclaration(decl.known);
25175 }
25176 else if (isConcreteDeclaration(decl) && decl.identity !== null &&
25177 decl.identity.kind === 0 /* DownleveledEnum */) {
25178 return this.getResolvedEnum(decl.node, decl.identity.enumMembers, context);
25179 }
25180 const declContext = { ...context, ...joinModuleContext(context, node, decl) };
25181 const result = this.visitAmbiguousDeclaration(decl, declContext);
25182 if (result instanceof Reference) {
25183 // Only record identifiers to non-synthetic references. Synthetic references may not have the
25184 // same value at runtime as they do at compile time, so it's not legal to refer to them by the
25185 // identifier here.
25186 if (!result.synthetic) {
25187 result.addIdentifier(node);
25188 }
25189 }
25190 else if (result instanceof DynamicValue) {
25191 return DynamicValue.fromDynamicInput(node, result);
25192 }
25193 return result;
25194 }
25195 visitDeclaration(node, context) {
25196 if (this.dependencyTracker !== null) {
25197 this.dependencyTracker.addDependency(context.originatingFile, node.getSourceFile());
25198 }
25199 if (this.host.isClass(node)) {
25200 return this.getReference(node, context);
25201 }
25202 else if (ts__default["default"].isVariableDeclaration(node)) {
25203 return this.visitVariableDeclaration(node, context);
25204 }
25205 else if (ts__default["default"].isParameter(node) && context.scope.has(node)) {
25206 return context.scope.get(node);
25207 }
25208 else if (ts__default["default"].isExportAssignment(node)) {
25209 return this.visitExpression(node.expression, context);
25210 }
25211 else if (ts__default["default"].isEnumDeclaration(node)) {
25212 return this.visitEnumDeclaration(node, context);
25213 }
25214 else if (ts__default["default"].isSourceFile(node)) {
25215 return this.visitSourceFile(node, context);
25216 }
25217 else if (ts__default["default"].isBindingElement(node)) {
25218 return this.visitBindingElement(node, context);
25219 }
25220 else {
25221 return this.getReference(node, context);
25222 }
25223 }
25224 visitVariableDeclaration(node, context) {
25225 const value = this.host.getVariableValue(node);
25226 if (value !== null) {
25227 return this.visitExpression(value, context);
25228 }
25229 else if (isVariableDeclarationDeclared(node)) {
25230 // If the declaration has a literal type that can be statically reduced to a value, resolve to
25231 // that value. If not, the historical behavior for variable declarations is to return a
25232 // `Reference` to the variable, as the consumer could use it in a context where knowing its
25233 // static value is not necessary.
25234 //
25235 // Arguably, since the value cannot be statically determined, we should return a
25236 // `DynamicValue`. This returns a `Reference` because it's the same behavior as before
25237 // `visitType` was introduced.
25238 //
25239 // TODO(zarend): investigate switching to a `DynamicValue` and verify this won't break any
25240 // use cases, especially in ngcc
25241 if (node.type !== undefined) {
25242 const evaluatedType = this.visitType(node.type, context);
25243 if (!(evaluatedType instanceof DynamicValue)) {
25244 return evaluatedType;
25245 }
25246 }
25247 return this.getReference(node, context);
25248 }
25249 else {
25250 return undefined;
25251 }
25252 }
25253 visitEnumDeclaration(node, context) {
25254 const enumRef = this.getReference(node, context);
25255 const map = new Map();
25256 node.members.forEach(member => {
25257 const name = this.stringNameFromPropertyName(member.name, context);
25258 if (name !== undefined) {
25259 const resolved = member.initializer && this.visit(member.initializer, context);
25260 map.set(name, new EnumValue(enumRef, name, resolved));
25261 }
25262 });
25263 return map;
25264 }
25265 visitElementAccessExpression(node, context) {
25266 const lhs = this.visitExpression(node.expression, context);
25267 if (lhs instanceof DynamicValue) {
25268 return DynamicValue.fromDynamicInput(node, lhs);
25269 }
25270 const rhs = this.visitExpression(node.argumentExpression, context);
25271 if (rhs instanceof DynamicValue) {
25272 return DynamicValue.fromDynamicInput(node, rhs);
25273 }
25274 if (typeof rhs !== 'string' && typeof rhs !== 'number') {
25275 return DynamicValue.fromInvalidExpressionType(node, rhs);
25276 }
25277 return this.accessHelper(node, lhs, rhs, context);
25278 }
25279 visitPropertyAccessExpression(node, context) {
25280 const lhs = this.visitExpression(node.expression, context);
25281 const rhs = node.name.text;
25282 // TODO: handle reference to class declaration.
25283 if (lhs instanceof DynamicValue) {
25284 return DynamicValue.fromDynamicInput(node, lhs);
25285 }
25286 return this.accessHelper(node, lhs, rhs, context);
25287 }
25288 visitSourceFile(node, context) {
25289 const declarations = this.host.getExportsOfModule(node);
25290 if (declarations === null) {
25291 return DynamicValue.fromUnknown(node);
25292 }
25293 return new ResolvedModule(declarations, decl => {
25294 if (decl.known !== null) {
25295 return resolveKnownDeclaration(decl.known);
25296 }
25297 const declContext = {
25298 ...context,
25299 ...joinModuleContext(context, node, decl),
25300 };
25301 // Visit both concrete and inline declarations.
25302 return this.visitAmbiguousDeclaration(decl, declContext);
25303 });
25304 }
25305 visitAmbiguousDeclaration(decl, declContext) {
25306 return decl.kind === 1 /* Inline */ && decl.implementation !== undefined &&
25307 !isDeclaration(decl.implementation) ?
25308 // Inline declarations whose `implementation` is a `ts.Expression` should be visited as
25309 // an expression.
25310 this.visitExpression(decl.implementation, declContext) :
25311 // Otherwise just visit the `node` as a declaration.
25312 this.visitDeclaration(decl.node, declContext);
25313 }
25314 accessHelper(node, lhs, rhs, context) {
25315 const strIndex = `${rhs}`;
25316 if (lhs instanceof Map) {
25317 if (lhs.has(strIndex)) {
25318 return lhs.get(strIndex);
25319 }
25320 else {
25321 return undefined;
25322 }
25323 }
25324 else if (lhs instanceof ResolvedModule) {
25325 return lhs.getExport(strIndex);
25326 }
25327 else if (Array.isArray(lhs)) {
25328 if (rhs === 'length') {
25329 return lhs.length;
25330 }
25331 else if (rhs === 'slice') {
25332 return new ArraySliceBuiltinFn(lhs);
25333 }
25334 else if (rhs === 'concat') {
25335 return new ArrayConcatBuiltinFn(lhs);
25336 }
25337 if (typeof rhs !== 'number' || !Number.isInteger(rhs)) {
25338 return DynamicValue.fromInvalidExpressionType(node, rhs);
25339 }
25340 return lhs[rhs];
25341 }
25342 else if (lhs instanceof Reference) {
25343 const ref = lhs.node;
25344 if (this.host.isClass(ref)) {
25345 const module = owningModule(context, lhs.bestGuessOwningModule);
25346 let value = undefined;
25347 const member = this.host.getMembersOfClass(ref).find(member => member.isStatic && member.name === strIndex);
25348 if (member !== undefined) {
25349 if (member.value !== null) {
25350 value = this.visitExpression(member.value, context);
25351 }
25352 else if (member.implementation !== null) {
25353 value = new Reference(member.implementation, module);
25354 }
25355 else if (member.node) {
25356 value = new Reference(member.node, module);
25357 }
25358 }
25359 return value;
25360 }
25361 else if (isDeclaration(ref)) {
25362 return DynamicValue.fromDynamicInput(node, DynamicValue.fromExternalReference(ref, lhs));
25363 }
25364 }
25365 else if (lhs instanceof DynamicValue) {
25366 return DynamicValue.fromDynamicInput(node, lhs);
25367 }
25368 return DynamicValue.fromUnknown(node);
25369 }
25370 visitCallExpression(node, context) {
25371 const lhs = this.visitExpression(node.expression, context);
25372 if (lhs instanceof DynamicValue) {
25373 return DynamicValue.fromDynamicInput(node, lhs);
25374 }
25375 // If the call refers to a builtin function, attempt to evaluate the function.
25376 if (lhs instanceof KnownFn) {
25377 return lhs.evaluate(node, this.evaluateFunctionArguments(node, context));
25378 }
25379 if (!(lhs instanceof Reference)) {
25380 return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
25381 }
25382 const fn = this.host.getDefinitionOfFunction(lhs.node);
25383 if (fn === null) {
25384 return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
25385 }
25386 if (!isFunctionOrMethodReference(lhs)) {
25387 return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
25388 }
25389 // If the function is foreign (declared through a d.ts file), attempt to resolve it with the
25390 // foreignFunctionResolver, if one is specified.
25391 if (fn.body === null) {
25392 let expr = null;
25393 if (context.foreignFunctionResolver) {
25394 expr = context.foreignFunctionResolver(lhs, node.arguments);
25395 }
25396 if (expr === null) {
25397 return DynamicValue.fromDynamicInput(node, DynamicValue.fromExternalReference(node.expression, lhs));
25398 }
25399 // If the foreign expression occurs in a different file, then assume that the owning module
25400 // of the call expression should also be used for the resolved foreign expression.
25401 if (expr.getSourceFile() !== node.expression.getSourceFile() &&
25402 lhs.bestGuessOwningModule !== null) {
25403 context = {
25404 ...context,
25405 absoluteModuleName: lhs.bestGuessOwningModule.specifier,
25406 resolutionContext: lhs.bestGuessOwningModule.resolutionContext,
25407 };
25408 }
25409 return this.visitFfrExpression(expr, context);
25410 }
25411 let res = this.visitFunctionBody(node, fn, context);
25412 // If the result of attempting to resolve the function body was a DynamicValue, attempt to use
25413 // the foreignFunctionResolver if one is present. This could still potentially yield a usable
25414 // value.
25415 if (res instanceof DynamicValue && context.foreignFunctionResolver !== undefined) {
25416 const ffrExpr = context.foreignFunctionResolver(lhs, node.arguments);
25417 if (ffrExpr !== null) {
25418 // The foreign function resolver was able to extract an expression from this function. See
25419 // if that expression leads to a non-dynamic result.
25420 const ffrRes = this.visitFfrExpression(ffrExpr, context);
25421 if (!(ffrRes instanceof DynamicValue)) {
25422 // FFR yielded an actual result that's not dynamic, so use that instead of the original
25423 // resolution.
25424 res = ffrRes;
25425 }
25426 }
25427 }
25428 return res;
25429 }
25430 /**
25431 * Visit an expression which was extracted from a foreign-function resolver.
25432 *
25433 * This will process the result and ensure it's correct for FFR-resolved values, including marking
25434 * `Reference`s as synthetic.
25435 */
25436 visitFfrExpression(expr, context) {
25437 const res = this.visitExpression(expr, context);
25438 if (res instanceof Reference) {
25439 // This Reference was created synthetically, via a foreign function resolver. The real
25440 // runtime value of the function expression may be different than the foreign function
25441 // resolved value, so mark the Reference as synthetic to avoid it being misinterpreted.
25442 res.synthetic = true;
25443 }
25444 return res;
25445 }
25446 visitFunctionBody(node, fn, context) {
25447 if (fn.body === null) {
25448 return DynamicValue.fromUnknown(node);
25449 }
25450 else if (fn.body.length !== 1 || !ts__default["default"].isReturnStatement(fn.body[0])) {
25451 return DynamicValue.fromComplexFunctionCall(node, fn);
25452 }
25453 const ret = fn.body[0];
25454 const args = this.evaluateFunctionArguments(node, context);
25455 const newScope = new Map();
25456 const calleeContext = { ...context, scope: newScope };
25457 fn.parameters.forEach((param, index) => {
25458 let arg = args[index];
25459 if (param.node.dotDotDotToken !== undefined) {
25460 arg = args.slice(index);
25461 }
25462 if (arg === undefined && param.initializer !== null) {
25463 arg = this.visitExpression(param.initializer, calleeContext);
25464 }
25465 newScope.set(param.node, arg);
25466 });
25467 return ret.expression !== undefined ? this.visitExpression(ret.expression, calleeContext) :
25468 undefined;
25469 }
25470 visitConditionalExpression(node, context) {
25471 const condition = this.visitExpression(node.condition, context);
25472 if (condition instanceof DynamicValue) {
25473 return DynamicValue.fromDynamicInput(node, condition);
25474 }
25475 if (condition) {
25476 return this.visitExpression(node.whenTrue, context);
25477 }
25478 else {
25479 return this.visitExpression(node.whenFalse, context);
25480 }
25481 }
25482 visitPrefixUnaryExpression(node, context) {
25483 const operatorKind = node.operator;
25484 if (!UNARY_OPERATORS$2.has(operatorKind)) {
25485 return DynamicValue.fromUnsupportedSyntax(node);
25486 }
25487 const op = UNARY_OPERATORS$2.get(operatorKind);
25488 const value = this.visitExpression(node.operand, context);
25489 if (value instanceof DynamicValue) {
25490 return DynamicValue.fromDynamicInput(node, value);
25491 }
25492 else {
25493 return op(value);
25494 }
25495 }
25496 visitBinaryExpression(node, context) {
25497 const tokenKind = node.operatorToken.kind;
25498 if (!BINARY_OPERATORS$2.has(tokenKind)) {
25499 return DynamicValue.fromUnsupportedSyntax(node);
25500 }
25501 const opRecord = BINARY_OPERATORS$2.get(tokenKind);
25502 let lhs, rhs;
25503 if (opRecord.literal) {
25504 lhs = literal(this.visitExpression(node.left, context), value => DynamicValue.fromInvalidExpressionType(node.left, value));
25505 rhs = literal(this.visitExpression(node.right, context), value => DynamicValue.fromInvalidExpressionType(node.right, value));
25506 }
25507 else {
25508 lhs = this.visitExpression(node.left, context);
25509 rhs = this.visitExpression(node.right, context);
25510 }
25511 if (lhs instanceof DynamicValue) {
25512 return DynamicValue.fromDynamicInput(node, lhs);
25513 }
25514 else if (rhs instanceof DynamicValue) {
25515 return DynamicValue.fromDynamicInput(node, rhs);
25516 }
25517 else {
25518 return opRecord.op(lhs, rhs);
25519 }
25520 }
25521 visitParenthesizedExpression(node, context) {
25522 return this.visitExpression(node.expression, context);
25523 }
25524 evaluateFunctionArguments(node, context) {
25525 const args = [];
25526 for (const arg of node.arguments) {
25527 if (ts__default["default"].isSpreadElement(arg)) {
25528 args.push(...this.visitSpreadElement(arg, context));
25529 }
25530 else {
25531 args.push(this.visitExpression(arg, context));
25532 }
25533 }
25534 return args;
25535 }
25536 visitSpreadElement(node, context) {
25537 const spread = this.visitExpression(node.expression, context);
25538 if (spread instanceof DynamicValue) {
25539 return [DynamicValue.fromDynamicInput(node, spread)];
25540 }
25541 else if (!Array.isArray(spread)) {
25542 return [DynamicValue.fromInvalidExpressionType(node, spread)];
25543 }
25544 else {
25545 return spread;
25546 }
25547 }
25548 visitBindingElement(node, context) {
25549 const path = [];
25550 let closestDeclaration = node;
25551 while (ts__default["default"].isBindingElement(closestDeclaration) ||
25552 ts__default["default"].isArrayBindingPattern(closestDeclaration) ||
25553 ts__default["default"].isObjectBindingPattern(closestDeclaration)) {
25554 if (ts__default["default"].isBindingElement(closestDeclaration)) {
25555 path.unshift(closestDeclaration);
25556 }
25557 closestDeclaration = closestDeclaration.parent;
25558 }
25559 if (!ts__default["default"].isVariableDeclaration(closestDeclaration) ||
25560 closestDeclaration.initializer === undefined) {
25561 return DynamicValue.fromUnknown(node);
25562 }
25563 let value = this.visit(closestDeclaration.initializer, context);
25564 for (const element of path) {
25565 let key;
25566 if (ts__default["default"].isArrayBindingPattern(element.parent)) {
25567 key = element.parent.elements.indexOf(element);
25568 }
25569 else {
25570 const name = element.propertyName || element.name;
25571 if (ts__default["default"].isIdentifier(name)) {
25572 key = name.text;
25573 }
25574 else {
25575 return DynamicValue.fromUnknown(element);
25576 }
25577 }
25578 value = this.accessHelper(element, value, key, context);
25579 if (value instanceof DynamicValue) {
25580 return value;
25581 }
25582 }
25583 return value;
25584 }
25585 stringNameFromPropertyName(node, context) {
25586 if (ts__default["default"].isIdentifier(node) || ts__default["default"].isStringLiteral(node) || ts__default["default"].isNumericLiteral(node)) {
25587 return node.text;
25588 }
25589 else if (ts__default["default"].isComputedPropertyName(node)) {
25590 const literal = this.visitExpression(node.expression, context);
25591 return typeof literal === 'string' ? literal : undefined;
25592 }
25593 else {
25594 return undefined;
25595 }
25596 }
25597 getResolvedEnum(node, enumMembers, context) {
25598 const enumRef = this.getReference(node, context);
25599 const map = new Map();
25600 enumMembers.forEach(member => {
25601 const name = this.stringNameFromPropertyName(member.name, context);
25602 if (name !== undefined) {
25603 const resolved = this.visit(member.initializer, context);
25604 map.set(name, new EnumValue(enumRef, name, resolved));
25605 }
25606 });
25607 return map;
25608 }
25609 getReference(node, context) {
25610 return new Reference(node, owningModule(context));
25611 }
25612 visitType(node, context) {
25613 if (ts__default["default"].isLiteralTypeNode(node)) {
25614 return this.visitExpression(node.literal, context);
25615 }
25616 else if (ts__default["default"].isTupleTypeNode(node)) {
25617 return this.visitTupleType(node, context);
25618 }
25619 else if (ts__default["default"].isNamedTupleMember(node)) {
25620 return this.visitType(node.type, context);
25621 }
25622 return DynamicValue.fromDynamicType(node);
25623 }
25624 visitTupleType(node, context) {
25625 const res = [];
25626 for (const elem of node.elements) {
25627 res.push(this.visitType(elem, context));
25628 }
25629 return res;
25630 }
25631 }
25632 function isFunctionOrMethodReference(ref) {
25633 return ts__default["default"].isFunctionDeclaration(ref.node) || ts__default["default"].isMethodDeclaration(ref.node) ||
25634 ts__default["default"].isFunctionExpression(ref.node);
25635 }
25636 function literal(value, reject) {
25637 if (value instanceof EnumValue) {
25638 value = value.resolved;
25639 }
25640 if (value instanceof DynamicValue || value === null || value === undefined ||
25641 typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
25642 return value;
25643 }
25644 return reject(value);
25645 }
25646 function isVariableDeclarationDeclared(node) {
25647 if (node.parent === undefined || !ts__default["default"].isVariableDeclarationList(node.parent)) {
25648 return false;
25649 }
25650 const declList = node.parent;
25651 if (declList.parent === undefined || !ts__default["default"].isVariableStatement(declList.parent)) {
25652 return false;
25653 }
25654 const varStmt = declList.parent;
25655 return varStmt.modifiers !== undefined &&
25656 varStmt.modifiers.some(mod => mod.kind === ts__default["default"].SyntaxKind.DeclareKeyword);
25657 }
25658 const EMPTY = {};
25659 function joinModuleContext(existing, node, decl) {
25660 if (decl.viaModule !== null && decl.viaModule !== existing.absoluteModuleName) {
25661 return {
25662 absoluteModuleName: decl.viaModule,
25663 resolutionContext: node.getSourceFile().fileName,
25664 };
25665 }
25666 else {
25667 return EMPTY;
25668 }
25669 }
25670 function owningModule(context, override = null) {
25671 let specifier = context.absoluteModuleName;
25672 if (override !== null) {
25673 specifier = override.specifier;
25674 }
25675 if (specifier !== null) {
25676 return {
25677 specifier,
25678 resolutionContext: context.resolutionContext,
25679 };
25680 }
25681 else {
25682 return null;
25683 }
25684 }
25685
25686 /**
25687 * @license
25688 * Copyright Google LLC All Rights Reserved.
25689 *
25690 * Use of this source code is governed by an MIT-style license that can be
25691 * found in the LICENSE file at https://angular.io/license
25692 */
25693 class PartialEvaluator {
25694 constructor(host, checker, dependencyTracker) {
25695 this.host = host;
25696 this.checker = checker;
25697 this.dependencyTracker = dependencyTracker;
25698 }
25699 evaluate(expr, foreignFunctionResolver) {
25700 const interpreter = new StaticInterpreter(this.host, this.checker, this.dependencyTracker);
25701 const sourceFile = expr.getSourceFile();
25702 return interpreter.visit(expr, {
25703 originatingFile: sourceFile,
25704 absoluteModuleName: null,
25705 resolutionContext: sourceFile.fileName,
25706 scope: new Map(),
25707 foreignFunctionResolver,
25708 });
25709 }
25710 }
25711
25712 /**
25713 * @license
25714 * Copyright Google LLC All Rights Reserved.
25715 *
25716 * Use of this source code is governed by an MIT-style license that can be
25717 * found in the LICENSE file at https://angular.io/license
25718 */
25719 /**
25720 * A phase of compilation for which time is tracked in a distinct bucket.
25721 */
25722 var PerfPhase;
25723 (function (PerfPhase) {
25724 /**
25725 * The "default" phase which tracks time not spent in any other phase.
25726 */
25727 PerfPhase[PerfPhase["Unaccounted"] = 0] = "Unaccounted";
25728 /**
25729 * Time spent setting up the compiler, before a TypeScript program is created.
25730 *
25731 * This includes operations like configuring the `ts.CompilerHost` and any wrappers.
25732 */
25733 PerfPhase[PerfPhase["Setup"] = 1] = "Setup";
25734 /**
25735 * Time spent in `ts.createProgram`, including reading and parsing `ts.SourceFile`s in the
25736 * `ts.CompilerHost`.
25737 *
25738 * This might be an incremental program creation operation.
25739 */
25740 PerfPhase[PerfPhase["TypeScriptProgramCreate"] = 2] = "TypeScriptProgramCreate";
25741 /**
25742 * Time spent reconciling the contents of an old `ts.Program` with the new incremental one.
25743 *
25744 * Only present in incremental compilations.
25745 */
25746 PerfPhase[PerfPhase["Reconciliation"] = 3] = "Reconciliation";
25747 /**
25748 * Time spent updating an `NgCompiler` instance with a resource-only change.
25749 *
25750 * Only present in incremental compilations where the change was resource-only.
25751 */
25752 PerfPhase[PerfPhase["ResourceUpdate"] = 4] = "ResourceUpdate";
25753 /**
25754 * Time spent calculating the plain TypeScript diagnostics (structural and semantic).
25755 */
25756 PerfPhase[PerfPhase["TypeScriptDiagnostics"] = 5] = "TypeScriptDiagnostics";
25757 /**
25758 * Time spent in Angular analysis of individual classes in the program.
25759 */
25760 PerfPhase[PerfPhase["Analysis"] = 6] = "Analysis";
25761 /**
25762 * Time spent in Angular global analysis (synthesis of analysis information into a complete
25763 * understanding of the program).
25764 */
25765 PerfPhase[PerfPhase["Resolve"] = 7] = "Resolve";
25766 /**
25767 * Time spent building the import graph of the program in order to perform cycle detection.
25768 */
25769 PerfPhase[PerfPhase["CycleDetection"] = 8] = "CycleDetection";
25770 /**
25771 * Time spent generating the text of Type Check Blocks in order to perform template type checking.
25772 */
25773 PerfPhase[PerfPhase["TcbGeneration"] = 9] = "TcbGeneration";
25774 /**
25775 * Time spent updating the `ts.Program` with new Type Check Block code.
25776 */
25777 PerfPhase[PerfPhase["TcbUpdateProgram"] = 10] = "TcbUpdateProgram";
25778 /**
25779 * Time spent by TypeScript performing its emit operations, including downleveling and writing
25780 * output files.
25781 */
25782 PerfPhase[PerfPhase["TypeScriptEmit"] = 11] = "TypeScriptEmit";
25783 /**
25784 * Time spent by Angular performing code transformations of ASTs as they're about to be emitted.
25785 *
25786 * This includes the actual code generation step for templates, and occurs during the emit phase
25787 * (but is tracked separately from `TypeScriptEmit` time).
25788 */
25789 PerfPhase[PerfPhase["Compile"] = 12] = "Compile";
25790 /**
25791 * Time spent performing a `TemplateTypeChecker` autocompletion operation.
25792 */
25793 PerfPhase[PerfPhase["TtcAutocompletion"] = 13] = "TtcAutocompletion";
25794 /**
25795 * Time spent computing template type-checking diagnostics.
25796 */
25797 PerfPhase[PerfPhase["TtcDiagnostics"] = 14] = "TtcDiagnostics";
25798 /**
25799 * Time spent getting a `Symbol` from the `TemplateTypeChecker`.
25800 */
25801 PerfPhase[PerfPhase["TtcSymbol"] = 15] = "TtcSymbol";
25802 /**
25803 * Time spent by the Angular Language Service calculating a "get references" or a renaming
25804 * operation.
25805 */
25806 PerfPhase[PerfPhase["LsReferencesAndRenames"] = 16] = "LsReferencesAndRenames";
25807 /**
25808 * Time spent by the Angular Language Service calculating a "quick info" operation.
25809 */
25810 PerfPhase[PerfPhase["LsQuickInfo"] = 17] = "LsQuickInfo";
25811 /**
25812 * Time spent by the Angular Language Service calculating a "get type definition" or "get
25813 * definition" operation.
25814 */
25815 PerfPhase[PerfPhase["LsDefinition"] = 18] = "LsDefinition";
25816 /**
25817 * Time spent by the Angular Language Service calculating a "get completions" (AKA autocomplete)
25818 * operation.
25819 */
25820 PerfPhase[PerfPhase["LsCompletions"] = 19] = "LsCompletions";
25821 /**
25822 * Time spent by the Angular Language Service calculating a "view template typecheck block"
25823 * operation.
25824 */
25825 PerfPhase[PerfPhase["LsTcb"] = 20] = "LsTcb";
25826 /**
25827 * Time spent by the Angular Language Service calculating diagnostics.
25828 */
25829 PerfPhase[PerfPhase["LsDiagnostics"] = 21] = "LsDiagnostics";
25830 /**
25831 * Time spent by the Angular Language Service calculating a "get component locations for template"
25832 * operation.
25833 */
25834 PerfPhase[PerfPhase["LsComponentLocations"] = 22] = "LsComponentLocations";
25835 /**
25836 * Time spent by the Angular Language Service calculating signature help.
25837 */
25838 PerfPhase[PerfPhase["LsSignatureHelp"] = 23] = "LsSignatureHelp";
25839 /**
25840 * Tracks the number of `PerfPhase`s, and must appear at the end of the list.
25841 */
25842 PerfPhase[PerfPhase["LAST"] = 24] = "LAST";
25843 })(PerfPhase || (PerfPhase = {}));
25844 /**
25845 * Represents some occurrence during compilation, and is tracked with a counter.
25846 */
25847 var PerfEvent;
25848 (function (PerfEvent) {
25849 /**
25850 * Counts the number of `.d.ts` files in the program.
25851 */
25852 PerfEvent[PerfEvent["InputDtsFile"] = 0] = "InputDtsFile";
25853 /**
25854 * Counts the number of non-`.d.ts` files in the program.
25855 */
25856 PerfEvent[PerfEvent["InputTsFile"] = 1] = "InputTsFile";
25857 /**
25858 * An `@Component` class was analyzed.
25859 */
25860 PerfEvent[PerfEvent["AnalyzeComponent"] = 2] = "AnalyzeComponent";
25861 /**
25862 * An `@Directive` class was analyzed.
25863 */
25864 PerfEvent[PerfEvent["AnalyzeDirective"] = 3] = "AnalyzeDirective";
25865 /**
25866 * An `@Injectable` class was analyzed.
25867 */
25868 PerfEvent[PerfEvent["AnalyzeInjectable"] = 4] = "AnalyzeInjectable";
25869 /**
25870 * An `@NgModule` class was analyzed.
25871 */
25872 PerfEvent[PerfEvent["AnalyzeNgModule"] = 5] = "AnalyzeNgModule";
25873 /**
25874 * An `@Pipe` class was analyzed.
25875 */
25876 PerfEvent[PerfEvent["AnalyzePipe"] = 6] = "AnalyzePipe";
25877 /**
25878 * A trait was analyzed.
25879 *
25880 * In theory, this should be the sum of the `Analyze` counters for each decorator type.
25881 */
25882 PerfEvent[PerfEvent["TraitAnalyze"] = 7] = "TraitAnalyze";
25883 /**
25884 * A trait had a prior analysis available from an incremental program, and did not need to be
25885 * re-analyzed.
25886 */
25887 PerfEvent[PerfEvent["TraitReuseAnalysis"] = 8] = "TraitReuseAnalysis";
25888 /**
25889 * A `ts.SourceFile` directly changed between the prior program and a new incremental compilation.
25890 */
25891 PerfEvent[PerfEvent["SourceFilePhysicalChange"] = 9] = "SourceFilePhysicalChange";
25892 /**
25893 * A `ts.SourceFile` did not physically changed, but according to the file dependency graph, has
25894 * logically changed between the prior program and a new incremental compilation.
25895 */
25896 PerfEvent[PerfEvent["SourceFileLogicalChange"] = 10] = "SourceFileLogicalChange";
25897 /**
25898 * A `ts.SourceFile` has not logically changed and all of its analysis results were thus available
25899 * for reuse.
25900 */
25901 PerfEvent[PerfEvent["SourceFileReuseAnalysis"] = 11] = "SourceFileReuseAnalysis";
25902 /**
25903 * A Type Check Block (TCB) was generated.
25904 */
25905 PerfEvent[PerfEvent["GenerateTcb"] = 12] = "GenerateTcb";
25906 /**
25907 * A Type Check Block (TCB) could not be generated because inlining was disabled, and the block
25908 * would've required inlining.
25909 */
25910 PerfEvent[PerfEvent["SkipGenerateTcbNoInline"] = 13] = "SkipGenerateTcbNoInline";
25911 /**
25912 * A `.ngtypecheck.ts` file could be reused from the previous program and did not need to be
25913 * regenerated.
25914 */
25915 PerfEvent[PerfEvent["ReuseTypeCheckFile"] = 14] = "ReuseTypeCheckFile";
25916 /**
25917 * The template type-checking program required changes and had to be updated in an incremental
25918 * step.
25919 */
25920 PerfEvent[PerfEvent["UpdateTypeCheckProgram"] = 15] = "UpdateTypeCheckProgram";
25921 /**
25922 * The compiler was able to prove that a `ts.SourceFile` did not need to be re-emitted.
25923 */
25924 PerfEvent[PerfEvent["EmitSkipSourceFile"] = 16] = "EmitSkipSourceFile";
25925 /**
25926 * A `ts.SourceFile` was emitted.
25927 */
25928 PerfEvent[PerfEvent["EmitSourceFile"] = 17] = "EmitSourceFile";
25929 /**
25930 * Tracks the number of `PrefEvent`s, and must appear at the end of the list.
25931 */
25932 PerfEvent[PerfEvent["LAST"] = 18] = "LAST";
25933 })(PerfEvent || (PerfEvent = {}));
25934 /**
25935 * Represents a checkpoint during compilation at which the memory usage of the compiler should be
25936 * recorded.
25937 */
25938 var PerfCheckpoint;
25939 (function (PerfCheckpoint) {
25940 /**
25941 * The point at which the `PerfRecorder` was created, and ideally tracks memory used before any
25942 * compilation structures are created.
25943 */
25944 PerfCheckpoint[PerfCheckpoint["Initial"] = 0] = "Initial";
25945 /**
25946 * The point just after the `ts.Program` has been created.
25947 */
25948 PerfCheckpoint[PerfCheckpoint["TypeScriptProgramCreate"] = 1] = "TypeScriptProgramCreate";
25949 /**
25950 * The point just before Angular analysis starts.
25951 *
25952 * In the main usage pattern for the compiler, TypeScript diagnostics have been calculated at this
25953 * point, so the `ts.TypeChecker` has fully ingested the current program, all `ts.Type` structures
25954 * and `ts.Symbol`s have been created.
25955 */
25956 PerfCheckpoint[PerfCheckpoint["PreAnalysis"] = 2] = "PreAnalysis";
25957 /**
25958 * The point just after Angular analysis completes.
25959 */
25960 PerfCheckpoint[PerfCheckpoint["Analysis"] = 3] = "Analysis";
25961 /**
25962 * The point just after Angular resolution is complete.
25963 */
25964 PerfCheckpoint[PerfCheckpoint["Resolve"] = 4] = "Resolve";
25965 /**
25966 * The point just after Type Check Blocks (TCBs) have been generated.
25967 */
25968 PerfCheckpoint[PerfCheckpoint["TtcGeneration"] = 5] = "TtcGeneration";
25969 /**
25970 * The point just after the template type-checking program has been updated with any new TCBs.
25971 */
25972 PerfCheckpoint[PerfCheckpoint["TtcUpdateProgram"] = 6] = "TtcUpdateProgram";
25973 /**
25974 * The point just before emit begins.
25975 *
25976 * In the main usage pattern for the compiler, all template type-checking diagnostics have been
25977 * requested at this point.
25978 */
25979 PerfCheckpoint[PerfCheckpoint["PreEmit"] = 7] = "PreEmit";
25980 /**
25981 * The point just after the program has been fully emitted.
25982 */
25983 PerfCheckpoint[PerfCheckpoint["Emit"] = 8] = "Emit";
25984 /**
25985 * Tracks the number of `PerfCheckpoint`s, and must appear at the end of the list.
25986 */
25987 PerfCheckpoint[PerfCheckpoint["LAST"] = 9] = "LAST";
25988 })(PerfCheckpoint || (PerfCheckpoint = {}));
25989
25990 /**
25991 * @license
25992 * Copyright Google LLC All Rights Reserved.
25993 *
25994 * Use of this source code is governed by an MIT-style license that can be
25995 * found in the LICENSE file at https://angular.io/license
25996 */
25997 /// <reference types="node" />
25998 function mark() {
25999 return process.hrtime();
26000 }
26001 function timeSinceInMicros(mark) {
26002 const delta = process.hrtime(mark);
26003 return (delta[0] * 1000000) + Math.floor(delta[1] / 1000);
26004 }
26005
26006 /**
26007 * @license
26008 * Copyright Google LLC All Rights Reserved.
26009 *
26010 * Use of this source code is governed by an MIT-style license that can be
26011 * found in the LICENSE file at https://angular.io/license
26012 */
26013 /**
26014 * A `PerfRecorder` that actively tracks performance statistics.
26015 */
26016 class ActivePerfRecorder {
26017 constructor(zeroTime) {
26018 this.zeroTime = zeroTime;
26019 this.currentPhase = PerfPhase.Unaccounted;
26020 this.currentPhaseEntered = this.zeroTime;
26021 this.counters = Array(PerfEvent.LAST).fill(0);
26022 this.phaseTime = Array(PerfPhase.LAST).fill(0);
26023 this.bytes = Array(PerfCheckpoint.LAST).fill(0);
26024 // Take an initial memory snapshot before any other compilation work begins.
26025 this.memory(PerfCheckpoint.Initial);
26026 }
26027 /**
26028 * Creates an `ActivePerfRecoder` with its zero point set to the current time.
26029 */
26030 static zeroedToNow() {
26031 return new ActivePerfRecorder(mark());
26032 }
26033 reset() {
26034 this.counters = Array(PerfEvent.LAST).fill(0);
26035 this.phaseTime = Array(PerfPhase.LAST).fill(0);
26036 this.bytes = Array(PerfCheckpoint.LAST).fill(0);
26037 this.zeroTime = mark();
26038 this.currentPhase = PerfPhase.Unaccounted;
26039 this.currentPhaseEntered = this.zeroTime;
26040 }
26041 memory(after) {
26042 this.bytes[after] = process.memoryUsage().heapUsed;
26043 }
26044 phase(phase) {
26045 const previous = this.currentPhase;
26046 this.phaseTime[this.currentPhase] += timeSinceInMicros(this.currentPhaseEntered);
26047 this.currentPhase = phase;
26048 this.currentPhaseEntered = mark();
26049 return previous;
26050 }
26051 inPhase(phase, fn) {
26052 const previousPhase = this.phase(phase);
26053 try {
26054 return fn();
26055 }
26056 finally {
26057 this.phase(previousPhase);
26058 }
26059 }
26060 eventCount(counter, incrementBy = 1) {
26061 this.counters[counter] += incrementBy;
26062 }
26063 /**
26064 * Return the current performance metrics as a serializable object.
26065 */
26066 finalize() {
26067 // Track the last segment of time spent in `this.currentPhase` in the time array.
26068 this.phase(PerfPhase.Unaccounted);
26069 const results = {
26070 events: {},
26071 phases: {},
26072 memory: {},
26073 };
26074 for (let i = 0; i < this.phaseTime.length; i++) {
26075 if (this.phaseTime[i] > 0) {
26076 results.phases[PerfPhase[i]] = this.phaseTime[i];
26077 }
26078 }
26079 for (let i = 0; i < this.phaseTime.length; i++) {
26080 if (this.counters[i] > 0) {
26081 results.events[PerfEvent[i]] = this.counters[i];
26082 }
26083 }
26084 for (let i = 0; i < this.bytes.length; i++) {
26085 if (this.bytes[i] > 0) {
26086 results.memory[PerfCheckpoint[i]] = this.bytes[i];
26087 }
26088 }
26089 return results;
26090 }
26091 }
26092 /**
26093 * A `PerfRecorder` that delegates to a target `PerfRecorder` which can be updated later.
26094 *
26095 * `DelegatingPerfRecorder` is useful when a compiler class that needs a `PerfRecorder` can outlive
26096 * the current compilation. This is true for most compiler classes as resource-only changes reuse
26097 * the same `NgCompiler` for a new compilation.
26098 */
26099 class DelegatingPerfRecorder {
26100 constructor(target) {
26101 this.target = target;
26102 }
26103 eventCount(counter, incrementBy) {
26104 this.target.eventCount(counter, incrementBy);
26105 }
26106 phase(phase) {
26107 return this.target.phase(phase);
26108 }
26109 inPhase(phase, fn) {
26110 // Note: this doesn't delegate to `this.target.inPhase` but instead is implemented manually here
26111 // to avoid adding an additional frame of noise to the stack when debugging.
26112 const previousPhase = this.target.phase(phase);
26113 try {
26114 return fn();
26115 }
26116 finally {
26117 this.target.phase(previousPhase);
26118 }
26119 }
26120 memory(after) {
26121 this.target.memory(after);
26122 }
26123 reset() {
26124 this.target.reset();
26125 }
26126 }
26127
26128 /**
26129 * @license
26130 * Copyright Google LLC All Rights Reserved.
26131 *
26132 * Use of this source code is governed by an MIT-style license that can be
26133 * found in the LICENSE file at https://angular.io/license
26134 */
26135 /**
26136 * Specifies the compilation mode that is used for the compilation.
26137 */
26138 var CompilationMode;
26139 (function (CompilationMode) {
26140 /**
26141 * Generates fully AOT compiled code using Ivy instructions.
26142 */
26143 CompilationMode[CompilationMode["FULL"] = 0] = "FULL";
26144 /**
26145 * Generates code using a stable, but intermediate format suitable to be published to NPM.
26146 */
26147 CompilationMode[CompilationMode["PARTIAL"] = 1] = "PARTIAL";
26148 })(CompilationMode || (CompilationMode = {}));
26149 var HandlerPrecedence;
26150 (function (HandlerPrecedence) {
26151 /**
26152 * Handler with PRIMARY precedence cannot overlap - there can only be one on a given class.
26153 *
26154 * If more than one PRIMARY handler matches a class, an error is produced.
26155 */
26156 HandlerPrecedence[HandlerPrecedence["PRIMARY"] = 0] = "PRIMARY";
26157 /**
26158 * Handlers with SHARED precedence can match any class, possibly in addition to a single PRIMARY
26159 * handler.
26160 *
26161 * It is not an error for a class to have any number of SHARED handlers.
26162 */
26163 HandlerPrecedence[HandlerPrecedence["SHARED"] = 1] = "SHARED";
26164 /**
26165 * Handlers with WEAK precedence that match a class are ignored if any handlers with stronger
26166 * precedence match a class.
26167 */
26168 HandlerPrecedence[HandlerPrecedence["WEAK"] = 2] = "WEAK";
26169 })(HandlerPrecedence || (HandlerPrecedence = {}));
26170 /**
26171 * A set of options which can be passed to a `DecoratorHandler` by a consumer, to tailor the output
26172 * of compilation beyond the decorators themselves.
26173 */
26174 var HandlerFlags;
26175 (function (HandlerFlags) {
26176 /**
26177 * No flags set.
26178 */
26179 HandlerFlags[HandlerFlags["NONE"] = 0] = "NONE";
26180 /**
26181 * Indicates that this decorator is fully inherited from its parent at runtime. In addition to
26182 * normally inherited aspects such as inputs and queries, full inheritance applies to every aspect
26183 * of the component or directive, such as the template function itself.
26184 *
26185 * Its primary effect is to cause the `CopyDefinitionFeature` to be applied to the definition
26186 * being compiled. See that class for more information.
26187 */
26188 HandlerFlags[HandlerFlags["FULL_INHERITANCE"] = 1] = "FULL_INHERITANCE";
26189 })(HandlerFlags || (HandlerFlags = {}));
26190
26191 /**
26192 * @license
26193 * Copyright Google LLC All Rights Reserved.
26194 *
26195 * Use of this source code is governed by an MIT-style license that can be
26196 * found in the LICENSE file at https://angular.io/license
26197 */
26198 function aliasTransformFactory(exportStatements) {
26199 return (context) => {
26200 return (file) => {
26201 if (ts__default["default"].isBundle(file) || !exportStatements.has(file.fileName)) {
26202 return file;
26203 }
26204 const statements = [...file.statements];
26205 exportStatements.get(file.fileName).forEach(([moduleName, symbolName], aliasName) => {
26206 const stmt = ts__default["default"].createExportDeclaration(
26207 /* decorators */ undefined,
26208 /* modifiers */ undefined,
26209 /* exportClause */ ts__default["default"].createNamedExports([ts__default["default"].createExportSpecifier(
26210 /* propertyName */ symbolName,
26211 /* name */ aliasName)]),
26212 /* moduleSpecifier */ ts__default["default"].createStringLiteral(moduleName));
26213 statements.push(stmt);
26214 });
26215 return ts__default["default"].updateSourceFileNode(file, statements);
26216 };
26217 };
26218 }
26219
26220 /**
26221 * @license
26222 * Copyright Google LLC All Rights Reserved.
26223 *
26224 * Use of this source code is governed by an MIT-style license that can be
26225 * found in the LICENSE file at https://angular.io/license
26226 */
26227 var TraitState;
26228 (function (TraitState) {
26229 /**
26230 * Pending traits are freshly created and have never been analyzed.
26231 */
26232 TraitState[TraitState["Pending"] = 0] = "Pending";
26233 /**
26234 * Analyzed traits have successfully been analyzed, but are pending resolution.
26235 */
26236 TraitState[TraitState["Analyzed"] = 1] = "Analyzed";
26237 /**
26238 * Resolved traits have successfully been analyzed and resolved and are ready for compilation.
26239 */
26240 TraitState[TraitState["Resolved"] = 2] = "Resolved";
26241 /**
26242 * Skipped traits are no longer considered for compilation.
26243 */
26244 TraitState[TraitState["Skipped"] = 3] = "Skipped";
26245 })(TraitState || (TraitState = {}));
26246 /**
26247 * The value side of `Trait` exposes a helper to create a `Trait` in a pending state (by delegating
26248 * to `TraitImpl`).
26249 */
26250 const Trait = {
26251 pending: (handler, detected) => TraitImpl.pending(handler, detected),
26252 };
26253 /**
26254 * An implementation of the `Trait` type which transitions safely between the various
26255 * `TraitState`s.
26256 */
26257 class TraitImpl {
26258 constructor(handler, detected) {
26259 this.state = TraitState.Pending;
26260 this.analysis = null;
26261 this.symbol = null;
26262 this.resolution = null;
26263 this.analysisDiagnostics = null;
26264 this.resolveDiagnostics = null;
26265 this.handler = handler;
26266 this.detected = detected;
26267 }
26268 toAnalyzed(analysis, diagnostics, symbol) {
26269 // Only pending traits can be analyzed.
26270 this.assertTransitionLegal(TraitState.Pending, TraitState.Analyzed);
26271 this.analysis = analysis;
26272 this.analysisDiagnostics = diagnostics;
26273 this.symbol = symbol;
26274 this.state = TraitState.Analyzed;
26275 return this;
26276 }
26277 toResolved(resolution, diagnostics) {
26278 // Only analyzed traits can be resolved.
26279 this.assertTransitionLegal(TraitState.Analyzed, TraitState.Resolved);
26280 if (this.analysis === null) {
26281 throw new Error(`Cannot transition an Analyzed trait with a null analysis to Resolved`);
26282 }
26283 this.resolution = resolution;
26284 this.state = TraitState.Resolved;
26285 this.resolveDiagnostics = diagnostics;
26286 return this;
26287 }
26288 toSkipped() {
26289 // Only pending traits can be skipped.
26290 this.assertTransitionLegal(TraitState.Pending, TraitState.Skipped);
26291 this.state = TraitState.Skipped;
26292 return this;
26293 }
26294 /**
26295 * Verifies that the trait is currently in one of the `allowedState`s.
26296 *
26297 * If correctly used, the `Trait` type and transition methods prevent illegal transitions from
26298 * occurring. However, if a reference to the `TraitImpl` instance typed with the previous
26299 * interface is retained after calling one of its transition methods, it will allow for illegal
26300 * transitions to take place. Hence, this assertion provides a little extra runtime protection.
26301 */
26302 assertTransitionLegal(allowedState, transitionTo) {
26303 if (!(this.state === allowedState)) {
26304 throw new Error(`Assertion failure: cannot transition from ${TraitState[this.state]} to ${TraitState[transitionTo]}.`);
26305 }
26306 }
26307 /**
26308 * Construct a new `TraitImpl` in the pending state.
26309 */
26310 static pending(handler, detected) {
26311 return new TraitImpl(handler, detected);
26312 }
26313 }
26314
26315 /**
26316 * @license
26317 * Copyright Google LLC All Rights Reserved.
26318 *
26319 * Use of this source code is governed by an MIT-style license that can be
26320 * found in the LICENSE file at https://angular.io/license
26321 */
26322 /**
26323 * The heart of Angular compilation.
26324 *
26325 * The `TraitCompiler` is responsible for processing all classes in the program. Any time a
26326 * `DecoratorHandler` matches a class, a "trait" is created to represent that Angular aspect of the
26327 * class (such as the class having a component definition).
26328 *
26329 * The `TraitCompiler` transitions each trait through the various phases of compilation, culminating
26330 * in the production of `CompileResult`s instructing the compiler to apply various mutations to the
26331 * class (like adding fields or type declarations).
26332 */
26333 class TraitCompiler {
26334 constructor(handlers, reflector, perf, incrementalBuild, compileNonExportedClasses, compilationMode, dtsTransforms, semanticDepGraphUpdater) {
26335 this.handlers = handlers;
26336 this.reflector = reflector;
26337 this.perf = perf;
26338 this.incrementalBuild = incrementalBuild;
26339 this.compileNonExportedClasses = compileNonExportedClasses;
26340 this.compilationMode = compilationMode;
26341 this.dtsTransforms = dtsTransforms;
26342 this.semanticDepGraphUpdater = semanticDepGraphUpdater;
26343 /**
26344 * Maps class declarations to their `ClassRecord`, which tracks the Ivy traits being applied to
26345 * those classes.
26346 */
26347 this.classes = new Map();
26348 /**
26349 * Maps source files to any class declaration(s) within them which have been discovered to contain
26350 * Ivy traits.
26351 */
26352 this.fileToClasses = new Map();
26353 /**
26354 * Tracks which source files have been analyzed but did not contain any traits. This set allows
26355 * the compiler to skip analyzing these files in an incremental rebuild.
26356 */
26357 this.filesWithoutTraits = new Set();
26358 this.reexportMap = new Map();
26359 this.handlersByName = new Map();
26360 for (const handler of handlers) {
26361 this.handlersByName.set(handler.name, handler);
26362 }
26363 }
26364 analyzeSync(sf) {
26365 this.analyze(sf, false);
26366 }
26367 analyzeAsync(sf) {
26368 return this.analyze(sf, true);
26369 }
26370 analyze(sf, preanalyze) {
26371 // We shouldn't analyze declaration files.
26372 if (sf.isDeclarationFile) {
26373 return undefined;
26374 }
26375 // analyze() really wants to return `Promise<void>|void`, but TypeScript cannot narrow a return
26376 // type of 'void', so `undefined` is used instead.
26377 const promises = [];
26378 const priorWork = this.incrementalBuild.priorAnalysisFor(sf);
26379 if (priorWork !== null) {
26380 this.perf.eventCount(PerfEvent.SourceFileReuseAnalysis);
26381 if (priorWork.length > 0) {
26382 for (const priorRecord of priorWork) {
26383 this.adopt(priorRecord);
26384 }
26385 this.perf.eventCount(PerfEvent.TraitReuseAnalysis, priorWork.length);
26386 }
26387 else {
26388 this.filesWithoutTraits.add(sf);
26389 }
26390 // Skip the rest of analysis, as this file's prior traits are being reused.
26391 return;
26392 }
26393 const visit = (node) => {
26394 if (this.reflector.isClass(node)) {
26395 this.analyzeClass(node, preanalyze ? promises : null);
26396 }
26397 ts__default["default"].forEachChild(node, visit);
26398 };
26399 visit(sf);
26400 if (preanalyze && promises.length > 0) {
26401 return Promise.all(promises).then(() => undefined);
26402 }
26403 else {
26404 return undefined;
26405 }
26406 }
26407 recordFor(clazz) {
26408 if (this.classes.has(clazz)) {
26409 return this.classes.get(clazz);
26410 }
26411 else {
26412 return null;
26413 }
26414 }
26415 recordsFor(sf) {
26416 if (!this.fileToClasses.has(sf)) {
26417 return null;
26418 }
26419 const records = [];
26420 for (const clazz of this.fileToClasses.get(sf)) {
26421 records.push(this.classes.get(clazz));
26422 }
26423 return records;
26424 }
26425 getAnalyzedRecords() {
26426 const result = new Map();
26427 for (const [sf, classes] of this.fileToClasses) {
26428 const records = [];
26429 for (const clazz of classes) {
26430 records.push(this.classes.get(clazz));
26431 }
26432 result.set(sf, records);
26433 }
26434 for (const sf of this.filesWithoutTraits) {
26435 result.set(sf, []);
26436 }
26437 return result;
26438 }
26439 /**
26440 * Import a `ClassRecord` from a previous compilation.
26441 *
26442 * Traits from the `ClassRecord` have accurate metadata, but the `handler` is from the old program
26443 * and needs to be updated (matching is done by name). A new pending trait is created and then
26444 * transitioned to analyzed using the previous analysis. If the trait is in the errored state,
26445 * instead the errors are copied over.
26446 */
26447 adopt(priorRecord) {
26448 const record = {
26449 hasPrimaryHandler: priorRecord.hasPrimaryHandler,
26450 hasWeakHandlers: priorRecord.hasWeakHandlers,
26451 metaDiagnostics: priorRecord.metaDiagnostics,
26452 node: priorRecord.node,
26453 traits: [],
26454 };
26455 for (const priorTrait of priorRecord.traits) {
26456 const handler = this.handlersByName.get(priorTrait.handler.name);
26457 let trait = Trait.pending(handler, priorTrait.detected);
26458 if (priorTrait.state === TraitState.Analyzed || priorTrait.state === TraitState.Resolved) {
26459 const symbol = this.makeSymbolForTrait(handler, record.node, priorTrait.analysis);
26460 trait = trait.toAnalyzed(priorTrait.analysis, priorTrait.analysisDiagnostics, symbol);
26461 if (trait.analysis !== null && trait.handler.register !== undefined) {
26462 trait.handler.register(record.node, trait.analysis);
26463 }
26464 }
26465 else if (priorTrait.state === TraitState.Skipped) {
26466 trait = trait.toSkipped();
26467 }
26468 record.traits.push(trait);
26469 }
26470 this.classes.set(record.node, record);
26471 const sf = record.node.getSourceFile();
26472 if (!this.fileToClasses.has(sf)) {
26473 this.fileToClasses.set(sf, new Set());
26474 }
26475 this.fileToClasses.get(sf).add(record.node);
26476 }
26477 scanClassForTraits(clazz) {
26478 if (!this.compileNonExportedClasses && !this.reflector.isStaticallyExported(clazz)) {
26479 return null;
26480 }
26481 const decorators = this.reflector.getDecoratorsOfDeclaration(clazz);
26482 return this.detectTraits(clazz, decorators);
26483 }
26484 detectTraits(clazz, decorators) {
26485 let record = this.recordFor(clazz);
26486 let foundTraits = [];
26487 for (const handler of this.handlers) {
26488 const result = handler.detect(clazz, decorators);
26489 if (result === undefined) {
26490 continue;
26491 }
26492 const isPrimaryHandler = handler.precedence === HandlerPrecedence.PRIMARY;
26493 const isWeakHandler = handler.precedence === HandlerPrecedence.WEAK;
26494 const trait = Trait.pending(handler, result);
26495 foundTraits.push(trait);
26496 if (record === null) {
26497 // This is the first handler to match this class. This path is a fast path through which
26498 // most classes will flow.
26499 record = {
26500 node: clazz,
26501 traits: [trait],
26502 metaDiagnostics: null,
26503 hasPrimaryHandler: isPrimaryHandler,
26504 hasWeakHandlers: isWeakHandler,
26505 };
26506 this.classes.set(clazz, record);
26507 const sf = clazz.getSourceFile();
26508 if (!this.fileToClasses.has(sf)) {
26509 this.fileToClasses.set(sf, new Set());
26510 }
26511 this.fileToClasses.get(sf).add(clazz);
26512 }
26513 else {
26514 // This is at least the second handler to match this class. This is a slower path that some
26515 // classes will go through, which validates that the set of decorators applied to the class
26516 // is valid.
26517 // Validate according to rules as follows:
26518 //
26519 // * WEAK handlers are removed if a non-WEAK handler matches.
26520 // * Only one PRIMARY handler can match at a time. Any other PRIMARY handler matching a
26521 // class with an existing PRIMARY handler is an error.
26522 if (!isWeakHandler && record.hasWeakHandlers) {
26523 // The current handler is not a WEAK handler, but the class has other WEAK handlers.
26524 // Remove them.
26525 record.traits =
26526 record.traits.filter(field => field.handler.precedence !== HandlerPrecedence.WEAK);
26527 record.hasWeakHandlers = false;
26528 }
26529 else if (isWeakHandler && !record.hasWeakHandlers) {
26530 // The current handler is a WEAK handler, but the class has non-WEAK handlers already.
26531 // Drop the current one.
26532 continue;
26533 }
26534 if (isPrimaryHandler && record.hasPrimaryHandler) {
26535 // The class already has a PRIMARY handler, and another one just matched.
26536 record.metaDiagnostics = [{
26537 category: ts__default["default"].DiagnosticCategory.Error,
26538 code: Number('-99' + ErrorCode.DECORATOR_COLLISION),
26539 file: getSourceFile(clazz),
26540 start: clazz.getStart(undefined, false),
26541 length: clazz.getWidth(),
26542 messageText: 'Two incompatible decorators on class',
26543 }];
26544 record.traits = foundTraits = [];
26545 break;
26546 }
26547 // Otherwise, it's safe to accept the multiple decorators here. Update some of the metadata
26548 // regarding this class.
26549 record.traits.push(trait);
26550 record.hasPrimaryHandler = record.hasPrimaryHandler || isPrimaryHandler;
26551 }
26552 }
26553 return foundTraits.length > 0 ? foundTraits : null;
26554 }
26555 makeSymbolForTrait(handler, decl, analysis) {
26556 if (analysis === null) {
26557 return null;
26558 }
26559 const symbol = handler.symbol(decl, analysis);
26560 if (symbol !== null && this.semanticDepGraphUpdater !== null) {
26561 const isPrimary = handler.precedence === HandlerPrecedence.PRIMARY;
26562 if (!isPrimary) {
26563 throw new Error(`AssertionError: ${handler.name} returned a symbol but is not a primary handler.`);
26564 }
26565 this.semanticDepGraphUpdater.registerSymbol(symbol);
26566 }
26567 return symbol;
26568 }
26569 analyzeClass(clazz, preanalyzeQueue) {
26570 const traits = this.scanClassForTraits(clazz);
26571 if (traits === null) {
26572 // There are no Ivy traits on the class, so it can safely be skipped.
26573 return;
26574 }
26575 for (const trait of traits) {
26576 const analyze = () => this.analyzeTrait(clazz, trait);
26577 let preanalysis = null;
26578 if (preanalyzeQueue !== null && trait.handler.preanalyze !== undefined) {
26579 // Attempt to run preanalysis. This could fail with a `FatalDiagnosticError`; catch it if it
26580 // does.
26581 try {
26582 preanalysis = trait.handler.preanalyze(clazz, trait.detected.metadata) || null;
26583 }
26584 catch (err) {
26585 if (err instanceof FatalDiagnosticError) {
26586 trait.toAnalyzed(null, [err.toDiagnostic()], null);
26587 return;
26588 }
26589 else {
26590 throw err;
26591 }
26592 }
26593 }
26594 if (preanalysis !== null) {
26595 preanalyzeQueue.push(preanalysis.then(analyze));
26596 }
26597 else {
26598 analyze();
26599 }
26600 }
26601 }
26602 analyzeTrait(clazz, trait, flags) {
26603 if (trait.state !== TraitState.Pending) {
26604 throw new Error(`Attempt to analyze trait of ${clazz.name.text} in state ${TraitState[trait.state]} (expected DETECTED)`);
26605 }
26606 this.perf.eventCount(PerfEvent.TraitAnalyze);
26607 // Attempt analysis. This could fail with a `FatalDiagnosticError`; catch it if it does.
26608 let result;
26609 try {
26610 result = trait.handler.analyze(clazz, trait.detected.metadata, flags);
26611 }
26612 catch (err) {
26613 if (err instanceof FatalDiagnosticError) {
26614 trait.toAnalyzed(null, [err.toDiagnostic()], null);
26615 return;
26616 }
26617 else {
26618 throw err;
26619 }
26620 }
26621 const symbol = this.makeSymbolForTrait(trait.handler, clazz, result.analysis ?? null);
26622 if (result.analysis !== undefined && trait.handler.register !== undefined) {
26623 trait.handler.register(clazz, result.analysis);
26624 }
26625 trait = trait.toAnalyzed(result.analysis ?? null, result.diagnostics ?? null, symbol);
26626 }
26627 resolve() {
26628 const classes = Array.from(this.classes.keys());
26629 for (const clazz of classes) {
26630 const record = this.classes.get(clazz);
26631 for (let trait of record.traits) {
26632 const handler = trait.handler;
26633 switch (trait.state) {
26634 case TraitState.Skipped:
26635 continue;
26636 case TraitState.Pending:
26637 throw new Error(`Resolving a trait that hasn't been analyzed: ${clazz.name.text} / ${Object.getPrototypeOf(trait.handler).constructor.name}`);
26638 case TraitState.Resolved:
26639 throw new Error(`Resolving an already resolved trait`);
26640 }
26641 if (trait.analysis === null) {
26642 // No analysis results, cannot further process this trait.
26643 continue;
26644 }
26645 if (handler.resolve === undefined) {
26646 // No resolution of this trait needed - it's considered successful by default.
26647 trait = trait.toResolved(null, null);
26648 continue;
26649 }
26650 let result;
26651 try {
26652 result = handler.resolve(clazz, trait.analysis, trait.symbol);
26653 }
26654 catch (err) {
26655 if (err instanceof FatalDiagnosticError) {
26656 trait = trait.toResolved(null, [err.toDiagnostic()]);
26657 continue;
26658 }
26659 else {
26660 throw err;
26661 }
26662 }
26663 trait = trait.toResolved(result.data ?? null, result.diagnostics ?? null);
26664 if (result.reexports !== undefined) {
26665 const fileName = clazz.getSourceFile().fileName;
26666 if (!this.reexportMap.has(fileName)) {
26667 this.reexportMap.set(fileName, new Map());
26668 }
26669 const fileReexports = this.reexportMap.get(fileName);
26670 for (const reexport of result.reexports) {
26671 fileReexports.set(reexport.asAlias, [reexport.fromModule, reexport.symbolName]);
26672 }
26673 }
26674 }
26675 }
26676 }
26677 /**
26678 * Generate type-checking code into the `TypeCheckContext` for any components within the given
26679 * `ts.SourceFile`.
26680 */
26681 typeCheck(sf, ctx) {
26682 if (!this.fileToClasses.has(sf)) {
26683 return;
26684 }
26685 for (const clazz of this.fileToClasses.get(sf)) {
26686 const record = this.classes.get(clazz);
26687 for (const trait of record.traits) {
26688 if (trait.state !== TraitState.Resolved) {
26689 continue;
26690 }
26691 else if (trait.handler.typeCheck === undefined) {
26692 continue;
26693 }
26694 if (trait.resolution !== null) {
26695 trait.handler.typeCheck(ctx, clazz, trait.analysis, trait.resolution);
26696 }
26697 }
26698 }
26699 }
26700 extendedTemplateCheck(sf, extendedTemplateChecker) {
26701 const classes = this.fileToClasses.get(sf);
26702 if (classes === undefined) {
26703 return [];
26704 }
26705 const diagnostics = [];
26706 for (const clazz of classes) {
26707 if (!isNamedClassDeclaration(clazz)) {
26708 continue;
26709 }
26710 const record = this.classes.get(clazz);
26711 for (const trait of record.traits) {
26712 if (trait.handler.extendedTemplateCheck === undefined) {
26713 continue;
26714 }
26715 diagnostics.push(...trait.handler.extendedTemplateCheck(clazz, extendedTemplateChecker));
26716 }
26717 }
26718 return diagnostics;
26719 }
26720 index(ctx) {
26721 for (const clazz of this.classes.keys()) {
26722 const record = this.classes.get(clazz);
26723 for (const trait of record.traits) {
26724 if (trait.state !== TraitState.Resolved) {
26725 // Skip traits that haven't been resolved successfully.
26726 continue;
26727 }
26728 else if (trait.handler.index === undefined) {
26729 // Skip traits that don't affect indexing.
26730 continue;
26731 }
26732 if (trait.resolution !== null) {
26733 trait.handler.index(ctx, clazz, trait.analysis, trait.resolution);
26734 }
26735 }
26736 }
26737 }
26738 xi18n(bundle) {
26739 for (const clazz of this.classes.keys()) {
26740 const record = this.classes.get(clazz);
26741 for (const trait of record.traits) {
26742 if (trait.state !== TraitState.Analyzed && trait.state !== TraitState.Resolved) {
26743 // Skip traits that haven't been analyzed successfully.
26744 continue;
26745 }
26746 else if (trait.handler.xi18n === undefined) {
26747 // Skip traits that don't support xi18n.
26748 continue;
26749 }
26750 if (trait.analysis !== null) {
26751 trait.handler.xi18n(bundle, clazz, trait.analysis);
26752 }
26753 }
26754 }
26755 }
26756 updateResources(clazz) {
26757 if (!this.reflector.isClass(clazz) || !this.classes.has(clazz)) {
26758 return;
26759 }
26760 const record = this.classes.get(clazz);
26761 for (const trait of record.traits) {
26762 if (trait.state !== TraitState.Resolved || trait.handler.updateResources === undefined) {
26763 continue;
26764 }
26765 trait.handler.updateResources(clazz, trait.analysis, trait.resolution);
26766 }
26767 }
26768 compile(clazz, constantPool) {
26769 const original = ts__default["default"].getOriginalNode(clazz);
26770 if (!this.reflector.isClass(clazz) || !this.reflector.isClass(original) ||
26771 !this.classes.has(original)) {
26772 return null;
26773 }
26774 const record = this.classes.get(original);
26775 let res = [];
26776 for (const trait of record.traits) {
26777 if (trait.state !== TraitState.Resolved || trait.analysisDiagnostics !== null ||
26778 trait.resolveDiagnostics !== null) {
26779 // Cannot compile a trait that is not resolved, or had any errors in its declaration.
26780 continue;
26781 }
26782 // `trait.resolution` is non-null asserted here because TypeScript does not recognize that
26783 // `Readonly<unknown>` is nullable (as `unknown` itself is nullable) due to the way that
26784 // `Readonly` works.
26785 let compileRes;
26786 if (this.compilationMode === CompilationMode.PARTIAL &&
26787 trait.handler.compilePartial !== undefined) {
26788 compileRes = trait.handler.compilePartial(clazz, trait.analysis, trait.resolution);
26789 }
26790 else {
26791 compileRes =
26792 trait.handler.compileFull(clazz, trait.analysis, trait.resolution, constantPool);
26793 }
26794 const compileMatchRes = compileRes;
26795 if (Array.isArray(compileMatchRes)) {
26796 for (const result of compileMatchRes) {
26797 if (!res.some(r => r.name === result.name)) {
26798 res.push(result);
26799 }
26800 }
26801 }
26802 else if (!res.some(result => result.name === compileMatchRes.name)) {
26803 res.push(compileMatchRes);
26804 }
26805 }
26806 // Look up the .d.ts transformer for the input file and record that at least one field was
26807 // generated, which will allow the .d.ts to be transformed later.
26808 this.dtsTransforms.getIvyDeclarationTransform(original.getSourceFile())
26809 .addFields(original, res);
26810 // Return the instruction to the transformer so the fields will be added.
26811 return res.length > 0 ? res : null;
26812 }
26813 decoratorsFor(node) {
26814 const original = ts__default["default"].getOriginalNode(node);
26815 if (!this.reflector.isClass(original) || !this.classes.has(original)) {
26816 return [];
26817 }
26818 const record = this.classes.get(original);
26819 const decorators = [];
26820 for (const trait of record.traits) {
26821 if (trait.state !== TraitState.Resolved) {
26822 continue;
26823 }
26824 if (trait.detected.trigger !== null && ts__default["default"].isDecorator(trait.detected.trigger)) {
26825 decorators.push(trait.detected.trigger);
26826 }
26827 }
26828 return decorators;
26829 }
26830 get diagnostics() {
26831 const diagnostics = [];
26832 for (const clazz of this.classes.keys()) {
26833 const record = this.classes.get(clazz);
26834 if (record.metaDiagnostics !== null) {
26835 diagnostics.push(...record.metaDiagnostics);
26836 }
26837 for (const trait of record.traits) {
26838 if ((trait.state === TraitState.Analyzed || trait.state === TraitState.Resolved) &&
26839 trait.analysisDiagnostics !== null) {
26840 diagnostics.push(...trait.analysisDiagnostics);
26841 }
26842 if (trait.state === TraitState.Resolved && trait.resolveDiagnostics !== null) {
26843 diagnostics.push(...trait.resolveDiagnostics);
26844 }
26845 }
26846 }
26847 return diagnostics;
26848 }
26849 get exportStatements() {
26850 return this.reexportMap;
26851 }
26852 }
26853
26854 /**
26855 * @license
26856 * Copyright Google LLC All Rights Reserved.
26857 *
26858 * Use of this source code is governed by an MIT-style license that can be
26859 * found in the LICENSE file at https://angular.io/license
26860 */
26861 /**
26862 * The current context of a translator visitor as it traverses the AST tree.
26863 *
26864 * It tracks whether we are in the process of outputting a statement or an expression.
26865 */
26866 class Context$1 {
26867 constructor(isStatement) {
26868 this.isStatement = isStatement;
26869 }
26870 get withExpressionMode() {
26871 return this.isStatement ? new Context$1(false) : this;
26872 }
26873 get withStatementMode() {
26874 return !this.isStatement ? new Context$1(true) : this;
26875 }
26876 }
26877
26878 /**
26879 * @license
26880 * Copyright Google LLC All Rights Reserved.
26881 *
26882 * Use of this source code is governed by an MIT-style license that can be
26883 * found in the LICENSE file at https://angular.io/license
26884 */
26885 class ImportManager {
26886 constructor(rewriter = new NoopImportRewriter(), prefix = 'i') {
26887 this.rewriter = rewriter;
26888 this.prefix = prefix;
26889 this.specifierToIdentifier = new Map();
26890 this.nextIndex = 0;
26891 }
26892 generateNamespaceImport(moduleName) {
26893 if (!this.specifierToIdentifier.has(moduleName)) {
26894 this.specifierToIdentifier.set(moduleName, ts__default["default"].createIdentifier(`${this.prefix}${this.nextIndex++}`));
26895 }
26896 return this.specifierToIdentifier.get(moduleName);
26897 }
26898 generateNamedImport(moduleName, originalSymbol) {
26899 // First, rewrite the symbol name.
26900 const symbol = this.rewriter.rewriteSymbol(originalSymbol, moduleName);
26901 // Ask the rewriter if this symbol should be imported at all. If not, it can be referenced
26902 // directly (moduleImport: null).
26903 if (!this.rewriter.shouldImportSymbol(symbol, moduleName)) {
26904 // The symbol should be referenced directly.
26905 return { moduleImport: null, symbol };
26906 }
26907 // If not, this symbol will be imported using a generated namespace import.
26908 const moduleImport = this.generateNamespaceImport(moduleName);
26909 return { moduleImport, symbol };
26910 }
26911 getAllImports(contextPath) {
26912 const imports = [];
26913 for (const [originalSpecifier, qualifier] of this.specifierToIdentifier) {
26914 const specifier = this.rewriter.rewriteSpecifier(originalSpecifier, contextPath);
26915 imports.push({
26916 specifier,
26917 qualifier,
26918 });
26919 }
26920 return imports;
26921 }
26922 }
26923
26924 /**
26925 * @license
26926 * Copyright Google LLC All Rights Reserved.
26927 *
26928 * Use of this source code is governed by an MIT-style license that can be
26929 * found in the LICENSE file at https://angular.io/license
26930 */
26931 const UNARY_OPERATORS$1 = new Map([
26932 [UnaryOperator.Minus, '-'],
26933 [UnaryOperator.Plus, '+'],
26934 ]);
26935 const BINARY_OPERATORS$1 = new Map([
26936 [BinaryOperator.And, '&&'],
26937 [BinaryOperator.Bigger, '>'],
26938 [BinaryOperator.BiggerEquals, '>='],
26939 [BinaryOperator.BitwiseAnd, '&'],
26940 [BinaryOperator.Divide, '/'],
26941 [BinaryOperator.Equals, '=='],
26942 [BinaryOperator.Identical, '==='],
26943 [BinaryOperator.Lower, '<'],
26944 [BinaryOperator.LowerEquals, '<='],
26945 [BinaryOperator.Minus, '-'],
26946 [BinaryOperator.Modulo, '%'],
26947 [BinaryOperator.Multiply, '*'],
26948 [BinaryOperator.NotEquals, '!='],
26949 [BinaryOperator.NotIdentical, '!=='],
26950 [BinaryOperator.Or, '||'],
26951 [BinaryOperator.Plus, '+'],
26952 [BinaryOperator.NullishCoalesce, '??'],
26953 ]);
26954 class ExpressionTranslatorVisitor {
26955 constructor(factory, imports, options) {
26956 this.factory = factory;
26957 this.imports = imports;
26958 this.downlevelTaggedTemplates = options.downlevelTaggedTemplates === true;
26959 this.downlevelVariableDeclarations = options.downlevelVariableDeclarations === true;
26960 this.recordWrappedNode = options.recordWrappedNode || (() => { });
26961 }
26962 visitDeclareVarStmt(stmt, context) {
26963 const varType = this.downlevelVariableDeclarations ?
26964 'var' :
26965 stmt.hasModifier(StmtModifier.Final) ? 'const' : 'let';
26966 return this.attachComments(this.factory.createVariableDeclaration(stmt.name, stmt.value?.visitExpression(this, context.withExpressionMode), varType), stmt.leadingComments);
26967 }
26968 visitDeclareFunctionStmt(stmt, context) {
26969 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);
26970 }
26971 visitExpressionStmt(stmt, context) {
26972 return this.attachComments(this.factory.createExpressionStatement(stmt.expr.visitExpression(this, context.withStatementMode)), stmt.leadingComments);
26973 }
26974 visitReturnStmt(stmt, context) {
26975 return this.attachComments(this.factory.createReturnStatement(stmt.value.visitExpression(this, context.withExpressionMode)), stmt.leadingComments);
26976 }
26977 visitDeclareClassStmt(_stmt, _context) {
26978 throw new Error('Method not implemented.');
26979 }
26980 visitIfStmt(stmt, context) {
26981 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)) :
26982 null), stmt.leadingComments);
26983 }
26984 visitTryCatchStmt(_stmt, _context) {
26985 throw new Error('Method not implemented.');
26986 }
26987 visitThrowStmt(stmt, context) {
26988 return this.attachComments(this.factory.createThrowStatement(stmt.error.visitExpression(this, context.withExpressionMode)), stmt.leadingComments);
26989 }
26990 visitReadVarExpr(ast, _context) {
26991 const identifier = this.factory.createIdentifier(ast.name);
26992 this.setSourceMapRange(identifier, ast.sourceSpan);
26993 return identifier;
26994 }
26995 visitWriteVarExpr(expr, context) {
26996 const assignment = this.factory.createAssignment(this.setSourceMapRange(this.factory.createIdentifier(expr.name), expr.sourceSpan), expr.value.visitExpression(this, context));
26997 return context.isStatement ? assignment :
26998 this.factory.createParenthesizedExpression(assignment);
26999 }
27000 visitWriteKeyExpr(expr, context) {
27001 const exprContext = context.withExpressionMode;
27002 const target = this.factory.createElementAccess(expr.receiver.visitExpression(this, exprContext), expr.index.visitExpression(this, exprContext));
27003 const assignment = this.factory.createAssignment(target, expr.value.visitExpression(this, exprContext));
27004 return context.isStatement ? assignment :
27005 this.factory.createParenthesizedExpression(assignment);
27006 }
27007 visitWritePropExpr(expr, context) {
27008 const target = this.factory.createPropertyAccess(expr.receiver.visitExpression(this, context), expr.name);
27009 return this.factory.createAssignment(target, expr.value.visitExpression(this, context));
27010 }
27011 visitInvokeFunctionExpr(ast, context) {
27012 return this.setSourceMapRange(this.factory.createCallExpression(ast.fn.visitExpression(this, context), ast.args.map(arg => arg.visitExpression(this, context)), ast.pure), ast.sourceSpan);
27013 }
27014 visitTaggedTemplateExpr(ast, context) {
27015 return this.setSourceMapRange(this.createTaggedTemplateExpression(ast.tag.visitExpression(this, context), {
27016 elements: ast.template.elements.map(e => createTemplateElement({
27017 cooked: e.text,
27018 raw: e.rawText,
27019 range: e.sourceSpan ?? ast.sourceSpan,
27020 })),
27021 expressions: ast.template.expressions.map(e => e.visitExpression(this, context))
27022 }), ast.sourceSpan);
27023 }
27024 visitInstantiateExpr(ast, context) {
27025 return this.factory.createNewExpression(ast.classExpr.visitExpression(this, context), ast.args.map(arg => arg.visitExpression(this, context)));
27026 }
27027 visitLiteralExpr(ast, _context) {
27028 return this.setSourceMapRange(this.factory.createLiteral(ast.value), ast.sourceSpan);
27029 }
27030 visitLocalizedString(ast, context) {
27031 // A `$localize` message consists of `messageParts` and `expressions`, which get interleaved
27032 // together. The interleaved pieces look like:
27033 // `[messagePart0, expression0, messagePart1, expression1, messagePart2]`
27034 //
27035 // Note that there is always a message part at the start and end, and so therefore
27036 // `messageParts.length === expressions.length + 1`.
27037 //
27038 // Each message part may be prefixed with "metadata", which is wrapped in colons (:) delimiters.
27039 // The metadata is attached to the first and subsequent message parts by calls to
27040 // `serializeI18nHead()` and `serializeI18nTemplatePart()` respectively.
27041 //
27042 // The first message part (i.e. `ast.messageParts[0]`) is used to initialize `messageParts`
27043 // array.
27044 const elements = [createTemplateElement(ast.serializeI18nHead())];
27045 const expressions = [];
27046 for (let i = 0; i < ast.expressions.length; i++) {
27047 const placeholder = this.setSourceMapRange(ast.expressions[i].visitExpression(this, context), ast.getPlaceholderSourceSpan(i));
27048 expressions.push(placeholder);
27049 elements.push(createTemplateElement(ast.serializeI18nTemplatePart(i + 1)));
27050 }
27051 const localizeTag = this.factory.createIdentifier('$localize');
27052 return this.setSourceMapRange(this.createTaggedTemplateExpression(localizeTag, { elements, expressions }), ast.sourceSpan);
27053 }
27054 createTaggedTemplateExpression(tag, template) {
27055 return this.downlevelTaggedTemplates ? this.createES5TaggedTemplateFunctionCall(tag, template) :
27056 this.factory.createTaggedTemplate(tag, template);
27057 }
27058 /**
27059 * Translate the tagged template literal into a call that is compatible with ES5, using the
27060 * imported `__makeTemplateObject` helper for ES5 formatted output.
27061 */
27062 createES5TaggedTemplateFunctionCall(tagHandler, { elements, expressions }) {
27063 // Ensure that the `__makeTemplateObject()` helper has been imported.
27064 const { moduleImport, symbol } = this.imports.generateNamedImport('tslib', '__makeTemplateObject');
27065 const __makeTemplateObjectHelper = (moduleImport === null) ?
27066 this.factory.createIdentifier(symbol) :
27067 this.factory.createPropertyAccess(moduleImport, symbol);
27068 // Collect up the cooked and raw strings into two separate arrays.
27069 const cooked = [];
27070 const raw = [];
27071 for (const element of elements) {
27072 cooked.push(this.factory.setSourceMapRange(this.factory.createLiteral(element.cooked), element.range));
27073 raw.push(this.factory.setSourceMapRange(this.factory.createLiteral(element.raw), element.range));
27074 }
27075 // Generate the helper call in the form: `__makeTemplateObject([cooked], [raw]);`
27076 const templateHelperCall = this.factory.createCallExpression(__makeTemplateObjectHelper, [this.factory.createArrayLiteral(cooked), this.factory.createArrayLiteral(raw)],
27077 /* pure */ false);
27078 // Finally create the tagged handler call in the form:
27079 // `tag(__makeTemplateObject([cooked], [raw]), ...expressions);`
27080 return this.factory.createCallExpression(tagHandler, [templateHelperCall, ...expressions],
27081 /* pure */ false);
27082 }
27083 visitExternalExpr(ast, _context) {
27084 if (ast.value.name === null) {
27085 if (ast.value.moduleName === null) {
27086 throw new Error('Invalid import without name nor moduleName');
27087 }
27088 return this.imports.generateNamespaceImport(ast.value.moduleName);
27089 }
27090 // If a moduleName is specified, this is a normal import. If there's no module name, it's a
27091 // reference to a global/ambient symbol.
27092 if (ast.value.moduleName !== null) {
27093 // This is a normal import. Find the imported module.
27094 const { moduleImport, symbol } = this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
27095 if (moduleImport === null) {
27096 // The symbol was ambient after all.
27097 return this.factory.createIdentifier(symbol);
27098 }
27099 else {
27100 return this.factory.createPropertyAccess(moduleImport, symbol);
27101 }
27102 }
27103 else {
27104 // The symbol is ambient, so just reference it.
27105 return this.factory.createIdentifier(ast.value.name);
27106 }
27107 }
27108 visitConditionalExpr(ast, context) {
27109 let cond = ast.condition.visitExpression(this, context);
27110 // Ordinarily the ternary operator is right-associative. The following are equivalent:
27111 // `a ? b : c ? d : e` => `a ? b : (c ? d : e)`
27112 //
27113 // However, occasionally Angular needs to produce a left-associative conditional, such as in
27114 // the case of a null-safe navigation production: `{{a?.b ? c : d}}`. This template produces
27115 // a ternary of the form:
27116 // `a == null ? null : rest of expression`
27117 // If the rest of the expression is also a ternary though, this would produce the form:
27118 // `a == null ? null : a.b ? c : d`
27119 // which, if left as right-associative, would be incorrectly associated as:
27120 // `a == null ? null : (a.b ? c : d)`
27121 //
27122 // In such cases, the left-associativity needs to be enforced with parentheses:
27123 // `(a == null ? null : a.b) ? c : d`
27124 //
27125 // Such parentheses could always be included in the condition (guaranteeing correct behavior) in
27126 // all cases, but this has a code size cost. Instead, parentheses are added only when a
27127 // conditional expression is directly used as the condition of another.
27128 //
27129 // TODO(alxhub): investigate better logic for precendence of conditional operators
27130 if (ast.condition instanceof ConditionalExpr) {
27131 // The condition of this ternary needs to be wrapped in parentheses to maintain
27132 // left-associativity.
27133 cond = this.factory.createParenthesizedExpression(cond);
27134 }
27135 return this.factory.createConditional(cond, ast.trueCase.visitExpression(this, context), ast.falseCase.visitExpression(this, context));
27136 }
27137 visitNotExpr(ast, context) {
27138 return this.factory.createUnaryExpression('!', ast.condition.visitExpression(this, context));
27139 }
27140 visitAssertNotNullExpr(ast, context) {
27141 return ast.condition.visitExpression(this, context);
27142 }
27143 visitCastExpr(ast, context) {
27144 return ast.value.visitExpression(this, context);
27145 }
27146 visitFunctionExpr(ast, context) {
27147 return this.factory.createFunctionExpression(ast.name ?? null, ast.params.map(param => param.name), this.factory.createBlock(this.visitStatements(ast.statements, context)));
27148 }
27149 visitBinaryOperatorExpr(ast, context) {
27150 if (!BINARY_OPERATORS$1.has(ast.operator)) {
27151 throw new Error(`Unknown binary operator: ${BinaryOperator[ast.operator]}`);
27152 }
27153 return this.factory.createBinaryExpression(ast.lhs.visitExpression(this, context), BINARY_OPERATORS$1.get(ast.operator), ast.rhs.visitExpression(this, context));
27154 }
27155 visitReadPropExpr(ast, context) {
27156 return this.factory.createPropertyAccess(ast.receiver.visitExpression(this, context), ast.name);
27157 }
27158 visitReadKeyExpr(ast, context) {
27159 return this.factory.createElementAccess(ast.receiver.visitExpression(this, context), ast.index.visitExpression(this, context));
27160 }
27161 visitLiteralArrayExpr(ast, context) {
27162 return this.factory.createArrayLiteral(ast.entries.map(expr => this.setSourceMapRange(expr.visitExpression(this, context), ast.sourceSpan)));
27163 }
27164 visitLiteralMapExpr(ast, context) {
27165 const properties = ast.entries.map(entry => {
27166 return {
27167 propertyName: entry.key,
27168 quoted: entry.quoted,
27169 value: entry.value.visitExpression(this, context)
27170 };
27171 });
27172 return this.setSourceMapRange(this.factory.createObjectLiteral(properties), ast.sourceSpan);
27173 }
27174 visitCommaExpr(ast, context) {
27175 throw new Error('Method not implemented.');
27176 }
27177 visitWrappedNodeExpr(ast, _context) {
27178 this.recordWrappedNode(ast);
27179 return ast.node;
27180 }
27181 visitTypeofExpr(ast, context) {
27182 return this.factory.createTypeOfExpression(ast.expr.visitExpression(this, context));
27183 }
27184 visitUnaryOperatorExpr(ast, context) {
27185 if (!UNARY_OPERATORS$1.has(ast.operator)) {
27186 throw new Error(`Unknown unary operator: ${UnaryOperator[ast.operator]}`);
27187 }
27188 return this.factory.createUnaryExpression(UNARY_OPERATORS$1.get(ast.operator), ast.expr.visitExpression(this, context));
27189 }
27190 visitStatements(statements, context) {
27191 return statements.map(stmt => stmt.visitStatement(this, context))
27192 .filter(stmt => stmt !== undefined);
27193 }
27194 setSourceMapRange(ast, span) {
27195 return this.factory.setSourceMapRange(ast, createRange(span));
27196 }
27197 attachComments(statement, leadingComments) {
27198 if (leadingComments !== undefined) {
27199 this.factory.attachComments(statement, leadingComments);
27200 }
27201 return statement;
27202 }
27203 }
27204 /**
27205 * Convert a cooked-raw string object into one that can be used by the AST factories.
27206 */
27207 function createTemplateElement({ cooked, raw, range }) {
27208 return { cooked, raw, range: createRange(range) };
27209 }
27210 /**
27211 * Convert an OutputAST source-span into a range that can be used by the AST factories.
27212 */
27213 function createRange(span) {
27214 if (span === null) {
27215 return null;
27216 }
27217 const { start, end } = span;
27218 const { url, content } = start.file;
27219 if (!url) {
27220 return null;
27221 }
27222 return {
27223 url,
27224 content,
27225 start: { offset: start.offset, line: start.line, column: start.col },
27226 end: { offset: end.offset, line: end.line, column: end.col },
27227 };
27228 }
27229
27230 /**
27231 * @license
27232 * Copyright Google LLC All Rights Reserved.
27233 *
27234 * Use of this source code is governed by an MIT-style license that can be
27235 * found in the LICENSE file at https://angular.io/license
27236 */
27237 function translateType(type, imports) {
27238 return type.visitType(new TypeTranslatorVisitor(imports), new Context$1(false));
27239 }
27240 class TypeTranslatorVisitor {
27241 constructor(imports) {
27242 this.imports = imports;
27243 }
27244 visitBuiltinType(type, context) {
27245 switch (type.name) {
27246 case BuiltinTypeName.Bool:
27247 return ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.BooleanKeyword);
27248 case BuiltinTypeName.Dynamic:
27249 return ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword);
27250 case BuiltinTypeName.Int:
27251 case BuiltinTypeName.Number:
27252 return ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.NumberKeyword);
27253 case BuiltinTypeName.String:
27254 return ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.StringKeyword);
27255 case BuiltinTypeName.None:
27256 return ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.NeverKeyword);
27257 default:
27258 throw new Error(`Unsupported builtin type: ${BuiltinTypeName[type.name]}`);
27259 }
27260 }
27261 visitExpressionType(type, context) {
27262 const typeNode = this.translateExpression(type.value, context);
27263 if (type.typeParams === null) {
27264 return typeNode;
27265 }
27266 if (!ts__default["default"].isTypeReferenceNode(typeNode)) {
27267 throw new Error('An ExpressionType with type arguments must translate into a TypeReferenceNode');
27268 }
27269 else if (typeNode.typeArguments !== undefined) {
27270 throw new Error(`An ExpressionType with type arguments cannot have multiple levels of type arguments`);
27271 }
27272 const typeArgs = type.typeParams.map(param => this.translateType(param, context));
27273 return ts__default["default"].createTypeReferenceNode(typeNode.typeName, typeArgs);
27274 }
27275 visitArrayType(type, context) {
27276 return ts__default["default"].createArrayTypeNode(this.translateType(type.of, context));
27277 }
27278 visitMapType(type, context) {
27279 const parameter = ts__default["default"].createParameter(undefined, undefined, undefined, 'key', undefined, ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.StringKeyword));
27280 const typeArgs = type.valueType !== null ?
27281 this.translateType(type.valueType, context) :
27282 ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.UnknownKeyword);
27283 const indexSignature = ts__default["default"].createIndexSignature(undefined, undefined, [parameter], typeArgs);
27284 return ts__default["default"].createTypeLiteralNode([indexSignature]);
27285 }
27286 visitReadVarExpr(ast, context) {
27287 if (ast.name === null) {
27288 throw new Error(`ReadVarExpr with no variable name in type`);
27289 }
27290 return ts__default["default"].createTypeQueryNode(ts__default["default"].createIdentifier(ast.name));
27291 }
27292 visitWriteVarExpr(expr, context) {
27293 throw new Error('Method not implemented.');
27294 }
27295 visitWriteKeyExpr(expr, context) {
27296 throw new Error('Method not implemented.');
27297 }
27298 visitWritePropExpr(expr, context) {
27299 throw new Error('Method not implemented.');
27300 }
27301 visitInvokeFunctionExpr(ast, context) {
27302 throw new Error('Method not implemented.');
27303 }
27304 visitTaggedTemplateExpr(ast, context) {
27305 throw new Error('Method not implemented.');
27306 }
27307 visitInstantiateExpr(ast, context) {
27308 throw new Error('Method not implemented.');
27309 }
27310 visitLiteralExpr(ast, context) {
27311 if (ast.value === null) {
27312 return ts__default["default"].createLiteralTypeNode(ts__default["default"].createNull());
27313 }
27314 else if (ast.value === undefined) {
27315 return ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.UndefinedKeyword);
27316 }
27317 else if (typeof ast.value === 'boolean') {
27318 return ts__default["default"].createLiteralTypeNode(ts__default["default"].createLiteral(ast.value));
27319 }
27320 else if (typeof ast.value === 'number') {
27321 return ts__default["default"].createLiteralTypeNode(ts__default["default"].createLiteral(ast.value));
27322 }
27323 else {
27324 return ts__default["default"].createLiteralTypeNode(ts__default["default"].createLiteral(ast.value));
27325 }
27326 }
27327 visitLocalizedString(ast, context) {
27328 throw new Error('Method not implemented.');
27329 }
27330 visitExternalExpr(ast, context) {
27331 if (ast.value.moduleName === null || ast.value.name === null) {
27332 throw new Error(`Import unknown module or symbol`);
27333 }
27334 const { moduleImport, symbol } = this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
27335 const symbolIdentifier = ts__default["default"].createIdentifier(symbol);
27336 const typeName = moduleImport ? ts__default["default"].createQualifiedName(moduleImport, symbolIdentifier) : symbolIdentifier;
27337 const typeArguments = ast.typeParams !== null ?
27338 ast.typeParams.map(type => this.translateType(type, context)) :
27339 undefined;
27340 return ts__default["default"].createTypeReferenceNode(typeName, typeArguments);
27341 }
27342 visitConditionalExpr(ast, context) {
27343 throw new Error('Method not implemented.');
27344 }
27345 visitNotExpr(ast, context) {
27346 throw new Error('Method not implemented.');
27347 }
27348 visitAssertNotNullExpr(ast, context) {
27349 throw new Error('Method not implemented.');
27350 }
27351 visitCastExpr(ast, context) {
27352 throw new Error('Method not implemented.');
27353 }
27354 visitFunctionExpr(ast, context) {
27355 throw new Error('Method not implemented.');
27356 }
27357 visitUnaryOperatorExpr(ast, context) {
27358 throw new Error('Method not implemented.');
27359 }
27360 visitBinaryOperatorExpr(ast, context) {
27361 throw new Error('Method not implemented.');
27362 }
27363 visitReadPropExpr(ast, context) {
27364 throw new Error('Method not implemented.');
27365 }
27366 visitReadKeyExpr(ast, context) {
27367 throw new Error('Method not implemented.');
27368 }
27369 visitLiteralArrayExpr(ast, context) {
27370 const values = ast.entries.map(expr => this.translateExpression(expr, context));
27371 return ts__default["default"].createTupleTypeNode(values);
27372 }
27373 visitLiteralMapExpr(ast, context) {
27374 const entries = ast.entries.map(entry => {
27375 const { key, quoted } = entry;
27376 const type = this.translateExpression(entry.value, context);
27377 return ts__default["default"].createPropertySignature(
27378 /* modifiers */ undefined,
27379 /* name */ quoted ? ts__default["default"].createStringLiteral(key) : key,
27380 /* questionToken */ undefined,
27381 /* type */ type,
27382 /* initializer */ undefined);
27383 });
27384 return ts__default["default"].createTypeLiteralNode(entries);
27385 }
27386 visitCommaExpr(ast, context) {
27387 throw new Error('Method not implemented.');
27388 }
27389 visitWrappedNodeExpr(ast, context) {
27390 const node = ast.node;
27391 if (ts__default["default"].isEntityName(node)) {
27392 return ts__default["default"].createTypeReferenceNode(node, /* typeArguments */ undefined);
27393 }
27394 else if (ts__default["default"].isTypeNode(node)) {
27395 return node;
27396 }
27397 else if (ts__default["default"].isLiteralExpression(node)) {
27398 return ts__default["default"].createLiteralTypeNode(node);
27399 }
27400 else {
27401 throw new Error(`Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts__default["default"].SyntaxKind[node.kind]}`);
27402 }
27403 }
27404 visitTypeofExpr(ast, context) {
27405 const typeNode = this.translateExpression(ast.expr, context);
27406 if (!ts__default["default"].isTypeReferenceNode(typeNode)) {
27407 throw new Error(`The target of a typeof expression must be a type reference, but it was
27408 ${ts__default["default"].SyntaxKind[typeNode.kind]}`);
27409 }
27410 return ts__default["default"].createTypeQueryNode(typeNode.typeName);
27411 }
27412 translateType(type, context) {
27413 const typeNode = type.visitType(this, context);
27414 if (!ts__default["default"].isTypeNode(typeNode)) {
27415 throw new Error(`A Type must translate to a TypeNode, but was ${ts__default["default"].SyntaxKind[typeNode.kind]}`);
27416 }
27417 return typeNode;
27418 }
27419 translateExpression(expr, context) {
27420 const typeNode = expr.visitExpression(this, context);
27421 if (!ts__default["default"].isTypeNode(typeNode)) {
27422 throw new Error(`An Expression must translate to a TypeNode, but was ${ts__default["default"].SyntaxKind[typeNode.kind]}`);
27423 }
27424 return typeNode;
27425 }
27426 }
27427
27428 /**
27429 * @license
27430 * Copyright Google LLC All Rights Reserved.
27431 *
27432 * Use of this source code is governed by an MIT-style license that can be
27433 * found in the LICENSE file at https://angular.io/license
27434 */
27435 /**
27436 * Different optimizers use different annotations on a function or method call to indicate its pure
27437 * status.
27438 */
27439 var PureAnnotation;
27440 (function (PureAnnotation) {
27441 /**
27442 * Closure's annotation for purity is `@pureOrBreakMyCode`, but this needs to be in a semantic
27443 * (jsdoc) enabled comment. Thus, the actual comment text for Closure must include the `*` that
27444 * turns a `/*` comment into a `/**` comment, as well as surrounding whitespace.
27445 */
27446 PureAnnotation["CLOSURE"] = "* @pureOrBreakMyCode ";
27447 PureAnnotation["TERSER"] = "@__PURE__";
27448 })(PureAnnotation || (PureAnnotation = {}));
27449 const UNARY_OPERATORS = {
27450 '+': ts__default["default"].SyntaxKind.PlusToken,
27451 '-': ts__default["default"].SyntaxKind.MinusToken,
27452 '!': ts__default["default"].SyntaxKind.ExclamationToken,
27453 };
27454 const BINARY_OPERATORS = {
27455 '&&': ts__default["default"].SyntaxKind.AmpersandAmpersandToken,
27456 '>': ts__default["default"].SyntaxKind.GreaterThanToken,
27457 '>=': ts__default["default"].SyntaxKind.GreaterThanEqualsToken,
27458 '&': ts__default["default"].SyntaxKind.AmpersandToken,
27459 '/': ts__default["default"].SyntaxKind.SlashToken,
27460 '==': ts__default["default"].SyntaxKind.EqualsEqualsToken,
27461 '===': ts__default["default"].SyntaxKind.EqualsEqualsEqualsToken,
27462 '<': ts__default["default"].SyntaxKind.LessThanToken,
27463 '<=': ts__default["default"].SyntaxKind.LessThanEqualsToken,
27464 '-': ts__default["default"].SyntaxKind.MinusToken,
27465 '%': ts__default["default"].SyntaxKind.PercentToken,
27466 '*': ts__default["default"].SyntaxKind.AsteriskToken,
27467 '!=': ts__default["default"].SyntaxKind.ExclamationEqualsToken,
27468 '!==': ts__default["default"].SyntaxKind.ExclamationEqualsEqualsToken,
27469 '||': ts__default["default"].SyntaxKind.BarBarToken,
27470 '+': ts__default["default"].SyntaxKind.PlusToken,
27471 '??': ts__default["default"].SyntaxKind.QuestionQuestionToken,
27472 };
27473 const VAR_TYPES = {
27474 'const': ts__default["default"].NodeFlags.Const,
27475 'let': ts__default["default"].NodeFlags.Let,
27476 'var': ts__default["default"].NodeFlags.None,
27477 };
27478 /**
27479 * A TypeScript flavoured implementation of the AstFactory.
27480 */
27481 class TypeScriptAstFactory {
27482 constructor(annotateForClosureCompiler) {
27483 this.annotateForClosureCompiler = annotateForClosureCompiler;
27484 this.externalSourceFiles = new Map();
27485 this.attachComments = attachComments;
27486 this.createArrayLiteral = ts__default["default"].createArrayLiteral;
27487 this.createConditional = ts__default["default"].createConditional;
27488 this.createElementAccess = ts__default["default"].createElementAccess;
27489 this.createExpressionStatement = ts__default["default"].createExpressionStatement;
27490 this.createIdentifier = ts__default["default"].createIdentifier;
27491 this.createParenthesizedExpression = ts__default["default"].createParen;
27492 this.createPropertyAccess = ts__default["default"].createPropertyAccess;
27493 this.createThrowStatement = ts__default["default"].createThrow;
27494 this.createTypeOfExpression = ts__default["default"].createTypeOf;
27495 }
27496 createAssignment(target, value) {
27497 return ts__default["default"].createBinary(target, ts__default["default"].SyntaxKind.EqualsToken, value);
27498 }
27499 createBinaryExpression(leftOperand, operator, rightOperand) {
27500 return ts__default["default"].createBinary(leftOperand, BINARY_OPERATORS[operator], rightOperand);
27501 }
27502 createBlock(body) {
27503 return ts__default["default"].createBlock(body);
27504 }
27505 createCallExpression(callee, args, pure) {
27506 const call = ts__default["default"].createCall(callee, undefined, args);
27507 if (pure) {
27508 ts__default["default"].addSyntheticLeadingComment(call, ts__default["default"].SyntaxKind.MultiLineCommentTrivia, this.annotateForClosureCompiler ? PureAnnotation.CLOSURE : PureAnnotation.TERSER,
27509 /* trailing newline */ false);
27510 }
27511 return call;
27512 }
27513 createFunctionDeclaration(functionName, parameters, body) {
27514 if (!ts__default["default"].isBlock(body)) {
27515 throw new Error(`Invalid syntax, expected a block, but got ${ts__default["default"].SyntaxKind[body.kind]}.`);
27516 }
27517 return ts__default["default"].createFunctionDeclaration(undefined, undefined, undefined, functionName, undefined, parameters.map(param => ts__default["default"].createParameter(undefined, undefined, undefined, param)), undefined, body);
27518 }
27519 createFunctionExpression(functionName, parameters, body) {
27520 if (!ts__default["default"].isBlock(body)) {
27521 throw new Error(`Invalid syntax, expected a block, but got ${ts__default["default"].SyntaxKind[body.kind]}.`);
27522 }
27523 return ts__default["default"].createFunctionExpression(undefined, undefined, functionName ?? undefined, undefined, parameters.map(param => ts__default["default"].createParameter(undefined, undefined, undefined, param)), undefined, body);
27524 }
27525 createIfStatement(condition, thenStatement, elseStatement) {
27526 return ts__default["default"].createIf(condition, thenStatement, elseStatement ?? undefined);
27527 }
27528 createLiteral(value) {
27529 if (value === undefined) {
27530 return ts__default["default"].createIdentifier('undefined');
27531 }
27532 else if (value === null) {
27533 return ts__default["default"].createNull();
27534 }
27535 else {
27536 return ts__default["default"].createLiteral(value);
27537 }
27538 }
27539 createNewExpression(expression, args) {
27540 return ts__default["default"].createNew(expression, undefined, args);
27541 }
27542 createObjectLiteral(properties) {
27543 return ts__default["default"].createObjectLiteral(properties.map(prop => ts__default["default"].createPropertyAssignment(prop.quoted ? ts__default["default"].createLiteral(prop.propertyName) :
27544 ts__default["default"].createIdentifier(prop.propertyName), prop.value)));
27545 }
27546 createReturnStatement(expression) {
27547 return ts__default["default"].createReturn(expression ?? undefined);
27548 }
27549 createTaggedTemplate(tag, template) {
27550 let templateLiteral;
27551 const length = template.elements.length;
27552 const head = template.elements[0];
27553 if (length === 1) {
27554 templateLiteral = ts__default["default"].createNoSubstitutionTemplateLiteral(head.cooked, head.raw);
27555 }
27556 else {
27557 const spans = [];
27558 // Create the middle parts
27559 for (let i = 1; i < length - 1; i++) {
27560 const { cooked, raw, range } = template.elements[i];
27561 const middle = createTemplateMiddle(cooked, raw);
27562 if (range !== null) {
27563 this.setSourceMapRange(middle, range);
27564 }
27565 spans.push(ts__default["default"].createTemplateSpan(template.expressions[i - 1], middle));
27566 }
27567 // Create the tail part
27568 const resolvedExpression = template.expressions[length - 2];
27569 const templatePart = template.elements[length - 1];
27570 const templateTail = createTemplateTail(templatePart.cooked, templatePart.raw);
27571 if (templatePart.range !== null) {
27572 this.setSourceMapRange(templateTail, templatePart.range);
27573 }
27574 spans.push(ts__default["default"].createTemplateSpan(resolvedExpression, templateTail));
27575 // Put it all together
27576 templateLiteral =
27577 ts__default["default"].createTemplateExpression(ts__default["default"].createTemplateHead(head.cooked, head.raw), spans);
27578 }
27579 if (head.range !== null) {
27580 this.setSourceMapRange(templateLiteral, head.range);
27581 }
27582 return ts__default["default"].createTaggedTemplate(tag, templateLiteral);
27583 }
27584 createUnaryExpression(operator, operand) {
27585 return ts__default["default"].createPrefix(UNARY_OPERATORS[operator], operand);
27586 }
27587 createVariableDeclaration(variableName, initializer, type) {
27588 return ts__default["default"].createVariableStatement(undefined, ts__default["default"].createVariableDeclarationList([ts__default["default"].createVariableDeclaration(variableName, undefined, initializer ?? undefined)], VAR_TYPES[type]));
27589 }
27590 setSourceMapRange(node, sourceMapRange) {
27591 if (sourceMapRange === null) {
27592 return node;
27593 }
27594 const url = sourceMapRange.url;
27595 if (!this.externalSourceFiles.has(url)) {
27596 this.externalSourceFiles.set(url, ts__default["default"].createSourceMapSource(url, sourceMapRange.content, pos => pos));
27597 }
27598 const source = this.externalSourceFiles.get(url);
27599 ts__default["default"].setSourceMapRange(node, { pos: sourceMapRange.start.offset, end: sourceMapRange.end.offset, source });
27600 return node;
27601 }
27602 }
27603 // HACK: Use this in place of `ts.createTemplateMiddle()`.
27604 // Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed.
27605 function createTemplateMiddle(cooked, raw) {
27606 const node = ts__default["default"].createTemplateHead(cooked, raw);
27607 node.kind = ts__default["default"].SyntaxKind.TemplateMiddle;
27608 return node;
27609 }
27610 // HACK: Use this in place of `ts.createTemplateTail()`.
27611 // Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed.
27612 function createTemplateTail(cooked, raw) {
27613 const node = ts__default["default"].createTemplateHead(cooked, raw);
27614 node.kind = ts__default["default"].SyntaxKind.TemplateTail;
27615 return node;
27616 }
27617 /**
27618 * Attach the given `leadingComments` to the `statement` node.
27619 *
27620 * @param statement The statement that will have comments attached.
27621 * @param leadingComments The comments to attach to the statement.
27622 */
27623 function attachComments(statement, leadingComments) {
27624 for (const comment of leadingComments) {
27625 const commentKind = comment.multiline ? ts__default["default"].SyntaxKind.MultiLineCommentTrivia :
27626 ts__default["default"].SyntaxKind.SingleLineCommentTrivia;
27627 if (comment.multiline) {
27628 ts__default["default"].addSyntheticLeadingComment(statement, commentKind, comment.toString(), comment.trailingNewline);
27629 }
27630 else {
27631 for (const line of comment.toString().split('\n')) {
27632 ts__default["default"].addSyntheticLeadingComment(statement, commentKind, line, comment.trailingNewline);
27633 }
27634 }
27635 }
27636 }
27637
27638 /**
27639 * @license
27640 * Copyright Google LLC All Rights Reserved.
27641 *
27642 * Use of this source code is governed by an MIT-style license that can be
27643 * found in the LICENSE file at https://angular.io/license
27644 */
27645 function translateExpression(expression, imports, options = {}) {
27646 return expression.visitExpression(new ExpressionTranslatorVisitor(new TypeScriptAstFactory(options.annotateForClosureCompiler === true), imports, options), new Context$1(false));
27647 }
27648 function translateStatement(statement, imports, options = {}) {
27649 return statement.visitStatement(new ExpressionTranslatorVisitor(new TypeScriptAstFactory(options.annotateForClosureCompiler === true), imports, options), new Context$1(true));
27650 }
27651
27652 /**
27653 * @license
27654 * Copyright Google LLC All Rights Reserved.
27655 *
27656 * Use of this source code is governed by an MIT-style license that can be
27657 * found in the LICENSE file at https://angular.io/license
27658 */
27659 /**
27660 * Adds extra imports in the import manage for this source file, after the existing imports
27661 * and before the module body.
27662 * Can optionally add extra statements (e.g. new constants) before the body as well.
27663 */
27664 function addImports(importManager, sf, extraStatements = []) {
27665 // Generate the import statements to prepend.
27666 const addedImports = importManager.getAllImports(sf.fileName).map(i => {
27667 const qualifier = ts__default["default"].createIdentifier(i.qualifier.text);
27668 const importClause = ts__default["default"].createImportClause(
27669 /* name */ undefined,
27670 /* namedBindings */ ts__default["default"].createNamespaceImport(qualifier));
27671 const decl = ts__default["default"].createImportDeclaration(
27672 /* decorators */ undefined,
27673 /* modifiers */ undefined,
27674 /* importClause */ importClause,
27675 /* moduleSpecifier */ ts__default["default"].createLiteral(i.specifier));
27676 // Set the qualifier's original TS node to the `ts.ImportDeclaration`. This allows downstream
27677 // transforms such as tsickle to properly process references to this import.
27678 //
27679 // This operation is load-bearing in g3 as some imported modules contain special metadata
27680 // generated by clutz, which tsickle uses to transform imports and references to those imports.
27681 //
27682 // TODO(alxhub): add a test for this when tsickle is updated externally to depend on this
27683 // behavior.
27684 ts__default["default"].setOriginalNode(i.qualifier, decl);
27685 return decl;
27686 });
27687 // Filter out the existing imports and the source file body. All new statements
27688 // will be inserted between them.
27689 const existingImports = sf.statements.filter(stmt => isImportStatement(stmt));
27690 const body = sf.statements.filter(stmt => !isImportStatement(stmt));
27691 // Prepend imports if needed.
27692 if (addedImports.length > 0) {
27693 // If we prepend imports, we also prepend NotEmittedStatement to use it as an anchor
27694 // for @fileoverview Closure annotation. If there is no @fileoverview annotations, this
27695 // statement would be a noop.
27696 const fileoverviewAnchorStmt = ts__default["default"].createNotEmittedStatement(sf);
27697 return ts__default["default"].updateSourceFileNode(sf, ts__default["default"].createNodeArray([
27698 fileoverviewAnchorStmt, ...existingImports, ...addedImports, ...extraStatements, ...body
27699 ]));
27700 }
27701 return sf;
27702 }
27703 function isImportStatement(stmt) {
27704 return ts__default["default"].isImportDeclaration(stmt) || ts__default["default"].isImportEqualsDeclaration(stmt) ||
27705 ts__default["default"].isNamespaceImport(stmt);
27706 }
27707
27708 /**
27709 * @license
27710 * Copyright Google LLC All Rights Reserved.
27711 *
27712 * Use of this source code is governed by an MIT-style license that can be
27713 * found in the LICENSE file at https://angular.io/license
27714 */
27715 /**
27716 * Keeps track of `DtsTransform`s per source file, so that it is known which source files need to
27717 * have their declaration file transformed.
27718 */
27719 class DtsTransformRegistry {
27720 constructor() {
27721 this.ivyDeclarationTransforms = new Map();
27722 }
27723 getIvyDeclarationTransform(sf) {
27724 if (!this.ivyDeclarationTransforms.has(sf)) {
27725 this.ivyDeclarationTransforms.set(sf, new IvyDeclarationDtsTransform());
27726 }
27727 return this.ivyDeclarationTransforms.get(sf);
27728 }
27729 /**
27730 * Gets the dts transforms to be applied for the given source file, or `null` if no transform is
27731 * necessary.
27732 */
27733 getAllTransforms(sf) {
27734 // No need to transform if it's not a declarations file, or if no changes have been requested
27735 // to the input file. Due to the way TypeScript afterDeclarations transformers work, the
27736 // `ts.SourceFile` path is the same as the original .ts. The only way we know it's actually a
27737 // declaration file is via the `isDeclarationFile` property.
27738 if (!sf.isDeclarationFile) {
27739 return null;
27740 }
27741 const originalSf = ts__default["default"].getOriginalNode(sf);
27742 let transforms = null;
27743 if (this.ivyDeclarationTransforms.has(originalSf)) {
27744 transforms = [];
27745 transforms.push(this.ivyDeclarationTransforms.get(originalSf));
27746 }
27747 return transforms;
27748 }
27749 }
27750 function declarationTransformFactory(transformRegistry, importRewriter, importPrefix) {
27751 return (context) => {
27752 const transformer = new DtsTransformer(context, importRewriter, importPrefix);
27753 return (fileOrBundle) => {
27754 if (ts__default["default"].isBundle(fileOrBundle)) {
27755 // Only attempt to transform source files.
27756 return fileOrBundle;
27757 }
27758 const transforms = transformRegistry.getAllTransforms(fileOrBundle);
27759 if (transforms === null) {
27760 return fileOrBundle;
27761 }
27762 return transformer.transform(fileOrBundle, transforms);
27763 };
27764 };
27765 }
27766 /**
27767 * Processes .d.ts file text and adds static field declarations, with types.
27768 */
27769 class DtsTransformer {
27770 constructor(ctx, importRewriter, importPrefix) {
27771 this.ctx = ctx;
27772 this.importRewriter = importRewriter;
27773 this.importPrefix = importPrefix;
27774 }
27775 /**
27776 * Transform the declaration file and add any declarations which were recorded.
27777 */
27778 transform(sf, transforms) {
27779 const imports = new ImportManager(this.importRewriter, this.importPrefix);
27780 const visitor = (node) => {
27781 if (ts__default["default"].isClassDeclaration(node)) {
27782 return this.transformClassDeclaration(node, transforms, imports);
27783 }
27784 else if (ts__default["default"].isFunctionDeclaration(node)) {
27785 return this.transformFunctionDeclaration(node, transforms, imports);
27786 }
27787 else {
27788 // Otherwise return node as is.
27789 return ts__default["default"].visitEachChild(node, visitor, this.ctx);
27790 }
27791 };
27792 // Recursively scan through the AST and process all nodes as desired.
27793 sf = ts__default["default"].visitNode(sf, visitor);
27794 // Add new imports for this file.
27795 return addImports(imports, sf);
27796 }
27797 transformClassDeclaration(clazz, transforms, imports) {
27798 let elements = clazz.members;
27799 let elementsChanged = false;
27800 for (const transform of transforms) {
27801 if (transform.transformClassElement !== undefined) {
27802 for (let i = 0; i < elements.length; i++) {
27803 const res = transform.transformClassElement(elements[i], imports);
27804 if (res !== elements[i]) {
27805 if (!elementsChanged) {
27806 elements = [...elements];
27807 elementsChanged = true;
27808 }
27809 elements[i] = res;
27810 }
27811 }
27812 }
27813 }
27814 let newClazz = clazz;
27815 for (const transform of transforms) {
27816 if (transform.transformClass !== undefined) {
27817 // If no DtsTransform has changed the class yet, then the (possibly mutated) elements have
27818 // not yet been incorporated. Otherwise, `newClazz.members` holds the latest class members.
27819 const inputMembers = (clazz === newClazz ? elements : newClazz.members);
27820 newClazz = transform.transformClass(newClazz, inputMembers, imports);
27821 }
27822 }
27823 // If some elements have been transformed but the class itself has not been transformed, create
27824 // an updated class declaration with the updated elements.
27825 if (elementsChanged && clazz === newClazz) {
27826 newClazz = ts__default["default"].updateClassDeclaration(
27827 /* node */ clazz,
27828 /* decorators */ clazz.decorators,
27829 /* modifiers */ clazz.modifiers,
27830 /* name */ clazz.name,
27831 /* typeParameters */ clazz.typeParameters,
27832 /* heritageClauses */ clazz.heritageClauses,
27833 /* members */ elements);
27834 }
27835 return newClazz;
27836 }
27837 transformFunctionDeclaration(declaration, transforms, imports) {
27838 let newDecl = declaration;
27839 for (const transform of transforms) {
27840 if (transform.transformFunctionDeclaration !== undefined) {
27841 newDecl = transform.transformFunctionDeclaration(newDecl, imports);
27842 }
27843 }
27844 return newDecl;
27845 }
27846 }
27847 class IvyDeclarationDtsTransform {
27848 constructor() {
27849 this.declarationFields = new Map();
27850 }
27851 addFields(decl, fields) {
27852 this.declarationFields.set(decl, fields);
27853 }
27854 transformClass(clazz, members, imports) {
27855 const original = ts__default["default"].getOriginalNode(clazz);
27856 if (!this.declarationFields.has(original)) {
27857 return clazz;
27858 }
27859 const fields = this.declarationFields.get(original);
27860 const newMembers = fields.map(decl => {
27861 const modifiers = [ts__default["default"].createModifier(ts__default["default"].SyntaxKind.StaticKeyword)];
27862 const typeRef = translateType(decl.type, imports);
27863 markForEmitAsSingleLine(typeRef);
27864 return ts__default["default"].createProperty(
27865 /* decorators */ undefined,
27866 /* modifiers */ modifiers,
27867 /* name */ decl.name,
27868 /* questionOrExclamationToken */ undefined,
27869 /* type */ typeRef,
27870 /* initializer */ undefined);
27871 });
27872 return ts__default["default"].updateClassDeclaration(
27873 /* node */ clazz,
27874 /* decorators */ clazz.decorators,
27875 /* modifiers */ clazz.modifiers,
27876 /* name */ clazz.name,
27877 /* typeParameters */ clazz.typeParameters,
27878 /* heritageClauses */ clazz.heritageClauses,
27879 /* members */ [...members, ...newMembers]);
27880 }
27881 }
27882 function markForEmitAsSingleLine(node) {
27883 ts__default["default"].setEmitFlags(node, ts__default["default"].EmitFlags.SingleLine);
27884 ts__default["default"].forEachChild(node, markForEmitAsSingleLine);
27885 }
27886
27887 /**
27888 * @license
27889 * Copyright Google LLC All Rights Reserved.
27890 *
27891 * Use of this source code is governed by an MIT-style license that can be
27892 * found in the LICENSE file at https://angular.io/license
27893 */
27894 /**
27895 * Visit a node with the given visitor and return a transformed copy.
27896 */
27897 function visit(node, visitor, context) {
27898 return visitor._visit(node, context);
27899 }
27900 /**
27901 * Abstract base class for visitors, which processes certain nodes specially to allow insertion
27902 * of other nodes before them.
27903 */
27904 class Visitor {
27905 constructor() {
27906 /**
27907 * Maps statements to an array of statements that should be inserted before them.
27908 */
27909 this._before = new Map();
27910 /**
27911 * Maps statements to an array of statements that should be inserted after them.
27912 */
27913 this._after = new Map();
27914 }
27915 _visitListEntryNode(node, visitor) {
27916 const result = visitor(node);
27917 if (result.before !== undefined) {
27918 // Record that some nodes should be inserted before the given declaration. The declaration's
27919 // parent's _visit call is responsible for performing this insertion.
27920 this._before.set(result.node, result.before);
27921 }
27922 if (result.after !== undefined) {
27923 // Same with nodes that should be inserted after.
27924 this._after.set(result.node, result.after);
27925 }
27926 return result.node;
27927 }
27928 /**
27929 * Visit types of nodes which don't have their own explicit visitor.
27930 */
27931 visitOtherNode(node) {
27932 return node;
27933 }
27934 /**
27935 * @internal
27936 */
27937 _visit(node, context) {
27938 // First, visit the node. visitedNode starts off as `null` but should be set after visiting
27939 // is completed.
27940 let visitedNode = null;
27941 node = ts__default["default"].visitEachChild(node, child => this._visit(child, context), context);
27942 if (ts__default["default"].isClassDeclaration(node)) {
27943 visitedNode =
27944 this._visitListEntryNode(node, (node) => this.visitClassDeclaration(node));
27945 }
27946 else {
27947 visitedNode = this.visitOtherNode(node);
27948 }
27949 // If the visited node has a `statements` array then process them, maybe replacing the visited
27950 // node and adding additional statements.
27951 if (hasStatements(visitedNode)) {
27952 visitedNode = this._maybeProcessStatements(visitedNode);
27953 }
27954 return visitedNode;
27955 }
27956 _maybeProcessStatements(node) {
27957 // Shortcut - if every statement doesn't require nodes to be prepended or appended,
27958 // this is a no-op.
27959 if (node.statements.every(stmt => !this._before.has(stmt) && !this._after.has(stmt))) {
27960 return node;
27961 }
27962 // There are statements to prepend, so clone the original node.
27963 const clone = ts__default["default"].getMutableClone(node);
27964 // Build a new list of statements and patch it onto the clone.
27965 const newStatements = [];
27966 clone.statements.forEach(stmt => {
27967 if (this._before.has(stmt)) {
27968 newStatements.push(...this._before.get(stmt));
27969 this._before.delete(stmt);
27970 }
27971 newStatements.push(stmt);
27972 if (this._after.has(stmt)) {
27973 newStatements.push(...this._after.get(stmt));
27974 this._after.delete(stmt);
27975 }
27976 });
27977 clone.statements = ts__default["default"].createNodeArray(newStatements, node.statements.hasTrailingComma);
27978 return clone;
27979 }
27980 }
27981 function hasStatements(node) {
27982 const block = node;
27983 return block.statements !== undefined && Array.isArray(block.statements);
27984 }
27985
27986 /**
27987 * @license
27988 * Copyright Google LLC All Rights Reserved.
27989 *
27990 * Use of this source code is governed by an MIT-style license that can be
27991 * found in the LICENSE file at https://angular.io/license
27992 */
27993 const NO_DECORATORS = new Set();
27994 const CLOSURE_FILE_OVERVIEW_REGEXP = /\s+@fileoverview\s+/i;
27995 function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportTracker, perf, isCore, isClosureCompilerEnabled) {
27996 const recordWrappedNode = createRecorderFn(defaultImportTracker);
27997 return (context) => {
27998 return (file) => {
27999 return perf.inPhase(PerfPhase.Compile, () => transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNode));
28000 };
28001 };
28002 }
28003 /**
28004 * Visits all classes, performs Ivy compilation where Angular decorators are present and collects
28005 * result in a Map that associates a ts.ClassDeclaration with Ivy compilation results. This visitor
28006 * does NOT perform any TS transformations.
28007 */
28008 class IvyCompilationVisitor extends Visitor {
28009 constructor(compilation, constantPool) {
28010 super();
28011 this.compilation = compilation;
28012 this.constantPool = constantPool;
28013 this.classCompilationMap = new Map();
28014 }
28015 visitClassDeclaration(node) {
28016 // Determine if this class has an Ivy field that needs to be added, and compile the field
28017 // to an expression if so.
28018 const result = this.compilation.compile(node, this.constantPool);
28019 if (result !== null) {
28020 this.classCompilationMap.set(node, result);
28021 }
28022 return { node };
28023 }
28024 }
28025 /**
28026 * Visits all classes and performs transformation of corresponding TS nodes based on the Ivy
28027 * compilation results (provided as an argument).
28028 */
28029 class IvyTransformationVisitor extends Visitor {
28030 constructor(compilation, classCompilationMap, reflector, importManager, recordWrappedNodeExpr, isClosureCompilerEnabled, isCore) {
28031 super();
28032 this.compilation = compilation;
28033 this.classCompilationMap = classCompilationMap;
28034 this.reflector = reflector;
28035 this.importManager = importManager;
28036 this.recordWrappedNodeExpr = recordWrappedNodeExpr;
28037 this.isClosureCompilerEnabled = isClosureCompilerEnabled;
28038 this.isCore = isCore;
28039 }
28040 visitClassDeclaration(node) {
28041 // If this class is not registered in the map, it means that it doesn't have Angular decorators,
28042 // thus no further processing is required.
28043 if (!this.classCompilationMap.has(node)) {
28044 return { node };
28045 }
28046 const translateOptions = {
28047 recordWrappedNode: this.recordWrappedNodeExpr,
28048 annotateForClosureCompiler: this.isClosureCompilerEnabled,
28049 };
28050 // There is at least one field to add.
28051 const statements = [];
28052 const members = [...node.members];
28053 for (const field of this.classCompilationMap.get(node)) {
28054 // Translate the initializer for the field into TS nodes.
28055 const exprNode = translateExpression(field.initializer, this.importManager, translateOptions);
28056 // Create a static property declaration for the new field.
28057 const property = ts__default["default"].createProperty(undefined, [ts__default["default"].createToken(ts__default["default"].SyntaxKind.StaticKeyword)], field.name, undefined, undefined, exprNode);
28058 if (this.isClosureCompilerEnabled) {
28059 // Closure compiler transforms the form `Service.ɵprov = X` into `Service$ɵprov = X`. To
28060 // prevent this transformation, such assignments need to be annotated with @nocollapse.
28061 // Note that tsickle is typically responsible for adding such annotations, however it
28062 // doesn't yet handle synthetic fields added during other transformations.
28063 ts__default["default"].addSyntheticLeadingComment(property, ts__default["default"].SyntaxKind.MultiLineCommentTrivia, '* @nocollapse ',
28064 /* hasTrailingNewLine */ false);
28065 }
28066 field.statements.map(stmt => translateStatement(stmt, this.importManager, translateOptions))
28067 .forEach(stmt => statements.push(stmt));
28068 members.push(property);
28069 }
28070 // Replace the class declaration with an updated version.
28071 node = ts__default["default"].updateClassDeclaration(node,
28072 // Remove the decorator which triggered this compilation, leaving the others alone.
28073 maybeFilterDecorator(node.decorators, this.compilation.decoratorsFor(node)), node.modifiers, node.name, node.typeParameters, node.heritageClauses || [],
28074 // Map over the class members and remove any Angular decorators from them.
28075 members.map(member => this._stripAngularDecorators(member)));
28076 return { node, after: statements };
28077 }
28078 /**
28079 * Return all decorators on a `Declaration` which are from @angular/core, or an empty set if none
28080 * are.
28081 */
28082 _angularCoreDecorators(decl) {
28083 const decorators = this.reflector.getDecoratorsOfDeclaration(decl);
28084 if (decorators === null) {
28085 return NO_DECORATORS;
28086 }
28087 const coreDecorators = decorators.filter(dec => this.isCore || isFromAngularCore(dec))
28088 .map(dec => dec.node);
28089 if (coreDecorators.length > 0) {
28090 return new Set(coreDecorators);
28091 }
28092 else {
28093 return NO_DECORATORS;
28094 }
28095 }
28096 /**
28097 * Given a `ts.Node`, filter the decorators array and return a version containing only non-Angular
28098 * decorators.
28099 *
28100 * If all decorators are removed (or none existed in the first place), this method returns
28101 * `undefined`.
28102 */
28103 _nonCoreDecoratorsOnly(node) {
28104 // Shortcut if the node has no decorators.
28105 if (node.decorators === undefined) {
28106 return undefined;
28107 }
28108 // Build a Set of the decorators on this node from @angular/core.
28109 const coreDecorators = this._angularCoreDecorators(node);
28110 if (coreDecorators.size === node.decorators.length) {
28111 // If all decorators are to be removed, return `undefined`.
28112 return undefined;
28113 }
28114 else if (coreDecorators.size === 0) {
28115 // If no decorators need to be removed, return the original decorators array.
28116 return node.decorators;
28117 }
28118 // Filter out the core decorators.
28119 const filtered = node.decorators.filter(dec => !coreDecorators.has(dec));
28120 // If no decorators survive, return `undefined`. This can only happen if a core decorator is
28121 // repeated on the node.
28122 if (filtered.length === 0) {
28123 return undefined;
28124 }
28125 // Create a new `NodeArray` with the filtered decorators that sourcemaps back to the original.
28126 const array = ts__default["default"].createNodeArray(filtered);
28127 array.pos = node.decorators.pos;
28128 array.end = node.decorators.end;
28129 return array;
28130 }
28131 /**
28132 * Remove Angular decorators from a `ts.Node` in a shallow manner.
28133 *
28134 * This will remove decorators from class elements (getters, setters, properties, methods) as well
28135 * as parameters of constructors.
28136 */
28137 _stripAngularDecorators(node) {
28138 if (ts__default["default"].isParameter(node)) {
28139 // Strip decorators from parameters (probably of the constructor).
28140 node = ts__default["default"].updateParameter(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.dotDotDotToken, node.name, node.questionToken, node.type, node.initializer);
28141 }
28142 else if (ts__default["default"].isMethodDeclaration(node) && node.decorators !== undefined) {
28143 // Strip decorators of methods.
28144 node = ts__default["default"].updateMethod(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters, node.type, node.body);
28145 }
28146 else if (ts__default["default"].isPropertyDeclaration(node) && node.decorators !== undefined) {
28147 // Strip decorators of properties.
28148 node = ts__default["default"].updateProperty(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name, node.questionToken, node.type, node.initializer);
28149 }
28150 else if (ts__default["default"].isGetAccessor(node)) {
28151 // Strip decorators of getters.
28152 node = ts__default["default"].updateGetAccessor(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name, node.parameters, node.type, node.body);
28153 }
28154 else if (ts__default["default"].isSetAccessor(node)) {
28155 // Strip decorators of setters.
28156 node = ts__default["default"].updateSetAccessor(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name, node.parameters, node.body);
28157 }
28158 else if (ts__default["default"].isConstructorDeclaration(node)) {
28159 // For constructors, strip decorators of the parameters.
28160 const parameters = node.parameters.map(param => this._stripAngularDecorators(param));
28161 node =
28162 ts__default["default"].updateConstructor(node, node.decorators, node.modifiers, parameters, node.body);
28163 }
28164 return node;
28165 }
28166 }
28167 /**
28168 * A transformer which operates on ts.SourceFiles and applies changes from an `IvyCompilation`.
28169 */
28170 function transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNode) {
28171 const constantPool = new ConstantPool(isClosureCompilerEnabled);
28172 const importManager = new ImportManager(importRewriter);
28173 // The transformation process consists of 2 steps:
28174 //
28175 // 1. Visit all classes, perform compilation and collect the results.
28176 // 2. Perform actual transformation of required TS nodes using compilation results from the first
28177 // step.
28178 //
28179 // This is needed to have all `o.Expression`s generated before any TS transforms happen. This
28180 // allows `ConstantPool` to properly identify expressions that can be shared across multiple
28181 // components declared in the same file.
28182 // Step 1. Go though all classes in AST, perform compilation and collect the results.
28183 const compilationVisitor = new IvyCompilationVisitor(compilation, constantPool);
28184 visit(file, compilationVisitor, context);
28185 // Step 2. Scan through the AST again and perform transformations based on Ivy compilation
28186 // results obtained at Step 1.
28187 const transformationVisitor = new IvyTransformationVisitor(compilation, compilationVisitor.classCompilationMap, reflector, importManager, recordWrappedNode, isClosureCompilerEnabled, isCore);
28188 let sf = visit(file, transformationVisitor, context);
28189 // Generate the constant statements first, as they may involve adding additional imports
28190 // to the ImportManager.
28191 const downlevelTranslatedCode = getLocalizeCompileTarget(context) < ts__default["default"].ScriptTarget.ES2015;
28192 const constants = constantPool.statements.map(stmt => translateStatement(stmt, importManager, {
28193 recordWrappedNode,
28194 downlevelTaggedTemplates: downlevelTranslatedCode,
28195 downlevelVariableDeclarations: downlevelTranslatedCode,
28196 annotateForClosureCompiler: isClosureCompilerEnabled,
28197 }));
28198 // Preserve @fileoverview comments required by Closure, since the location might change as a
28199 // result of adding extra imports and constant pool statements.
28200 const fileOverviewMeta = isClosureCompilerEnabled ? getFileOverviewComment(sf.statements) : null;
28201 // Add new imports for this file.
28202 sf = addImports(importManager, sf, constants);
28203 if (fileOverviewMeta !== null) {
28204 setFileOverviewComment(sf, fileOverviewMeta);
28205 }
28206 return sf;
28207 }
28208 /**
28209 * Compute the correct target output for `$localize` messages generated by Angular
28210 *
28211 * In some versions of TypeScript, the transformation of synthetic `$localize` tagged template
28212 * literals is broken. See https://github.com/microsoft/TypeScript/issues/38485
28213 *
28214 * Here we compute what the expected final output target of the compilation will
28215 * be so that we can generate ES5 compliant `$localize` calls instead of relying upon TS to do the
28216 * downleveling for us.
28217 */
28218 function getLocalizeCompileTarget(context) {
28219 const target = context.getCompilerOptions().target || ts__default["default"].ScriptTarget.ES2015;
28220 return target !== ts__default["default"].ScriptTarget.JSON ? target : ts__default["default"].ScriptTarget.ES2015;
28221 }
28222 function getFileOverviewComment(statements) {
28223 if (statements.length > 0) {
28224 const host = statements[0];
28225 let trailing = false;
28226 let comments = ts__default["default"].getSyntheticLeadingComments(host);
28227 // If @fileoverview tag is not found in source file, tsickle produces fake node with trailing
28228 // comment and inject it at the very beginning of the generated file. So we need to check for
28229 // leading as well as trailing comments.
28230 if (!comments || comments.length === 0) {
28231 trailing = true;
28232 comments = ts__default["default"].getSyntheticTrailingComments(host);
28233 }
28234 if (comments && comments.length > 0 && CLOSURE_FILE_OVERVIEW_REGEXP.test(comments[0].text)) {
28235 return { comments, host, trailing };
28236 }
28237 }
28238 return null;
28239 }
28240 function setFileOverviewComment(sf, fileoverview) {
28241 const { comments, host, trailing } = fileoverview;
28242 // If host statement is no longer the first one, it means that extra statements were added at the
28243 // very beginning, so we need to relocate @fileoverview comment and cleanup the original statement
28244 // that hosted it.
28245 if (sf.statements.length > 0 && host !== sf.statements[0]) {
28246 if (trailing) {
28247 ts__default["default"].setSyntheticTrailingComments(host, undefined);
28248 }
28249 else {
28250 ts__default["default"].setSyntheticLeadingComments(host, undefined);
28251 }
28252 ts__default["default"].setSyntheticLeadingComments(sf.statements[0], comments);
28253 }
28254 }
28255 function maybeFilterDecorator(decorators, toRemove) {
28256 if (decorators === undefined) {
28257 return undefined;
28258 }
28259 const filtered = decorators.filter(dec => toRemove.find(decToRemove => ts__default["default"].getOriginalNode(dec) === decToRemove) === undefined);
28260 if (filtered.length === 0) {
28261 return undefined;
28262 }
28263 return ts__default["default"].createNodeArray(filtered);
28264 }
28265 function isFromAngularCore(decorator) {
28266 return decorator.import !== null && decorator.import.from === '@angular/core';
28267 }
28268 function createRecorderFn(defaultImportTracker) {
28269 return node => {
28270 const importDecl = getDefaultImportDeclaration(node);
28271 if (importDecl !== null) {
28272 defaultImportTracker.recordUsedImport(importDecl);
28273 }
28274 };
28275 }
28276
28277 /**
28278 * @license
28279 * Copyright Google LLC All Rights Reserved.
28280 *
28281 * Use of this source code is governed by an MIT-style license that can be
28282 * found in the LICENSE file at https://angular.io/license
28283 */
28284 function getConstructorDependencies(clazz, reflector, isCore) {
28285 const deps = [];
28286 const errors = [];
28287 let ctorParams = reflector.getConstructorParameters(clazz);
28288 if (ctorParams === null) {
28289 if (reflector.hasBaseClass(clazz)) {
28290 return null;
28291 }
28292 else {
28293 ctorParams = [];
28294 }
28295 }
28296 ctorParams.forEach((param, idx) => {
28297 let token = valueReferenceToExpression(param.typeValueReference);
28298 let attributeNameType = null;
28299 let optional = false, self = false, skipSelf = false, host = false;
28300 (param.decorators || []).filter(dec => isCore || isAngularCore(dec)).forEach(dec => {
28301 const name = isCore || dec.import === null ? dec.name : dec.import.name;
28302 if (name === 'Inject') {
28303 if (dec.args === null || dec.args.length !== 1) {
28304 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(dec), `Unexpected number of arguments to @Inject().`);
28305 }
28306 token = new WrappedNodeExpr(dec.args[0]);
28307 }
28308 else if (name === 'Optional') {
28309 optional = true;
28310 }
28311 else if (name === 'SkipSelf') {
28312 skipSelf = true;
28313 }
28314 else if (name === 'Self') {
28315 self = true;
28316 }
28317 else if (name === 'Host') {
28318 host = true;
28319 }
28320 else if (name === 'Attribute') {
28321 if (dec.args === null || dec.args.length !== 1) {
28322 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(dec), `Unexpected number of arguments to @Attribute().`);
28323 }
28324 const attributeName = dec.args[0];
28325 token = new WrappedNodeExpr(attributeName);
28326 if (ts__default["default"].isStringLiteralLike(attributeName)) {
28327 attributeNameType = new LiteralExpr(attributeName.text);
28328 }
28329 else {
28330 attributeNameType =
28331 new WrappedNodeExpr(ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.UnknownKeyword));
28332 }
28333 }
28334 else {
28335 throw new FatalDiagnosticError(ErrorCode.DECORATOR_UNEXPECTED, Decorator.nodeForError(dec), `Unexpected decorator ${name} on parameter.`);
28336 }
28337 });
28338 if (token === null) {
28339 if (param.typeValueReference.kind !== 2 /* UNAVAILABLE */) {
28340 throw new Error('Illegal state: expected value reference to be unavailable if no token is present');
28341 }
28342 errors.push({
28343 index: idx,
28344 param,
28345 reason: param.typeValueReference.reason,
28346 });
28347 }
28348 else {
28349 deps.push({ token, attributeNameType, optional, self, skipSelf, host });
28350 }
28351 });
28352 if (errors.length === 0) {
28353 return { deps };
28354 }
28355 else {
28356 return { deps: null, errors };
28357 }
28358 }
28359 function valueReferenceToExpression(valueRef) {
28360 if (valueRef.kind === 2 /* UNAVAILABLE */) {
28361 return null;
28362 }
28363 else if (valueRef.kind === 0 /* LOCAL */) {
28364 const expr = new WrappedNodeExpr(valueRef.expression);
28365 if (valueRef.defaultImportStatement !== null) {
28366 attachDefaultImportDeclaration(expr, valueRef.defaultImportStatement);
28367 }
28368 return expr;
28369 }
28370 else {
28371 let importExpr = new ExternalExpr({ moduleName: valueRef.moduleName, name: valueRef.importedName });
28372 if (valueRef.nestedPath !== null) {
28373 for (const property of valueRef.nestedPath) {
28374 importExpr = new ReadPropExpr(importExpr, property);
28375 }
28376 }
28377 return importExpr;
28378 }
28379 }
28380 /**
28381 * Convert `ConstructorDeps` into the `R3DependencyMetadata` array for those deps if they're valid,
28382 * or into an `'invalid'` signal if they're not.
28383 *
28384 * This is a companion function to `validateConstructorDependencies` which accepts invalid deps.
28385 */
28386 function unwrapConstructorDependencies(deps) {
28387 if (deps === null) {
28388 return null;
28389 }
28390 else if (deps.deps !== null) {
28391 // These constructor dependencies are valid.
28392 return deps.deps;
28393 }
28394 else {
28395 // These deps are invalid.
28396 return 'invalid';
28397 }
28398 }
28399 function getValidConstructorDependencies(clazz, reflector, isCore) {
28400 return validateConstructorDependencies(clazz, getConstructorDependencies(clazz, reflector, isCore));
28401 }
28402 /**
28403 * Validate that `ConstructorDeps` does not have any invalid dependencies and convert them into the
28404 * `R3DependencyMetadata` array if so, or raise a diagnostic if some deps are invalid.
28405 *
28406 * This is a companion function to `unwrapConstructorDependencies` which does not accept invalid
28407 * deps.
28408 */
28409 function validateConstructorDependencies(clazz, deps) {
28410 if (deps === null) {
28411 return null;
28412 }
28413 else if (deps.deps !== null) {
28414 return deps.deps;
28415 }
28416 else {
28417 // TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here.
28418 // There is at least one error.
28419 const error = deps.errors[0];
28420 throw createUnsuitableInjectionTokenError(clazz, error);
28421 }
28422 }
28423 /**
28424 * Creates a fatal error with diagnostic for an invalid injection token.
28425 * @param clazz The class for which the injection token was unavailable.
28426 * @param error The reason why no valid injection token is available.
28427 */
28428 function createUnsuitableInjectionTokenError(clazz, error) {
28429 const { param, index, reason } = error;
28430 let chainMessage = undefined;
28431 let hints = undefined;
28432 switch (reason.kind) {
28433 case 5 /* UNSUPPORTED */:
28434 chainMessage = 'Consider using the @Inject decorator to specify an injection token.';
28435 hints = [
28436 makeRelatedInformation(reason.typeNode, 'This type is not supported as injection token.'),
28437 ];
28438 break;
28439 case 1 /* NO_VALUE_DECLARATION */:
28440 chainMessage = 'Consider using the @Inject decorator to specify an injection token.';
28441 hints = [
28442 makeRelatedInformation(reason.typeNode, 'This type does not have a value, so it cannot be used as injection token.'),
28443 ];
28444 if (reason.decl !== null) {
28445 hints.push(makeRelatedInformation(reason.decl, 'The type is declared here.'));
28446 }
28447 break;
28448 case 2 /* TYPE_ONLY_IMPORT */:
28449 chainMessage =
28450 'Consider changing the type-only import to a regular import, or use the @Inject decorator to specify an injection token.';
28451 hints = [
28452 makeRelatedInformation(reason.typeNode, 'This type is imported using a type-only import, which prevents it from being usable as an injection token.'),
28453 makeRelatedInformation(reason.importClause, 'The type-only import occurs here.'),
28454 ];
28455 break;
28456 case 4 /* NAMESPACE */:
28457 chainMessage = 'Consider using the @Inject decorator to specify an injection token.';
28458 hints = [
28459 makeRelatedInformation(reason.typeNode, 'This type corresponds with a namespace, which cannot be used as injection token.'),
28460 makeRelatedInformation(reason.importClause, 'The namespace import occurs here.'),
28461 ];
28462 break;
28463 case 3 /* UNKNOWN_REFERENCE */:
28464 chainMessage = 'The type should reference a known declaration.';
28465 hints = [makeRelatedInformation(reason.typeNode, 'This type could not be resolved.')];
28466 break;
28467 case 0 /* MISSING_TYPE */:
28468 chainMessage =
28469 'Consider adding a type to the parameter or use the @Inject decorator to specify an injection token.';
28470 break;
28471 }
28472 const chain = {
28473 messageText: `No suitable injection token for parameter '${param.name || index}' of class '${clazz.name.text}'.`,
28474 category: ts__default["default"].DiagnosticCategory.Error,
28475 code: 0,
28476 next: [{
28477 messageText: chainMessage,
28478 category: ts__default["default"].DiagnosticCategory.Message,
28479 code: 0,
28480 }],
28481 };
28482 return new FatalDiagnosticError(ErrorCode.PARAM_MISSING_TOKEN, param.nameNode, chain, hints);
28483 }
28484 function toR3Reference(valueRef, typeRef, valueContext, typeContext, refEmitter) {
28485 return {
28486 value: refEmitter.emit(valueRef, valueContext).expression,
28487 type: refEmitter
28488 .emit(typeRef, typeContext, ImportFlags.ForceNewImport | ImportFlags.AllowTypeImports)
28489 .expression,
28490 };
28491 }
28492 function isAngularCore(decorator) {
28493 return decorator.import !== null && decorator.import.from === '@angular/core';
28494 }
28495 function isAngularCoreReference(reference, symbolName) {
28496 return reference.ownedByModuleGuess === '@angular/core' && reference.debugName === symbolName;
28497 }
28498 function findAngularDecorator(decorators, name, isCore) {
28499 return decorators.find(decorator => isAngularDecorator$1(decorator, name, isCore));
28500 }
28501 function isAngularDecorator$1(decorator, name, isCore) {
28502 if (isCore) {
28503 return decorator.name === name;
28504 }
28505 else if (isAngularCore(decorator)) {
28506 return decorator.import.name === name;
28507 }
28508 return false;
28509 }
28510 /**
28511 * Unwrap a `ts.Expression`, removing outer type-casts or parentheses until the expression is in its
28512 * lowest level form.
28513 *
28514 * For example, the expression "(foo as Type)" unwraps to "foo".
28515 */
28516 function unwrapExpression(node) {
28517 while (ts__default["default"].isAsExpression(node) || ts__default["default"].isParenthesizedExpression(node)) {
28518 node = node.expression;
28519 }
28520 return node;
28521 }
28522 function expandForwardRef(arg) {
28523 arg = unwrapExpression(arg);
28524 if (!ts__default["default"].isArrowFunction(arg) && !ts__default["default"].isFunctionExpression(arg)) {
28525 return null;
28526 }
28527 const body = arg.body;
28528 // Either the body is a ts.Expression directly, or a block with a single return statement.
28529 if (ts__default["default"].isBlock(body)) {
28530 // Block body - look for a single return statement.
28531 if (body.statements.length !== 1) {
28532 return null;
28533 }
28534 const stmt = body.statements[0];
28535 if (!ts__default["default"].isReturnStatement(stmt) || stmt.expression === undefined) {
28536 return null;
28537 }
28538 return stmt.expression;
28539 }
28540 else {
28541 // Shorthand body - return as an expression.
28542 return body;
28543 }
28544 }
28545 /**
28546 * If the given `node` is a forwardRef() expression then resolve its inner value, otherwise return
28547 * `null`.
28548 *
28549 * @param node the forwardRef() expression to resolve
28550 * @param reflector a ReflectionHost
28551 * @returns the resolved expression, if the original expression was a forwardRef(), or `null`
28552 * otherwise.
28553 */
28554 function tryUnwrapForwardRef(node, reflector) {
28555 node = unwrapExpression(node);
28556 if (!ts__default["default"].isCallExpression(node) || node.arguments.length !== 1) {
28557 return null;
28558 }
28559 const fn = ts__default["default"].isPropertyAccessExpression(node.expression) ? node.expression.name : node.expression;
28560 if (!ts__default["default"].isIdentifier(fn)) {
28561 return null;
28562 }
28563 const expr = expandForwardRef(node.arguments[0]);
28564 if (expr === null) {
28565 return null;
28566 }
28567 const imp = reflector.getImportOfIdentifier(fn);
28568 if (imp === null || imp.from !== '@angular/core' || imp.name !== 'forwardRef') {
28569 return null;
28570 }
28571 return expr;
28572 }
28573 /**
28574 * A foreign function resolver for `staticallyResolve` which unwraps forwardRef() expressions.
28575 *
28576 * @param ref a Reference to the declaration of the function being called (which might be
28577 * forwardRef)
28578 * @param args the arguments to the invocation of the forwardRef expression
28579 * @returns an unwrapped argument if `ref` pointed to forwardRef, or null otherwise
28580 */
28581 function forwardRefResolver(ref, args) {
28582 if (!isAngularCoreReference(ref, 'forwardRef') || args.length !== 1) {
28583 return null;
28584 }
28585 return expandForwardRef(args[0]);
28586 }
28587 /**
28588 * Combines an array of resolver functions into a one.
28589 * @param resolvers Resolvers to be combined.
28590 */
28591 function combineResolvers(resolvers) {
28592 return (ref, args) => {
28593 for (const resolver of resolvers) {
28594 const resolved = resolver(ref, args);
28595 if (resolved !== null) {
28596 return resolved;
28597 }
28598 }
28599 return null;
28600 };
28601 }
28602 function isExpressionForwardReference(expr, context, contextSource) {
28603 if (isWrappedTsNodeExpr(expr)) {
28604 const node = ts__default["default"].getOriginalNode(expr.node);
28605 return node.getSourceFile() === contextSource && context.pos < node.pos;
28606 }
28607 else {
28608 return false;
28609 }
28610 }
28611 function isWrappedTsNodeExpr(expr) {
28612 return expr instanceof WrappedNodeExpr;
28613 }
28614 function readBaseClass(node, reflector, evaluator) {
28615 const baseExpression = reflector.getBaseClassExpression(node);
28616 if (baseExpression !== null) {
28617 const baseClass = evaluator.evaluate(baseExpression);
28618 if (baseClass instanceof Reference && reflector.isClass(baseClass.node)) {
28619 return baseClass;
28620 }
28621 else {
28622 return 'dynamic';
28623 }
28624 }
28625 return null;
28626 }
28627 const parensWrapperTransformerFactory = (context) => {
28628 const visitor = (node) => {
28629 const visited = ts__default["default"].visitEachChild(node, visitor, context);
28630 if (ts__default["default"].isArrowFunction(visited) || ts__default["default"].isFunctionExpression(visited)) {
28631 return ts__default["default"].createParen(visited);
28632 }
28633 return visited;
28634 };
28635 return (node) => ts__default["default"].visitEachChild(node, visitor, context);
28636 };
28637 /**
28638 * Wraps all functions in a given expression in parentheses. This is needed to avoid problems
28639 * where Tsickle annotations added between analyse and transform phases in Angular may trigger
28640 * automatic semicolon insertion, e.g. if a function is the expression in a `return` statement.
28641 * More
28642 * info can be found in Tsickle source code here:
28643 * https://github.com/angular/tsickle/blob/d7974262571c8a17d684e5ba07680e1b1993afdd/src/jsdoc_transformer.ts#L1021
28644 *
28645 * @param expression Expression where functions should be wrapped in parentheses
28646 */
28647 function wrapFunctionExpressionsInParens(expression) {
28648 return ts__default["default"].transform(expression, [parensWrapperTransformerFactory]).transformed[0];
28649 }
28650 /**
28651 * Create a `ts.Diagnostic` which indicates the given class is part of the declarations of two or
28652 * more NgModules.
28653 *
28654 * The resulting `ts.Diagnostic` will have a context entry for each NgModule showing the point where
28655 * the directive/pipe exists in its `declarations` (if possible).
28656 */
28657 function makeDuplicateDeclarationError(node, data, kind) {
28658 const context = [];
28659 for (const decl of data) {
28660 if (decl.rawDeclarations === null) {
28661 continue;
28662 }
28663 // Try to find the reference to the declaration within the declarations array, to hang the
28664 // error there. If it can't be found, fall back on using the NgModule's name.
28665 const contextNode = decl.ref.getOriginForDiagnostics(decl.rawDeclarations, decl.ngModule.name);
28666 context.push(makeRelatedInformation(contextNode, `'${node.name.text}' is listed in the declarations of the NgModule '${decl.ngModule.name.text}'.`));
28667 }
28668 // Finally, produce the diagnostic.
28669 return makeDiagnostic(ErrorCode.NGMODULE_DECLARATION_NOT_UNIQUE, node.name, `The ${kind} '${node.name.text}' is declared by more than one NgModule.`, context);
28670 }
28671 /**
28672 * Resolves the given `rawProviders` into `ClassDeclarations` and returns
28673 * a set containing those that are known to require a factory definition.
28674 * @param rawProviders Expression that declared the providers array in the source.
28675 */
28676 function resolveProvidersRequiringFactory(rawProviders, reflector, evaluator) {
28677 const providers = new Set();
28678 const resolvedProviders = evaluator.evaluate(rawProviders);
28679 if (!Array.isArray(resolvedProviders)) {
28680 return providers;
28681 }
28682 resolvedProviders.forEach(function processProviders(provider) {
28683 let tokenClass = null;
28684 if (Array.isArray(provider)) {
28685 // If we ran into an array, recurse into it until we've resolve all the classes.
28686 provider.forEach(processProviders);
28687 }
28688 else if (provider instanceof Reference) {
28689 tokenClass = provider;
28690 }
28691 else if (provider instanceof Map && provider.has('useClass') && !provider.has('deps')) {
28692 const useExisting = provider.get('useClass');
28693 if (useExisting instanceof Reference) {
28694 tokenClass = useExisting;
28695 }
28696 }
28697 // TODO(alxhub): there was a bug where `getConstructorParameters` would return `null` for a
28698 // class in a .d.ts file, always, even if the class had a constructor. This was fixed for
28699 // `getConstructorParameters`, but that fix causes more classes to be recognized here as needing
28700 // provider checks, which is a breaking change in g3. Avoid this breakage for now by skipping
28701 // classes from .d.ts files here directly, until g3 can be cleaned up.
28702 if (tokenClass !== null && !tokenClass.node.getSourceFile().isDeclarationFile &&
28703 reflector.isClass(tokenClass.node)) {
28704 const constructorParameters = reflector.getConstructorParameters(tokenClass.node);
28705 // Note that we only want to capture providers with a non-trivial constructor,
28706 // because they're the ones that might be using DI and need to be decorated.
28707 if (constructorParameters !== null && constructorParameters.length > 0) {
28708 providers.add(tokenClass);
28709 }
28710 }
28711 });
28712 return providers;
28713 }
28714 /**
28715 * Create an R3Reference for a class.
28716 *
28717 * The `value` is the exported declaration of the class from its source file.
28718 * The `type` is an expression that would be used by ngcc in the typings (.d.ts) files.
28719 */
28720 function wrapTypeReference(reflector, clazz) {
28721 const dtsClass = reflector.getDtsDeclaration(clazz);
28722 const value = new WrappedNodeExpr(clazz.name);
28723 const type = dtsClass !== null && isNamedClassDeclaration(dtsClass) ?
28724 new WrappedNodeExpr(dtsClass.name) :
28725 value;
28726 return { value, type };
28727 }
28728 /** Creates a ParseSourceSpan for a TypeScript node. */
28729 function createSourceSpan(node) {
28730 const sf = node.getSourceFile();
28731 const [startOffset, endOffset] = [node.getStart(), node.getEnd()];
28732 const { line: startLine, character: startCol } = sf.getLineAndCharacterOfPosition(startOffset);
28733 const { line: endLine, character: endCol } = sf.getLineAndCharacterOfPosition(endOffset);
28734 const parseSf = new ParseSourceFile(sf.getFullText(), sf.fileName);
28735 // +1 because values are zero-indexed.
28736 return new ParseSourceSpan(new ParseLocation(parseSf, startOffset, startLine + 1, startCol + 1), new ParseLocation(parseSf, endOffset, endLine + 1, endCol + 1));
28737 }
28738 /**
28739 * Collate the factory and definition compiled results into an array of CompileResult objects.
28740 */
28741 function compileResults(fac, def, metadataStmt, propName) {
28742 const statements = def.statements;
28743 if (metadataStmt !== null) {
28744 statements.push(metadataStmt);
28745 }
28746 return [
28747 fac, {
28748 name: propName,
28749 initializer: def.expression,
28750 statements: def.statements,
28751 type: def.type,
28752 }
28753 ];
28754 }
28755 function toFactoryMetadata(meta, target) {
28756 return {
28757 name: meta.name,
28758 type: meta.type,
28759 internalType: meta.internalType,
28760 typeArgumentCount: meta.typeArgumentCount,
28761 deps: meta.deps,
28762 target
28763 };
28764 }
28765
28766 /**
28767 * @license
28768 * Copyright Google LLC All Rights Reserved.
28769 *
28770 * Use of this source code is governed by an MIT-style license that can be
28771 * found in the LICENSE file at https://angular.io/license
28772 */
28773 /**
28774 * Creates a `FatalDiagnosticError` for a node that did not evaluate to the expected type. The
28775 * diagnostic that is created will include details on why the value is incorrect, i.e. it includes
28776 * a representation of the actual type that was unsupported, or in the case of a dynamic value the
28777 * trace to the node where the dynamic value originated.
28778 *
28779 * @param node The node for which the diagnostic should be produced.
28780 * @param value The evaluated value that has the wrong type.
28781 * @param messageText The message text of the error.
28782 */
28783 function createValueHasWrongTypeError(node, value, messageText) {
28784 let chainedMessage;
28785 let relatedInformation;
28786 if (value instanceof DynamicValue) {
28787 chainedMessage = 'Value could not be determined statically.';
28788 relatedInformation = traceDynamicValue(node, value);
28789 }
28790 else if (value instanceof Reference) {
28791 const target = value.debugName !== null ? `'${value.debugName}'` : 'an anonymous declaration';
28792 chainedMessage = `Value is a reference to ${target}.`;
28793 const referenceNode = identifierOfNode(value.node) ?? value.node;
28794 relatedInformation = [makeRelatedInformation(referenceNode, 'Reference is declared here.')];
28795 }
28796 else {
28797 chainedMessage = `Value is of type '${describeResolvedType(value)}'.`;
28798 }
28799 const chain = {
28800 messageText,
28801 category: ts__default["default"].DiagnosticCategory.Error,
28802 code: 0,
28803 next: [{
28804 messageText: chainedMessage,
28805 category: ts__default["default"].DiagnosticCategory.Message,
28806 code: 0,
28807 }]
28808 };
28809 return new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, node, chain, relatedInformation);
28810 }
28811 /**
28812 * Gets the diagnostics for a set of provider classes.
28813 * @param providerClasses Classes that should be checked.
28814 * @param providersDeclaration Node that declares the providers array.
28815 * @param registry Registry that keeps track of the registered injectable classes.
28816 */
28817 function getProviderDiagnostics(providerClasses, providersDeclaration, registry) {
28818 const diagnostics = [];
28819 for (const provider of providerClasses) {
28820 if (registry.isInjectable(provider.node)) {
28821 continue;
28822 }
28823 const contextNode = provider.getOriginForDiagnostics(providersDeclaration);
28824 diagnostics.push(makeDiagnostic(ErrorCode.UNDECORATED_PROVIDER, contextNode, `The class '${provider.node.name
28825 .text}' cannot be created via dependency injection, as it does not have an Angular decorator. This will result in an error at runtime.
28826
28827Either add the @Injectable() decorator to '${provider.node.name
28828 .text}', or configure a different provider (such as a provider with 'useFactory').
28829`, [makeRelatedInformation(provider.node, `'${provider.node.name.text}' is declared here.`)]));
28830 }
28831 return diagnostics;
28832 }
28833 function getDirectiveDiagnostics(node, reader, evaluator, reflector, scopeRegistry, kind) {
28834 let diagnostics = [];
28835 const addDiagnostics = (more) => {
28836 if (more === null) {
28837 return;
28838 }
28839 else if (diagnostics === null) {
28840 diagnostics = Array.isArray(more) ? more : [more];
28841 }
28842 else if (Array.isArray(more)) {
28843 diagnostics.push(...more);
28844 }
28845 else {
28846 diagnostics.push(more);
28847 }
28848 };
28849 const duplicateDeclarations = scopeRegistry.getDuplicateDeclarations(node);
28850 if (duplicateDeclarations !== null) {
28851 addDiagnostics(makeDuplicateDeclarationError(node, duplicateDeclarations, kind));
28852 }
28853 addDiagnostics(checkInheritanceOfDirective(node, reader, reflector, evaluator));
28854 return diagnostics;
28855 }
28856 function getUndecoratedClassWithAngularFeaturesDiagnostic(node) {
28857 return makeDiagnostic(ErrorCode.UNDECORATED_CLASS_USING_ANGULAR_FEATURES, node.name, `Class is using Angular features but is not decorated. Please add an explicit ` +
28858 `Angular decorator.`);
28859 }
28860 function checkInheritanceOfDirective(node, reader, reflector, evaluator) {
28861 if (!reflector.isClass(node) || reflector.getConstructorParameters(node) !== null) {
28862 // We should skip nodes that aren't classes. If a constructor exists, then no base class
28863 // definition is required on the runtime side - it's legal to inherit from any class.
28864 return null;
28865 }
28866 // The extends clause is an expression which can be as dynamic as the user wants. Try to
28867 // evaluate it, but fall back on ignoring the clause if it can't be understood. This is a View
28868 // Engine compatibility hack: View Engine ignores 'extends' expressions that it cannot understand.
28869 let baseClass = readBaseClass(node, reflector, evaluator);
28870 while (baseClass !== null) {
28871 if (baseClass === 'dynamic') {
28872 return null;
28873 }
28874 // We can skip the base class if it has metadata.
28875 const baseClassMeta = reader.getDirectiveMetadata(baseClass);
28876 if (baseClassMeta !== null) {
28877 return null;
28878 }
28879 // If the base class has a blank constructor we can skip it since it can't be using DI.
28880 const baseClassConstructorParams = reflector.getConstructorParameters(baseClass.node);
28881 const newParentClass = readBaseClass(baseClass.node, reflector, evaluator);
28882 if (baseClassConstructorParams !== null && baseClassConstructorParams.length > 0) {
28883 // This class has a non-trivial constructor, that's an error!
28884 return getInheritedUndecoratedCtorDiagnostic(node, baseClass, reader);
28885 }
28886 else if (baseClassConstructorParams !== null || newParentClass === null) {
28887 // This class has a trivial constructor, or no constructor + is the
28888 // top of the inheritance chain, so it's okay.
28889 return null;
28890 }
28891 // Go up the chain and continue
28892 baseClass = newParentClass;
28893 }
28894 return null;
28895 }
28896 function getInheritedUndecoratedCtorDiagnostic(node, baseClass, reader) {
28897 const subclassMeta = reader.getDirectiveMetadata(new Reference(node));
28898 const dirOrComp = subclassMeta.isComponent ? 'Component' : 'Directive';
28899 const baseClassName = baseClass.debugName;
28900 return makeDiagnostic(ErrorCode.DIRECTIVE_INHERITS_UNDECORATED_CTOR, node.name, `The ${dirOrComp.toLowerCase()} ${node.name.text} inherits its constructor from ${baseClassName}, ` +
28901 `but the latter does not have an Angular decorator of its own. Dependency injection will not be able to ` +
28902 `resolve the parameters of ${baseClassName}'s constructor. Either add a @Directive decorator ` +
28903 `to ${baseClassName}, or add an explicit constructor to ${node.name.text}.`);
28904 }
28905
28906 /**
28907 * @license
28908 * Copyright Google LLC All Rights Reserved.
28909 *
28910 * Use of this source code is governed by an MIT-style license that can be
28911 * found in the LICENSE file at https://angular.io/license
28912 */
28913 function compileNgFactoryDefField(metadata) {
28914 const res = compileFactoryFunction(metadata);
28915 return { name: 'ɵfac', initializer: res.expression, statements: res.statements, type: res.type };
28916 }
28917 function compileDeclareFactory(metadata) {
28918 const res = compileDeclareFactoryFunction(metadata);
28919 return { name: 'ɵfac', initializer: res.expression, statements: res.statements, type: res.type };
28920 }
28921
28922 /**
28923 * @license
28924 * Copyright Google LLC All Rights Reserved.
28925 *
28926 * Use of this source code is governed by an MIT-style license that can be
28927 * found in the LICENSE file at https://angular.io/license
28928 */
28929 /**
28930 * Given a class declaration, generate a call to `setClassMetadata` with the Angular metadata
28931 * present on the class or its member fields. An ngDevMode guard is used to allow the call to be
28932 * tree-shaken away, as the `setClassMetadata` invocation is only needed for testing purposes.
28933 *
28934 * If no such metadata is present, this function returns `null`. Otherwise, the call is returned
28935 * as a `Statement` for inclusion along with the class.
28936 */
28937 function extractClassMetadata(clazz, reflection, isCore, annotateForClosureCompiler, angularDecoratorTransform = dec => dec) {
28938 if (!reflection.isClass(clazz)) {
28939 return null;
28940 }
28941 const id = reflection.getAdjacentNameOfClass(clazz);
28942 // Reflect over the class decorators. If none are present, or those that are aren't from
28943 // Angular, then return null. Otherwise, turn them into metadata.
28944 const classDecorators = reflection.getDecoratorsOfDeclaration(clazz);
28945 if (classDecorators === null) {
28946 return null;
28947 }
28948 const ngClassDecorators = classDecorators.filter(dec => isAngularDecorator(dec, isCore))
28949 .map(decorator => decoratorToMetadata(angularDecoratorTransform(decorator), annotateForClosureCompiler))
28950 // Since the `setClassMetadata` call is intended to be emitted after the class
28951 // declaration, we have to strip references to the existing identifiers or
28952 // TypeScript might generate invalid code when it emits to JS. In particular
28953 // this can break when emitting a class to ES5 which has a custom decorator
28954 // and is referenced inside of its own metadata (see #39509 for more information).
28955 .map(decorator => removeIdentifierReferences(decorator, id.text));
28956 if (ngClassDecorators.length === 0) {
28957 return null;
28958 }
28959 const metaDecorators = new WrappedNodeExpr(ts__default["default"].createArrayLiteral(ngClassDecorators));
28960 // Convert the constructor parameters to metadata, passing null if none are present.
28961 let metaCtorParameters = null;
28962 const classCtorParameters = reflection.getConstructorParameters(clazz);
28963 if (classCtorParameters !== null) {
28964 const ctorParameters = classCtorParameters.map(param => ctorParameterToMetadata(param, isCore));
28965 metaCtorParameters = new FunctionExpr([], [
28966 new ReturnStatement(new LiteralArrayExpr(ctorParameters)),
28967 ]);
28968 }
28969 // Do the same for property decorators.
28970 let metaPropDecorators = null;
28971 const classMembers = reflection.getMembersOfClass(clazz).filter(member => !member.isStatic && member.decorators !== null && member.decorators.length > 0);
28972 const duplicateDecoratedMemberNames = classMembers.map(member => member.name).filter((name, i, arr) => arr.indexOf(name) < i);
28973 if (duplicateDecoratedMemberNames.length > 0) {
28974 // This should theoretically never happen, because the only way to have duplicate instance
28975 // member names is getter/setter pairs and decorators cannot appear in both a getter and the
28976 // corresponding setter.
28977 throw new Error(`Duplicate decorated properties found on class '${clazz.name.text}': ` +
28978 duplicateDecoratedMemberNames.join(', '));
28979 }
28980 const decoratedMembers = classMembers.map(member => classMemberToMetadata(member.nameNode ?? member.name, member.decorators, isCore));
28981 if (decoratedMembers.length > 0) {
28982 metaPropDecorators = new WrappedNodeExpr(ts__default["default"].createObjectLiteral(decoratedMembers));
28983 }
28984 return {
28985 type: new WrappedNodeExpr(id),
28986 decorators: metaDecorators,
28987 ctorParameters: metaCtorParameters,
28988 propDecorators: metaPropDecorators,
28989 };
28990 }
28991 /**
28992 * Convert a reflected constructor parameter to metadata.
28993 */
28994 function ctorParameterToMetadata(param, isCore) {
28995 // Parameters sometimes have a type that can be referenced. If so, then use it, otherwise
28996 // its type is undefined.
28997 const type = param.typeValueReference.kind !== 2 /* UNAVAILABLE */ ?
28998 valueReferenceToExpression(param.typeValueReference) :
28999 new LiteralExpr(undefined);
29000 const mapEntries = [
29001 { key: 'type', value: type, quoted: false },
29002 ];
29003 // If the parameter has decorators, include the ones from Angular.
29004 if (param.decorators !== null) {
29005 const ngDecorators = param.decorators.filter(dec => isAngularDecorator(dec, isCore))
29006 .map((decorator) => decoratorToMetadata(decorator));
29007 const value = new WrappedNodeExpr(ts__default["default"].createArrayLiteral(ngDecorators));
29008 mapEntries.push({ key: 'decorators', value, quoted: false });
29009 }
29010 return literalMap(mapEntries);
29011 }
29012 /**
29013 * Convert a reflected class member to metadata.
29014 */
29015 function classMemberToMetadata(name, decorators, isCore) {
29016 const ngDecorators = decorators.filter(dec => isAngularDecorator(dec, isCore))
29017 .map((decorator) => decoratorToMetadata(decorator));
29018 const decoratorMeta = ts__default["default"].createArrayLiteral(ngDecorators);
29019 return ts__default["default"].createPropertyAssignment(name, decoratorMeta);
29020 }
29021 /**
29022 * Convert a reflected decorator to metadata.
29023 */
29024 function decoratorToMetadata(decorator, wrapFunctionsInParens) {
29025 if (decorator.identifier === null) {
29026 throw new Error('Illegal state: synthesized decorator cannot be emitted in class metadata.');
29027 }
29028 // Decorators have a type.
29029 const properties = [
29030 ts__default["default"].createPropertyAssignment('type', ts__default["default"].getMutableClone(decorator.identifier)),
29031 ];
29032 // Sometimes they have arguments.
29033 if (decorator.args !== null && decorator.args.length > 0) {
29034 const args = decorator.args.map(arg => {
29035 const expr = ts__default["default"].getMutableClone(arg);
29036 return wrapFunctionsInParens ? wrapFunctionExpressionsInParens(expr) : expr;
29037 });
29038 properties.push(ts__default["default"].createPropertyAssignment('args', ts__default["default"].createArrayLiteral(args)));
29039 }
29040 return ts__default["default"].createObjectLiteral(properties, true);
29041 }
29042 /**
29043 * Whether a given decorator should be treated as an Angular decorator.
29044 *
29045 * Either it's used in @angular/core, or it's imported from there.
29046 */
29047 function isAngularDecorator(decorator, isCore) {
29048 return isCore || (decorator.import !== null && decorator.import.from === '@angular/core');
29049 }
29050 /**
29051 * Recursively recreates all of the `Identifier` descendant nodes with a particular name inside
29052 * of an AST node, thus removing any references to them. Useful if a particular node has to be
29053 * taken from one place any emitted to another one exactly as it has been written.
29054 */
29055 function removeIdentifierReferences(node, name) {
29056 const result = ts__default["default"].transform(node, [context => root => ts__default["default"].visitNode(root, function walk(current) {
29057 return ts__default["default"].isIdentifier(current) && current.text === name ?
29058 ts__default["default"].createIdentifier(current.text) :
29059 ts__default["default"].visitEachChild(current, walk, context);
29060 })]);
29061 return result.transformed[0];
29062 }
29063
29064 /**
29065 * @license
29066 * Copyright Google LLC All Rights Reserved.
29067 *
29068 * Use of this source code is governed by an MIT-style license that can be
29069 * found in the LICENSE file at https://angular.io/license
29070 */
29071 const EMPTY_OBJECT = {};
29072 const FIELD_DECORATORS = [
29073 'Input', 'Output', 'ViewChild', 'ViewChildren', 'ContentChild', 'ContentChildren', 'HostBinding',
29074 'HostListener'
29075 ];
29076 const LIFECYCLE_HOOKS = new Set([
29077 'ngOnChanges', 'ngOnInit', 'ngOnDestroy', 'ngDoCheck', 'ngAfterViewInit', 'ngAfterViewChecked',
29078 'ngAfterContentInit', 'ngAfterContentChecked'
29079 ]);
29080 /**
29081 * Represents an Angular directive. Components are represented by `ComponentSymbol`, which inherits
29082 * from this symbol.
29083 */
29084 class DirectiveSymbol extends SemanticSymbol {
29085 constructor(decl, selector, inputs, outputs, exportAs, typeCheckMeta, typeParameters) {
29086 super(decl);
29087 this.selector = selector;
29088 this.inputs = inputs;
29089 this.outputs = outputs;
29090 this.exportAs = exportAs;
29091 this.typeCheckMeta = typeCheckMeta;
29092 this.typeParameters = typeParameters;
29093 this.baseClass = null;
29094 }
29095 isPublicApiAffected(previousSymbol) {
29096 // Note: since components and directives have exactly the same items contributing to their
29097 // public API, it is okay for a directive to change into a component and vice versa without
29098 // the API being affected.
29099 if (!(previousSymbol instanceof DirectiveSymbol)) {
29100 return true;
29101 }
29102 // Directives and components have a public API of:
29103 // 1. Their selector.
29104 // 2. The binding names of their inputs and outputs; a change in ordering is also considered
29105 // to be a change in public API.
29106 // 3. The list of exportAs names and its ordering.
29107 return this.selector !== previousSymbol.selector ||
29108 !isArrayEqual(this.inputs.propertyNames, previousSymbol.inputs.propertyNames) ||
29109 !isArrayEqual(this.outputs.propertyNames, previousSymbol.outputs.propertyNames) ||
29110 !isArrayEqual(this.exportAs, previousSymbol.exportAs);
29111 }
29112 isTypeCheckApiAffected(previousSymbol) {
29113 // If the public API of the directive has changed, then so has its type-check API.
29114 if (this.isPublicApiAffected(previousSymbol)) {
29115 return true;
29116 }
29117 if (!(previousSymbol instanceof DirectiveSymbol)) {
29118 return true;
29119 }
29120 // The type-check block also depends on the class property names, as writes property bindings
29121 // directly into the backing fields.
29122 if (!isArrayEqual(Array.from(this.inputs), Array.from(previousSymbol.inputs), isInputMappingEqual) ||
29123 !isArrayEqual(Array.from(this.outputs), Array.from(previousSymbol.outputs), isInputMappingEqual)) {
29124 return true;
29125 }
29126 // The type parameters of a directive are emitted into the type constructors in the type-check
29127 // block of a component, so if the type parameters are not considered equal then consider the
29128 // type-check API of this directive to be affected.
29129 if (!areTypeParametersEqual(this.typeParameters, previousSymbol.typeParameters)) {
29130 return true;
29131 }
29132 // The type-check metadata is used during TCB code generation, so any changes should invalidate
29133 // prior type-check files.
29134 if (!isTypeCheckMetaEqual(this.typeCheckMeta, previousSymbol.typeCheckMeta)) {
29135 return true;
29136 }
29137 // Changing the base class of a directive means that its inputs/outputs etc may have changed,
29138 // so the type-check block of components that use this directive needs to be regenerated.
29139 if (!isBaseClassEqual(this.baseClass, previousSymbol.baseClass)) {
29140 return true;
29141 }
29142 return false;
29143 }
29144 }
29145 function isInputMappingEqual(current, previous) {
29146 return current[0] === previous[0] && current[1] === previous[1];
29147 }
29148 function isTypeCheckMetaEqual(current, previous) {
29149 if (current.hasNgTemplateContextGuard !== previous.hasNgTemplateContextGuard) {
29150 return false;
29151 }
29152 if (current.isGeneric !== previous.isGeneric) {
29153 // Note: changes in the number of type parameters is also considered in `areTypeParametersEqual`
29154 // so this check is technically not needed; it is done anyway for completeness in terms of
29155 // whether the `DirectiveTypeCheckMeta` struct itself compares equal or not.
29156 return false;
29157 }
29158 if (!isArrayEqual(current.ngTemplateGuards, previous.ngTemplateGuards, isTemplateGuardEqual)) {
29159 return false;
29160 }
29161 if (!isSetEqual(current.coercedInputFields, previous.coercedInputFields)) {
29162 return false;
29163 }
29164 if (!isSetEqual(current.restrictedInputFields, previous.restrictedInputFields)) {
29165 return false;
29166 }
29167 if (!isSetEqual(current.stringLiteralInputFields, previous.stringLiteralInputFields)) {
29168 return false;
29169 }
29170 if (!isSetEqual(current.undeclaredInputFields, previous.undeclaredInputFields)) {
29171 return false;
29172 }
29173 return true;
29174 }
29175 function isTemplateGuardEqual(current, previous) {
29176 return current.inputName === previous.inputName && current.type === previous.type;
29177 }
29178 function isBaseClassEqual(current, previous) {
29179 if (current === null || previous === null) {
29180 return current === previous;
29181 }
29182 return isSymbolEqual(current, previous);
29183 }
29184 class DirectiveDecoratorHandler {
29185 constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, injectableRegistry, isCore, semanticDepGraphUpdater, annotateForClosureCompiler, compileUndecoratedClassesWithAngularFeatures, perf) {
29186 this.reflector = reflector;
29187 this.evaluator = evaluator;
29188 this.metaRegistry = metaRegistry;
29189 this.scopeRegistry = scopeRegistry;
29190 this.metaReader = metaReader;
29191 this.injectableRegistry = injectableRegistry;
29192 this.isCore = isCore;
29193 this.semanticDepGraphUpdater = semanticDepGraphUpdater;
29194 this.annotateForClosureCompiler = annotateForClosureCompiler;
29195 this.compileUndecoratedClassesWithAngularFeatures = compileUndecoratedClassesWithAngularFeatures;
29196 this.perf = perf;
29197 this.precedence = HandlerPrecedence.PRIMARY;
29198 this.name = DirectiveDecoratorHandler.name;
29199 }
29200 detect(node, decorators) {
29201 // If a class is undecorated but uses Angular features, we detect it as an
29202 // abstract directive. This is an unsupported pattern as of v10, but we want
29203 // to still detect these patterns so that we can report diagnostics, or compile
29204 // them for backwards compatibility in ngcc.
29205 if (!decorators) {
29206 const angularField = this.findClassFieldWithAngularFeatures(node);
29207 return angularField ? { trigger: angularField.node, decorator: null, metadata: null } :
29208 undefined;
29209 }
29210 else {
29211 const decorator = findAngularDecorator(decorators, 'Directive', this.isCore);
29212 return decorator ? { trigger: decorator.node, decorator, metadata: decorator } : undefined;
29213 }
29214 }
29215 analyze(node, decorator, flags = HandlerFlags.NONE) {
29216 // Skip processing of the class declaration if compilation of undecorated classes
29217 // with Angular features is disabled. Previously in ngtsc, such classes have always
29218 // been processed, but we want to enforce a consistent decorator mental model.
29219 // See: https://v9.angular.io/guide/migration-undecorated-classes.
29220 if (this.compileUndecoratedClassesWithAngularFeatures === false && decorator === null) {
29221 return { diagnostics: [getUndecoratedClassWithAngularFeaturesDiagnostic(node)] };
29222 }
29223 this.perf.eventCount(PerfEvent.AnalyzeDirective);
29224 const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.isCore, flags, this.annotateForClosureCompiler);
29225 if (directiveResult === undefined) {
29226 return {};
29227 }
29228 const analysis = directiveResult.metadata;
29229 let providersRequiringFactory = null;
29230 if (directiveResult !== undefined && directiveResult.decorator.has('providers')) {
29231 providersRequiringFactory = resolveProvidersRequiringFactory(directiveResult.decorator.get('providers'), this.reflector, this.evaluator);
29232 }
29233 return {
29234 analysis: {
29235 inputs: directiveResult.inputs,
29236 outputs: directiveResult.outputs,
29237 meta: analysis,
29238 classMetadata: extractClassMetadata(node, this.reflector, this.isCore, this.annotateForClosureCompiler),
29239 baseClass: readBaseClass(node, this.reflector, this.evaluator),
29240 typeCheckMeta: extractDirectiveTypeCheckMeta(node, directiveResult.inputs, this.reflector),
29241 providersRequiringFactory,
29242 isPoisoned: false,
29243 isStructural: directiveResult.isStructural,
29244 }
29245 };
29246 }
29247 symbol(node, analysis) {
29248 const typeParameters = extractSemanticTypeParameters(node);
29249 return new DirectiveSymbol(node, analysis.meta.selector, analysis.inputs, analysis.outputs, analysis.meta.exportAs, analysis.typeCheckMeta, typeParameters);
29250 }
29251 register(node, analysis) {
29252 // Register this directive's information with the `MetadataRegistry`. This ensures that
29253 // the information about the directive is available during the compile() phase.
29254 const ref = new Reference(node);
29255 this.metaRegistry.registerDirectiveMetadata({
29256 type: MetaType.Directive,
29257 ref,
29258 name: node.name.text,
29259 selector: analysis.meta.selector,
29260 exportAs: analysis.meta.exportAs,
29261 inputs: analysis.inputs,
29262 outputs: analysis.outputs,
29263 queries: analysis.meta.queries.map(query => query.propertyName),
29264 isComponent: false,
29265 baseClass: analysis.baseClass,
29266 ...analysis.typeCheckMeta,
29267 isPoisoned: analysis.isPoisoned,
29268 isStructural: analysis.isStructural,
29269 });
29270 this.injectableRegistry.registerInjectable(node);
29271 }
29272 resolve(node, analysis, symbol) {
29273 if (this.semanticDepGraphUpdater !== null && analysis.baseClass instanceof Reference) {
29274 symbol.baseClass = this.semanticDepGraphUpdater.getSymbol(analysis.baseClass.node);
29275 }
29276 const diagnostics = [];
29277 if (analysis.providersRequiringFactory !== null &&
29278 analysis.meta.providers instanceof WrappedNodeExpr) {
29279 const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.meta.providers.node, this.injectableRegistry);
29280 diagnostics.push(...providerDiagnostics);
29281 }
29282 const directiveDiagnostics = getDirectiveDiagnostics(node, this.metaReader, this.evaluator, this.reflector, this.scopeRegistry, 'Directive');
29283 if (directiveDiagnostics !== null) {
29284 diagnostics.push(...directiveDiagnostics);
29285 }
29286 return { diagnostics: diagnostics.length > 0 ? diagnostics : undefined };
29287 }
29288 compileFull(node, analysis, resolution, pool) {
29289 const fac = compileNgFactoryDefField(toFactoryMetadata(analysis.meta, FactoryTarget$1.Directive));
29290 const def = compileDirectiveFromMetadata(analysis.meta, pool, makeBindingParser());
29291 const classMetadata = analysis.classMetadata !== null ?
29292 compileClassMetadata(analysis.classMetadata).toStmt() :
29293 null;
29294 return compileResults(fac, def, classMetadata, 'ɵdir');
29295 }
29296 compilePartial(node, analysis, resolution) {
29297 const fac = compileDeclareFactory(toFactoryMetadata(analysis.meta, FactoryTarget$1.Directive));
29298 const def = compileDeclareDirectiveFromMetadata(analysis.meta);
29299 const classMetadata = analysis.classMetadata !== null ?
29300 compileDeclareClassMetadata(analysis.classMetadata).toStmt() :
29301 null;
29302 return compileResults(fac, def, classMetadata, 'ɵdir');
29303 }
29304 /**
29305 * Checks if a given class uses Angular features and returns the TypeScript node
29306 * that indicated the usage. Classes are considered using Angular features if they
29307 * contain class members that are either decorated with a known Angular decorator,
29308 * or if they correspond to a known Angular lifecycle hook.
29309 */
29310 findClassFieldWithAngularFeatures(node) {
29311 return this.reflector.getMembersOfClass(node).find(member => {
29312 if (!member.isStatic && member.kind === ClassMemberKind.Method &&
29313 LIFECYCLE_HOOKS.has(member.name)) {
29314 return true;
29315 }
29316 if (member.decorators) {
29317 return member.decorators.some(decorator => FIELD_DECORATORS.some(decoratorName => isAngularDecorator$1(decorator, decoratorName, this.isCore)));
29318 }
29319 return false;
29320 });
29321 }
29322 }
29323 /**
29324 * Helper function to extract metadata from a `Directive` or `Component`. `Directive`s without a
29325 * selector are allowed to be used for abstract base classes. These abstract directives should not
29326 * appear in the declarations of an `NgModule` and additional verification is done when processing
29327 * the module.
29328 */
29329 function extractDirectiveMetadata(clazz, decorator, reflector, evaluator, isCore, flags, annotateForClosureCompiler, defaultSelector = null) {
29330 let directive;
29331 if (decorator === null || decorator.args === null || decorator.args.length === 0) {
29332 directive = new Map();
29333 }
29334 else if (decorator.args.length !== 1) {
29335 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @${decorator.name} decorator`);
29336 }
29337 else {
29338 const meta = unwrapExpression(decorator.args[0]);
29339 if (!ts__default["default"].isObjectLiteralExpression(meta)) {
29340 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, `@${decorator.name} argument must be an object literal`);
29341 }
29342 directive = reflectObjectLiteral(meta);
29343 }
29344 if (directive.has('jit')) {
29345 // The only allowed value is true, so there's no need to expand further.
29346 return undefined;
29347 }
29348 const members = reflector.getMembersOfClass(clazz);
29349 // Precompute a list of ts.ClassElements that have decorators. This includes things like @Input,
29350 // @Output, @HostBinding, etc.
29351 const decoratedElements = members.filter(member => !member.isStatic && member.decorators !== null);
29352 const coreModule = isCore ? undefined : '@angular/core';
29353 // Construct the map of inputs both from the @Directive/@Component
29354 // decorator, and the decorated
29355 // fields.
29356 const inputsFromMeta = parseFieldToPropertyMapping(directive, 'inputs', evaluator);
29357 const inputsFromFields = parseDecoratedFields(filterToMembersWithDecorator(decoratedElements, 'Input', coreModule), evaluator, resolveInput);
29358 // And outputs.
29359 const outputsFromMeta = parseFieldToPropertyMapping(directive, 'outputs', evaluator);
29360 const outputsFromFields = parseDecoratedFields(filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), evaluator, resolveOutput);
29361 // Construct the list of queries.
29362 const contentChildFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ContentChild', coreModule), reflector, evaluator);
29363 const contentChildrenFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ContentChildren', coreModule), reflector, evaluator);
29364 const queries = [...contentChildFromFields, ...contentChildrenFromFields];
29365 // Construct the list of view queries.
29366 const viewChildFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ViewChild', coreModule), reflector, evaluator);
29367 const viewChildrenFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ViewChildren', coreModule), reflector, evaluator);
29368 const viewQueries = [...viewChildFromFields, ...viewChildrenFromFields];
29369 if (directive.has('queries')) {
29370 const queriesFromDecorator = extractQueriesFromDecorator(directive.get('queries'), reflector, evaluator, isCore);
29371 queries.push(...queriesFromDecorator.content);
29372 viewQueries.push(...queriesFromDecorator.view);
29373 }
29374 // Parse the selector.
29375 let selector = defaultSelector;
29376 if (directive.has('selector')) {
29377 const expr = directive.get('selector');
29378 const resolved = evaluator.evaluate(expr);
29379 if (typeof resolved !== 'string') {
29380 throw createValueHasWrongTypeError(expr, resolved, `selector must be a string`);
29381 }
29382 // use default selector in case selector is an empty string
29383 selector = resolved === '' ? defaultSelector : resolved;
29384 if (!selector) {
29385 throw new FatalDiagnosticError(ErrorCode.DIRECTIVE_MISSING_SELECTOR, expr, `Directive ${clazz.name.text} has no selector, please add it!`);
29386 }
29387 }
29388 const host = extractHostBindings(decoratedElements, evaluator, coreModule, directive);
29389 const providers = directive.has('providers') ?
29390 new WrappedNodeExpr(annotateForClosureCompiler ?
29391 wrapFunctionExpressionsInParens(directive.get('providers')) :
29392 directive.get('providers')) :
29393 null;
29394 // Determine if `ngOnChanges` is a lifecycle hook defined on the component.
29395 const usesOnChanges = members.some(member => !member.isStatic && member.kind === ClassMemberKind.Method &&
29396 member.name === 'ngOnChanges');
29397 // Parse exportAs.
29398 let exportAs = null;
29399 if (directive.has('exportAs')) {
29400 const expr = directive.get('exportAs');
29401 const resolved = evaluator.evaluate(expr);
29402 if (typeof resolved !== 'string') {
29403 throw createValueHasWrongTypeError(expr, resolved, `exportAs must be a string`);
29404 }
29405 exportAs = resolved.split(',').map(part => part.trim());
29406 }
29407 const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore);
29408 // Non-abstract directives (those with a selector) require valid constructor dependencies, whereas
29409 // abstract directives are allowed to have invalid dependencies, given that a subclass may call
29410 // the constructor explicitly.
29411 const ctorDeps = selector !== null ? validateConstructorDependencies(clazz, rawCtorDeps) :
29412 unwrapConstructorDependencies(rawCtorDeps);
29413 // Structural directives must have a `TemplateRef` dependency.
29414 const isStructural = ctorDeps !== null && ctorDeps !== 'invalid' &&
29415 ctorDeps.some(dep => (dep.token instanceof ExternalExpr) &&
29416 dep.token.value.moduleName === '@angular/core' &&
29417 dep.token.value.name === 'TemplateRef');
29418 // Detect if the component inherits from another class
29419 const usesInheritance = reflector.hasBaseClass(clazz);
29420 const type = wrapTypeReference(reflector, clazz);
29421 const internalType = new WrappedNodeExpr(reflector.getInternalNameOfClass(clazz));
29422 const inputs = ClassPropertyMapping.fromMappedObject({ ...inputsFromMeta, ...inputsFromFields });
29423 const outputs = ClassPropertyMapping.fromMappedObject({ ...outputsFromMeta, ...outputsFromFields });
29424 const metadata = {
29425 name: clazz.name.text,
29426 deps: ctorDeps,
29427 host,
29428 lifecycle: {
29429 usesOnChanges,
29430 },
29431 inputs: inputs.toJointMappedObject(),
29432 outputs: outputs.toDirectMappedObject(),
29433 queries,
29434 viewQueries,
29435 selector,
29436 fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE),
29437 type,
29438 internalType,
29439 typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
29440 typeSourceSpan: createSourceSpan(clazz.name),
29441 usesInheritance,
29442 exportAs,
29443 providers
29444 };
29445 return {
29446 decorator: directive,
29447 metadata,
29448 inputs,
29449 outputs,
29450 isStructural,
29451 };
29452 }
29453 function extractQueryMetadata(exprNode, name, args, propertyName, reflector, evaluator) {
29454 if (args.length === 0) {
29455 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, exprNode, `@${name} must have arguments`);
29456 }
29457 const first = name === 'ViewChild' || name === 'ContentChild';
29458 const forwardReferenceTarget = tryUnwrapForwardRef(args[0], reflector);
29459 const node = forwardReferenceTarget ?? args[0];
29460 const arg = evaluator.evaluate(node);
29461 /** Whether or not this query should collect only static results (see view/api.ts) */
29462 let isStatic = false;
29463 // Extract the predicate
29464 let predicate = null;
29465 if (arg instanceof Reference || arg instanceof DynamicValue) {
29466 // References and predicates that could not be evaluated statically are emitted as is.
29467 predicate = createMayBeForwardRefExpression(new WrappedNodeExpr(node), forwardReferenceTarget !== null ? 2 /* Unwrapped */ : 0 /* None */);
29468 }
29469 else if (typeof arg === 'string') {
29470 predicate = [arg];
29471 }
29472 else if (isStringArrayOrDie(arg, `@${name} predicate`, node)) {
29473 predicate = arg;
29474 }
29475 else {
29476 throw createValueHasWrongTypeError(node, arg, `@${name} predicate cannot be interpreted`);
29477 }
29478 // Extract the read and descendants options.
29479 let read = null;
29480 // The default value for descendants is true for every decorator except @ContentChildren.
29481 let descendants = name !== 'ContentChildren';
29482 let emitDistinctChangesOnly = emitDistinctChangesOnlyDefaultValue;
29483 if (args.length === 2) {
29484 const optionsExpr = unwrapExpression(args[1]);
29485 if (!ts__default["default"].isObjectLiteralExpression(optionsExpr)) {
29486 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, optionsExpr, `@${name} options must be an object literal`);
29487 }
29488 const options = reflectObjectLiteral(optionsExpr);
29489 if (options.has('read')) {
29490 read = new WrappedNodeExpr(options.get('read'));
29491 }
29492 if (options.has('descendants')) {
29493 const descendantsExpr = options.get('descendants');
29494 const descendantsValue = evaluator.evaluate(descendantsExpr);
29495 if (typeof descendantsValue !== 'boolean') {
29496 throw createValueHasWrongTypeError(descendantsExpr, descendantsValue, `@${name} options.descendants must be a boolean`);
29497 }
29498 descendants = descendantsValue;
29499 }
29500 if (options.has('emitDistinctChangesOnly')) {
29501 const emitDistinctChangesOnlyExpr = options.get('emitDistinctChangesOnly');
29502 const emitDistinctChangesOnlyValue = evaluator.evaluate(emitDistinctChangesOnlyExpr);
29503 if (typeof emitDistinctChangesOnlyValue !== 'boolean') {
29504 throw createValueHasWrongTypeError(emitDistinctChangesOnlyExpr, emitDistinctChangesOnlyValue, `@${name} options.emitDistinctChangesOnly must be a boolean`);
29505 }
29506 emitDistinctChangesOnly = emitDistinctChangesOnlyValue;
29507 }
29508 if (options.has('static')) {
29509 const staticValue = evaluator.evaluate(options.get('static'));
29510 if (typeof staticValue !== 'boolean') {
29511 throw createValueHasWrongTypeError(node, staticValue, `@${name} options.static must be a boolean`);
29512 }
29513 isStatic = staticValue;
29514 }
29515 }
29516 else if (args.length > 2) {
29517 // Too many arguments.
29518 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, node, `@${name} has too many arguments`);
29519 }
29520 return {
29521 propertyName,
29522 predicate,
29523 first,
29524 descendants,
29525 read,
29526 static: isStatic,
29527 emitDistinctChangesOnly,
29528 };
29529 }
29530 function extractQueriesFromDecorator(queryData, reflector, evaluator, isCore) {
29531 const content = [], view = [];
29532 if (!ts__default["default"].isObjectLiteralExpression(queryData)) {
29533 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator queries metadata must be an object literal');
29534 }
29535 reflectObjectLiteral(queryData).forEach((queryExpr, propertyName) => {
29536 queryExpr = unwrapExpression(queryExpr);
29537 if (!ts__default["default"].isNewExpression(queryExpr)) {
29538 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator query metadata must be an instance of a query type');
29539 }
29540 const queryType = ts__default["default"].isPropertyAccessExpression(queryExpr.expression) ?
29541 queryExpr.expression.name :
29542 queryExpr.expression;
29543 if (!ts__default["default"].isIdentifier(queryType)) {
29544 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator query metadata must be an instance of a query type');
29545 }
29546 const type = reflector.getImportOfIdentifier(queryType);
29547 if (type === null || (!isCore && type.from !== '@angular/core') ||
29548 !QUERY_TYPES.has(type.name)) {
29549 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator query metadata must be an instance of a query type');
29550 }
29551 const query = extractQueryMetadata(queryExpr, type.name, queryExpr.arguments || [], propertyName, reflector, evaluator);
29552 if (type.name.startsWith('Content')) {
29553 content.push(query);
29554 }
29555 else {
29556 view.push(query);
29557 }
29558 });
29559 return { content, view };
29560 }
29561 function isStringArrayOrDie(value, name, node) {
29562 if (!Array.isArray(value)) {
29563 return false;
29564 }
29565 for (let i = 0; i < value.length; i++) {
29566 if (typeof value[i] !== 'string') {
29567 throw createValueHasWrongTypeError(node, value[i], `Failed to resolve ${name} at position ${i} to a string`);
29568 }
29569 }
29570 return true;
29571 }
29572 function parseFieldArrayValue(directive, field, evaluator) {
29573 if (!directive.has(field)) {
29574 return null;
29575 }
29576 // Resolve the field of interest from the directive metadata to a string[].
29577 const expression = directive.get(field);
29578 const value = evaluator.evaluate(expression);
29579 if (!isStringArrayOrDie(value, field, expression)) {
29580 throw createValueHasWrongTypeError(expression, value, `Failed to resolve @Directive.${field} to a string array`);
29581 }
29582 return value;
29583 }
29584 /**
29585 * Interpret property mapping fields on the decorator (e.g. inputs or outputs) and return the
29586 * correctly shaped metadata object.
29587 */
29588 function parseFieldToPropertyMapping(directive, field, evaluator) {
29589 const metaValues = parseFieldArrayValue(directive, field, evaluator);
29590 if (!metaValues) {
29591 return EMPTY_OBJECT;
29592 }
29593 return metaValues.reduce((results, value) => {
29594 // Either the value is 'field' or 'field: property'. In the first case, `property` will
29595 // be undefined, in which case the field name should also be used as the property name.
29596 const [field, property] = value.split(':', 2).map(str => str.trim());
29597 results[field] = property || field;
29598 return results;
29599 }, {});
29600 }
29601 /**
29602 * Parse property decorators (e.g. `Input` or `Output`) and return the correctly shaped metadata
29603 * object.
29604 */
29605 function parseDecoratedFields(fields, evaluator, mapValueResolver) {
29606 return fields.reduce((results, field) => {
29607 const fieldName = field.member.name;
29608 field.decorators.forEach(decorator => {
29609 // The decorator either doesn't have an argument (@Input()) in which case the property
29610 // name is used, or it has one argument (@Output('named')).
29611 if (decorator.args == null || decorator.args.length === 0) {
29612 results[fieldName] = fieldName;
29613 }
29614 else if (decorator.args.length === 1) {
29615 const property = evaluator.evaluate(decorator.args[0]);
29616 if (typeof property !== 'string') {
29617 throw createValueHasWrongTypeError(Decorator.nodeForError(decorator), property, `@${decorator.name} decorator argument must resolve to a string`);
29618 }
29619 results[fieldName] = mapValueResolver(property, fieldName);
29620 }
29621 else {
29622 // Too many arguments.
29623 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `@${decorator.name} can have at most one argument, got ${decorator.args.length} argument(s)`);
29624 }
29625 });
29626 return results;
29627 }, {});
29628 }
29629 function resolveInput(publicName, internalName) {
29630 return [publicName, internalName];
29631 }
29632 function resolveOutput(publicName, internalName) {
29633 return publicName;
29634 }
29635 function queriesFromFields(fields, reflector, evaluator) {
29636 return fields.map(({ member, decorators }) => {
29637 const decorator = decorators[0];
29638 const node = member.node || Decorator.nodeForError(decorator);
29639 // Throw in case of `@Input() @ContentChild('foo') foo: any`, which is not supported in Ivy
29640 if (member.decorators.some(v => v.name === 'Input')) {
29641 throw new FatalDiagnosticError(ErrorCode.DECORATOR_COLLISION, node, 'Cannot combine @Input decorators with query decorators');
29642 }
29643 if (decorators.length !== 1) {
29644 throw new FatalDiagnosticError(ErrorCode.DECORATOR_COLLISION, node, 'Cannot have multiple query decorators on the same class member');
29645 }
29646 else if (!isPropertyTypeMember(member)) {
29647 throw new FatalDiagnosticError(ErrorCode.DECORATOR_UNEXPECTED, node, 'Query decorator must go on a property-type member');
29648 }
29649 return extractQueryMetadata(node, decorator.name, decorator.args || [], member.name, reflector, evaluator);
29650 });
29651 }
29652 function isPropertyTypeMember(member) {
29653 return member.kind === ClassMemberKind.Getter || member.kind === ClassMemberKind.Setter ||
29654 member.kind === ClassMemberKind.Property;
29655 }
29656 function evaluateHostExpressionBindings(hostExpr, evaluator) {
29657 const hostMetaMap = evaluator.evaluate(hostExpr);
29658 if (!(hostMetaMap instanceof Map)) {
29659 throw createValueHasWrongTypeError(hostExpr, hostMetaMap, `Decorator host metadata must be an object`);
29660 }
29661 const hostMetadata = {};
29662 hostMetaMap.forEach((value, key) => {
29663 // Resolve Enum references to their declared value.
29664 if (value instanceof EnumValue) {
29665 value = value.resolved;
29666 }
29667 if (typeof key !== 'string') {
29668 throw createValueHasWrongTypeError(hostExpr, key, `Decorator host metadata must be a string -> string object, but found unparseable key`);
29669 }
29670 if (typeof value == 'string') {
29671 hostMetadata[key] = value;
29672 }
29673 else if (value instanceof DynamicValue) {
29674 hostMetadata[key] = new WrappedNodeExpr(value.node);
29675 }
29676 else {
29677 throw createValueHasWrongTypeError(hostExpr, value, `Decorator host metadata must be a string -> string object, but found unparseable value`);
29678 }
29679 });
29680 const bindings = parseHostBindings(hostMetadata);
29681 const errors = verifyHostBindings(bindings, createSourceSpan(hostExpr));
29682 if (errors.length > 0) {
29683 throw new FatalDiagnosticError(
29684 // TODO: provide more granular diagnostic and output specific host expression that
29685 // triggered an error instead of the whole host object.
29686 ErrorCode.HOST_BINDING_PARSE_ERROR, hostExpr, errors.map((error) => error.msg).join('\n'));
29687 }
29688 return bindings;
29689 }
29690 function extractHostBindings(members, evaluator, coreModule, metadata) {
29691 let bindings;
29692 if (metadata && metadata.has('host')) {
29693 bindings = evaluateHostExpressionBindings(metadata.get('host'), evaluator);
29694 }
29695 else {
29696 bindings = parseHostBindings({});
29697 }
29698 filterToMembersWithDecorator(members, 'HostBinding', coreModule)
29699 .forEach(({ member, decorators }) => {
29700 decorators.forEach(decorator => {
29701 let hostPropertyName = member.name;
29702 if (decorator.args !== null && decorator.args.length > 0) {
29703 if (decorator.args.length !== 1) {
29704 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `@HostBinding can have at most one argument, got ${decorator.args.length} argument(s)`);
29705 }
29706 const resolved = evaluator.evaluate(decorator.args[0]);
29707 if (typeof resolved !== 'string') {
29708 throw createValueHasWrongTypeError(Decorator.nodeForError(decorator), resolved, `@HostBinding's argument must be a string`);
29709 }
29710 hostPropertyName = resolved;
29711 }
29712 // Since this is a decorator, we know that the value is a class member. Always access it
29713 // through `this` so that further down the line it can't be confused for a literal value
29714 // (e.g. if there's a property called `true`). There is no size penalty, because all
29715 // values (except literals) are converted to `ctx.propName` eventually.
29716 bindings.properties[hostPropertyName] = getSafePropertyAccessString('this', member.name);
29717 });
29718 });
29719 filterToMembersWithDecorator(members, 'HostListener', coreModule)
29720 .forEach(({ member, decorators }) => {
29721 decorators.forEach(decorator => {
29722 let eventName = member.name;
29723 let args = [];
29724 if (decorator.args !== null && decorator.args.length > 0) {
29725 if (decorator.args.length > 2) {
29726 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, decorator.args[2], `@HostListener can have at most two arguments`);
29727 }
29728 const resolved = evaluator.evaluate(decorator.args[0]);
29729 if (typeof resolved !== 'string') {
29730 throw createValueHasWrongTypeError(decorator.args[0], resolved, `@HostListener's event name argument must be a string`);
29731 }
29732 eventName = resolved;
29733 if (decorator.args.length === 2) {
29734 const expression = decorator.args[1];
29735 const resolvedArgs = evaluator.evaluate(decorator.args[1]);
29736 if (!isStringArrayOrDie(resolvedArgs, '@HostListener.args', expression)) {
29737 throw createValueHasWrongTypeError(decorator.args[1], resolvedArgs, `@HostListener's second argument must be a string array`);
29738 }
29739 args = resolvedArgs;
29740 }
29741 }
29742 bindings.listeners[eventName] = `${member.name}(${args.join(',')})`;
29743 });
29744 });
29745 return bindings;
29746 }
29747 const QUERY_TYPES = new Set([
29748 'ContentChild',
29749 'ContentChildren',
29750 'ViewChild',
29751 'ViewChildren',
29752 ]);
29753
29754 /**
29755 * @license
29756 * Copyright Google LLC All Rights Reserved.
29757 *
29758 * Use of this source code is governed by an MIT-style license that can be
29759 * found in the LICENSE file at https://angular.io/license
29760 */
29761 /**
29762 * Represents an Angular NgModule.
29763 */
29764 class NgModuleSymbol extends SemanticSymbol {
29765 constructor() {
29766 super(...arguments);
29767 this.remotelyScopedComponents = [];
29768 }
29769 isPublicApiAffected(previousSymbol) {
29770 if (!(previousSymbol instanceof NgModuleSymbol)) {
29771 return true;
29772 }
29773 // NgModules don't have a public API that could affect emit of Angular decorated classes.
29774 return false;
29775 }
29776 isEmitAffected(previousSymbol) {
29777 if (!(previousSymbol instanceof NgModuleSymbol)) {
29778 return true;
29779 }
29780 // compare our remotelyScopedComponents to the previous symbol
29781 if (previousSymbol.remotelyScopedComponents.length !== this.remotelyScopedComponents.length) {
29782 return true;
29783 }
29784 for (const currEntry of this.remotelyScopedComponents) {
29785 const prevEntry = previousSymbol.remotelyScopedComponents.find(prevEntry => {
29786 return isSymbolEqual(prevEntry.component, currEntry.component);
29787 });
29788 if (prevEntry === undefined) {
29789 // No previous entry was found, which means that this component became remotely scoped and
29790 // hence this NgModule needs to be re-emitted.
29791 return true;
29792 }
29793 if (!isArrayEqual(currEntry.usedDirectives, prevEntry.usedDirectives, isReferenceEqual)) {
29794 // The list of used directives or their order has changed. Since this NgModule emits
29795 // references to the list of used directives, it should be re-emitted to update this list.
29796 // Note: the NgModule does not have to be re-emitted when any of the directives has had
29797 // their public API changed, as the NgModule only emits a reference to the symbol by its
29798 // name. Therefore, testing for symbol equality is sufficient.
29799 return true;
29800 }
29801 if (!isArrayEqual(currEntry.usedPipes, prevEntry.usedPipes, isReferenceEqual)) {
29802 return true;
29803 }
29804 }
29805 return false;
29806 }
29807 isTypeCheckApiAffected(previousSymbol) {
29808 if (!(previousSymbol instanceof NgModuleSymbol)) {
29809 return true;
29810 }
29811 return false;
29812 }
29813 addRemotelyScopedComponent(component, usedDirectives, usedPipes) {
29814 this.remotelyScopedComponents.push({ component, usedDirectives, usedPipes });
29815 }
29816 }
29817 /**
29818 * Compiles @NgModule annotations to ngModuleDef fields.
29819 */
29820 class NgModuleDecoratorHandler {
29821 constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, refEmitter, factoryTracker, annotateForClosureCompiler, injectableRegistry, perf) {
29822 this.reflector = reflector;
29823 this.evaluator = evaluator;
29824 this.metaReader = metaReader;
29825 this.metaRegistry = metaRegistry;
29826 this.scopeRegistry = scopeRegistry;
29827 this.referencesRegistry = referencesRegistry;
29828 this.isCore = isCore;
29829 this.refEmitter = refEmitter;
29830 this.factoryTracker = factoryTracker;
29831 this.annotateForClosureCompiler = annotateForClosureCompiler;
29832 this.injectableRegistry = injectableRegistry;
29833 this.perf = perf;
29834 this.precedence = HandlerPrecedence.PRIMARY;
29835 this.name = NgModuleDecoratorHandler.name;
29836 }
29837 detect(node, decorators) {
29838 if (!decorators) {
29839 return undefined;
29840 }
29841 const decorator = findAngularDecorator(decorators, 'NgModule', this.isCore);
29842 if (decorator !== undefined) {
29843 return {
29844 trigger: decorator.node,
29845 decorator: decorator,
29846 metadata: decorator,
29847 };
29848 }
29849 else {
29850 return undefined;
29851 }
29852 }
29853 analyze(node, decorator) {
29854 this.perf.eventCount(PerfEvent.AnalyzeNgModule);
29855 const name = node.name.text;
29856 if (decorator.args === null || decorator.args.length > 1) {
29857 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @NgModule decorator`);
29858 }
29859 // @NgModule can be invoked without arguments. In case it is, pretend as if a blank object
29860 // literal was specified. This simplifies the code below.
29861 const meta = decorator.args.length === 1 ? unwrapExpression(decorator.args[0]) :
29862 ts__default["default"].createObjectLiteral([]);
29863 if (!ts__default["default"].isObjectLiteralExpression(meta)) {
29864 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@NgModule argument must be an object literal');
29865 }
29866 const ngModule = reflectObjectLiteral(meta);
29867 if (ngModule.has('jit')) {
29868 // The only allowed value is true, so there's no need to expand further.
29869 return {};
29870 }
29871 const moduleResolvers = combineResolvers([
29872 ref => this._extractModuleFromModuleWithProvidersFn(ref.node),
29873 forwardRefResolver,
29874 ]);
29875 const diagnostics = [];
29876 // Extract the module declarations, imports, and exports.
29877 let declarationRefs = [];
29878 let rawDeclarations = null;
29879 if (ngModule.has('declarations')) {
29880 rawDeclarations = ngModule.get('declarations');
29881 const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver);
29882 declarationRefs =
29883 this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations');
29884 // Look through the declarations to make sure they're all a part of the current compilation.
29885 for (const ref of declarationRefs) {
29886 if (ref.node.getSourceFile().isDeclarationFile) {
29887 const errorNode = ref.getOriginForDiagnostics(rawDeclarations);
29888 diagnostics.push(makeDiagnostic(ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, `Cannot declare '${ref.node.name
29889 .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.`)]));
29890 }
29891 }
29892 }
29893 if (diagnostics.length > 0) {
29894 return { diagnostics };
29895 }
29896 let importRefs = [];
29897 if (ngModule.has('imports')) {
29898 const rawImports = ngModule.get('imports');
29899 const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers);
29900 importRefs = this.resolveTypeList(rawImports, importsMeta, name, 'imports');
29901 }
29902 let exportRefs = [];
29903 if (ngModule.has('exports')) {
29904 const rawExports = ngModule.get('exports');
29905 const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers);
29906 exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports');
29907 this.referencesRegistry.add(node, ...exportRefs);
29908 }
29909 let bootstrapRefs = [];
29910 if (ngModule.has('bootstrap')) {
29911 const expr = ngModule.get('bootstrap');
29912 const bootstrapMeta = this.evaluator.evaluate(expr, forwardRefResolver);
29913 bootstrapRefs = this.resolveTypeList(expr, bootstrapMeta, name, 'bootstrap');
29914 }
29915 const schemas = [];
29916 if (ngModule.has('schemas')) {
29917 const rawExpr = ngModule.get('schemas');
29918 const result = this.evaluator.evaluate(rawExpr);
29919 if (!Array.isArray(result)) {
29920 throw createValueHasWrongTypeError(rawExpr, result, `NgModule.schemas must be an array`);
29921 }
29922 for (const schemaRef of result) {
29923 if (!(schemaRef instanceof Reference)) {
29924 throw createValueHasWrongTypeError(rawExpr, result, 'NgModule.schemas must be an array of schemas');
29925 }
29926 const id = schemaRef.getIdentityIn(schemaRef.node.getSourceFile());
29927 if (id === null || schemaRef.ownedByModuleGuess !== '@angular/core') {
29928 throw createValueHasWrongTypeError(rawExpr, result, 'NgModule.schemas must be an array of schemas');
29929 }
29930 // Since `id` is the `ts.Identifer` within the schema ref's declaration file, it's safe to
29931 // use `id.text` here to figure out which schema is in use. Even if the actual reference was
29932 // renamed when the user imported it, these names will match.
29933 switch (id.text) {
29934 case 'CUSTOM_ELEMENTS_SCHEMA':
29935 schemas.push(CUSTOM_ELEMENTS_SCHEMA);
29936 break;
29937 case 'NO_ERRORS_SCHEMA':
29938 schemas.push(NO_ERRORS_SCHEMA);
29939 break;
29940 default:
29941 throw createValueHasWrongTypeError(rawExpr, schemaRef, `'${schemaRef.debugName}' is not a valid NgModule schema`);
29942 }
29943 }
29944 }
29945 const id = ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id')) : null;
29946 const valueContext = node.getSourceFile();
29947 let typeContext = valueContext;
29948 const typeNode = this.reflector.getDtsDeclaration(node);
29949 if (typeNode !== null) {
29950 typeContext = typeNode.getSourceFile();
29951 }
29952 const bootstrap = bootstrapRefs.map(bootstrap => this._toR3Reference(bootstrap, valueContext, typeContext));
29953 const declarations = declarationRefs.map(decl => this._toR3Reference(decl, valueContext, typeContext));
29954 const imports = importRefs.map(imp => this._toR3Reference(imp, valueContext, typeContext));
29955 const exports = exportRefs.map(exp => this._toR3Reference(exp, valueContext, typeContext));
29956 const isForwardReference = (ref) => isExpressionForwardReference(ref.value, node.name, valueContext);
29957 const containsForwardDecls = bootstrap.some(isForwardReference) ||
29958 declarations.some(isForwardReference) || imports.some(isForwardReference) ||
29959 exports.some(isForwardReference);
29960 const type = wrapTypeReference(this.reflector, node);
29961 const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(node));
29962 const adjacentType = new WrappedNodeExpr(this.reflector.getAdjacentNameOfClass(node));
29963 const ngModuleMetadata = {
29964 type,
29965 internalType,
29966 adjacentType,
29967 bootstrap,
29968 declarations,
29969 exports,
29970 imports,
29971 containsForwardDecls,
29972 id,
29973 emitInline: false,
29974 // TODO: to be implemented as a part of FW-1004.
29975 schemas: [],
29976 };
29977 const rawProviders = ngModule.has('providers') ? ngModule.get('providers') : null;
29978 const wrapperProviders = rawProviders !== null ?
29979 new WrappedNodeExpr(this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) :
29980 rawProviders) :
29981 null;
29982 // At this point, only add the module's imports as the injectors' imports. Any exported modules
29983 // are added during `resolve`, as we need scope information to be able to filter out directives
29984 // and pipes from the module exports.
29985 const injectorImports = [];
29986 if (ngModule.has('imports')) {
29987 injectorImports.push(new WrappedNodeExpr(ngModule.get('imports')));
29988 }
29989 const injectorMetadata = {
29990 name,
29991 type,
29992 internalType,
29993 providers: wrapperProviders,
29994 imports: injectorImports,
29995 };
29996 const factoryMetadata = {
29997 name,
29998 type,
29999 internalType,
30000 typeArgumentCount: 0,
30001 deps: getValidConstructorDependencies(node, this.reflector, this.isCore),
30002 target: FactoryTarget$1.NgModule,
30003 };
30004 return {
30005 analysis: {
30006 id,
30007 schemas,
30008 mod: ngModuleMetadata,
30009 inj: injectorMetadata,
30010 fac: factoryMetadata,
30011 declarations: declarationRefs,
30012 rawDeclarations,
30013 imports: importRefs,
30014 exports: exportRefs,
30015 providers: rawProviders,
30016 providersRequiringFactory: rawProviders ?
30017 resolveProvidersRequiringFactory(rawProviders, this.reflector, this.evaluator) :
30018 null,
30019 classMetadata: extractClassMetadata(node, this.reflector, this.isCore, this.annotateForClosureCompiler),
30020 factorySymbolName: node.name.text,
30021 },
30022 };
30023 }
30024 symbol(node) {
30025 return new NgModuleSymbol(node);
30026 }
30027 register(node, analysis) {
30028 // Register this module's information with the LocalModuleScopeRegistry. This ensures that
30029 // during the compile() phase, the module's metadata is available for selector scope
30030 // computation.
30031 this.metaRegistry.registerNgModuleMetadata({
30032 ref: new Reference(node),
30033 schemas: analysis.schemas,
30034 declarations: analysis.declarations,
30035 imports: analysis.imports,
30036 exports: analysis.exports,
30037 rawDeclarations: analysis.rawDeclarations,
30038 });
30039 if (this.factoryTracker !== null) {
30040 this.factoryTracker.track(node.getSourceFile(), {
30041 name: analysis.factorySymbolName,
30042 hasId: analysis.id !== null,
30043 });
30044 }
30045 this.injectableRegistry.registerInjectable(node);
30046 }
30047 resolve(node, analysis) {
30048 const scope = this.scopeRegistry.getScopeOfModule(node);
30049 const diagnostics = [];
30050 const scopeDiagnostics = this.scopeRegistry.getDiagnosticsOfModule(node);
30051 if (scopeDiagnostics !== null) {
30052 diagnostics.push(...scopeDiagnostics);
30053 }
30054 if (analysis.providersRequiringFactory !== null) {
30055 const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.providers, this.injectableRegistry);
30056 diagnostics.push(...providerDiagnostics);
30057 }
30058 const data = {
30059 injectorImports: [],
30060 };
30061 if (scope !== null && !scope.compilation.isPoisoned) {
30062 // Using the scope information, extend the injector's imports using the modules that are
30063 // specified as module exports.
30064 const context = getSourceFile(node);
30065 for (const exportRef of analysis.exports) {
30066 if (isNgModule(exportRef.node, scope.compilation)) {
30067 data.injectorImports.push(this.refEmitter.emit(exportRef, context).expression);
30068 }
30069 }
30070 for (const decl of analysis.declarations) {
30071 const metadata = this.metaReader.getDirectiveMetadata(decl);
30072 if (metadata !== null && metadata.selector === null) {
30073 throw new FatalDiagnosticError(ErrorCode.DIRECTIVE_MISSING_SELECTOR, decl.node, `Directive ${decl.node.name.text} has no selector, please add it!`);
30074 }
30075 }
30076 }
30077 if (diagnostics.length > 0) {
30078 return { diagnostics };
30079 }
30080 if (scope === null || scope.compilation.isPoisoned || scope.exported.isPoisoned ||
30081 scope.reexports === null) {
30082 return { data };
30083 }
30084 else {
30085 return {
30086 data,
30087 reexports: scope.reexports,
30088 };
30089 }
30090 }
30091 compileFull(node, { inj, mod, fac, classMetadata, declarations }, { injectorImports }) {
30092 const factoryFn = compileNgFactoryDefField(fac);
30093 const ngInjectorDef = compileInjector(this.mergeInjectorImports(inj, injectorImports));
30094 const ngModuleDef = compileNgModule(mod);
30095 const statements = ngModuleDef.statements;
30096 const metadata = classMetadata !== null ? compileClassMetadata(classMetadata) : null;
30097 this.insertMetadataStatement(statements, metadata);
30098 this.appendRemoteScopingStatements(statements, node, declarations);
30099 return this.compileNgModule(factoryFn, ngInjectorDef, ngModuleDef);
30100 }
30101 compilePartial(node, { inj, fac, mod, classMetadata }, { injectorImports }) {
30102 const factoryFn = compileDeclareFactory(fac);
30103 const injectorDef = compileDeclareInjectorFromMetadata(this.mergeInjectorImports(inj, injectorImports));
30104 const ngModuleDef = compileDeclareNgModuleFromMetadata(mod);
30105 const metadata = classMetadata !== null ? compileDeclareClassMetadata(classMetadata) : null;
30106 this.insertMetadataStatement(ngModuleDef.statements, metadata);
30107 // NOTE: no remote scoping required as this is banned in partial compilation.
30108 return this.compileNgModule(factoryFn, injectorDef, ngModuleDef);
30109 }
30110 /**
30111 * Merge the injector imports (which are 'exports' that were later found to be NgModules)
30112 * computed during resolution with the ones from analysis.
30113 */
30114 mergeInjectorImports(inj, injectorImports) {
30115 return { ...inj, imports: [...inj.imports, ...injectorImports] };
30116 }
30117 /**
30118 * Add class metadata statements, if provided, to the `ngModuleStatements`.
30119 */
30120 insertMetadataStatement(ngModuleStatements, metadata) {
30121 if (metadata !== null) {
30122 ngModuleStatements.unshift(metadata.toStmt());
30123 }
30124 }
30125 /**
30126 * Add remote scoping statements, as needed, to the `ngModuleStatements`.
30127 */
30128 appendRemoteScopingStatements(ngModuleStatements, node, declarations) {
30129 const context = getSourceFile(node);
30130 for (const decl of declarations) {
30131 const remoteScope = this.scopeRegistry.getRemoteScope(decl.node);
30132 if (remoteScope !== null) {
30133 const directives = remoteScope.directives.map(directive => this.refEmitter.emit(directive, context).expression);
30134 const pipes = remoteScope.pipes.map(pipe => this.refEmitter.emit(pipe, context).expression);
30135 const directiveArray = new LiteralArrayExpr(directives);
30136 const pipesArray = new LiteralArrayExpr(pipes);
30137 const declExpr = this.refEmitter.emit(decl, context).expression;
30138 const setComponentScope = new ExternalExpr(Identifiers$1.setComponentScope);
30139 const callExpr = new InvokeFunctionExpr(setComponentScope, [declExpr, directiveArray, pipesArray]);
30140 ngModuleStatements.push(callExpr.toStmt());
30141 }
30142 }
30143 }
30144 compileNgModule(factoryFn, injectorDef, ngModuleDef) {
30145 const res = [
30146 factoryFn,
30147 {
30148 name: 'ɵmod',
30149 initializer: ngModuleDef.expression,
30150 statements: ngModuleDef.statements,
30151 type: ngModuleDef.type,
30152 },
30153 {
30154 name: 'ɵinj',
30155 initializer: injectorDef.expression,
30156 statements: injectorDef.statements,
30157 type: injectorDef.type,
30158 },
30159 ];
30160 return res;
30161 }
30162 _toR3Reference(valueRef, valueContext, typeContext) {
30163 if (valueRef.hasOwningModuleGuess) {
30164 return toR3Reference(valueRef, valueRef, valueContext, valueContext, this.refEmitter);
30165 }
30166 else {
30167 let typeRef = valueRef;
30168 let typeNode = this.reflector.getDtsDeclaration(typeRef.node);
30169 if (typeNode !== null && isNamedClassDeclaration(typeNode)) {
30170 typeRef = new Reference(typeNode);
30171 }
30172 return toR3Reference(valueRef, typeRef, valueContext, typeContext, this.refEmitter);
30173 }
30174 }
30175 /**
30176 * Given a `FunctionDeclaration`, `MethodDeclaration` or `FunctionExpression`, check if it is
30177 * typed as a `ModuleWithProviders` and return an expression referencing the module if available.
30178 */
30179 _extractModuleFromModuleWithProvidersFn(node) {
30180 const type = node.type || null;
30181 return type &&
30182 (this._reflectModuleFromTypeParam(type, node) || this._reflectModuleFromLiteralType(type));
30183 }
30184 /**
30185 * Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
30186 * `ModuleWithProviders<T>`
30187 * @param type The type to reflect on.
30188 * @returns the identifier of the NgModule type if found, or null otherwise.
30189 */
30190 _reflectModuleFromTypeParam(type, node) {
30191 // Examine the type of the function to see if it's a ModuleWithProviders reference.
30192 if (!ts__default["default"].isTypeReferenceNode(type)) {
30193 return null;
30194 }
30195 const typeName = type &&
30196 (ts__default["default"].isIdentifier(type.typeName) && type.typeName ||
30197 ts__default["default"].isQualifiedName(type.typeName) && type.typeName.right) ||
30198 null;
30199 if (typeName === null) {
30200 return null;
30201 }
30202 // Look at the type itself to see where it comes from.
30203 const id = this.reflector.getImportOfIdentifier(typeName);
30204 // If it's not named ModuleWithProviders, bail.
30205 if (id === null || id.name !== 'ModuleWithProviders') {
30206 return null;
30207 }
30208 // If it's not from @angular/core, bail.
30209 if (!this.isCore && id.from !== '@angular/core') {
30210 return null;
30211 }
30212 // If there's no type parameter specified, bail.
30213 if (type.typeArguments === undefined || type.typeArguments.length !== 1) {
30214 const parent = ts__default["default"].isMethodDeclaration(node) && ts__default["default"].isClassDeclaration(node.parent) ? node.parent : null;
30215 const symbolName = (parent && parent.name ? parent.name.getText() + '.' : '') +
30216 (node.name ? node.name.getText() : 'anonymous');
30217 throw new FatalDiagnosticError(ErrorCode.NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC, type, `${symbolName} returns a ModuleWithProviders type without a generic type argument. ` +
30218 `Please add a generic type argument to the ModuleWithProviders type. If this ` +
30219 `occurrence is in library code you don't control, please contact the library authors.`);
30220 }
30221 const arg = type.typeArguments[0];
30222 return typeNodeToValueExpr(arg);
30223 }
30224 /**
30225 * Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
30226 * `A|B|{ngModule: T}|C`.
30227 * @param type The type to reflect on.
30228 * @returns the identifier of the NgModule type if found, or null otherwise.
30229 */
30230 _reflectModuleFromLiteralType(type) {
30231 if (!ts__default["default"].isIntersectionTypeNode(type)) {
30232 return null;
30233 }
30234 for (const t of type.types) {
30235 if (ts__default["default"].isTypeLiteralNode(t)) {
30236 for (const m of t.members) {
30237 const ngModuleType = ts__default["default"].isPropertySignature(m) && ts__default["default"].isIdentifier(m.name) &&
30238 m.name.text === 'ngModule' && m.type ||
30239 null;
30240 const ngModuleExpression = ngModuleType && typeNodeToValueExpr(ngModuleType);
30241 if (ngModuleExpression) {
30242 return ngModuleExpression;
30243 }
30244 }
30245 }
30246 }
30247 return null;
30248 }
30249 // Verify that a "Declaration" reference is a `ClassDeclaration` reference.
30250 isClassDeclarationReference(ref) {
30251 return this.reflector.isClass(ref.node);
30252 }
30253 /**
30254 * Compute a list of `Reference`s from a resolved metadata value.
30255 */
30256 resolveTypeList(expr, resolvedList, className, arrayName) {
30257 const refList = [];
30258 if (!Array.isArray(resolvedList)) {
30259 throw createValueHasWrongTypeError(expr, resolvedList, `Expected array when reading the NgModule.${arrayName} of ${className}`);
30260 }
30261 resolvedList.forEach((entry, idx) => {
30262 // Unwrap ModuleWithProviders for modules that are locally declared (and thus static
30263 // resolution was able to descend into the function and return an object literal, a Map).
30264 if (entry instanceof Map && entry.has('ngModule')) {
30265 entry = entry.get('ngModule');
30266 }
30267 if (Array.isArray(entry)) {
30268 // Recurse into nested arrays.
30269 refList.push(...this.resolveTypeList(expr, entry, className, arrayName));
30270 }
30271 else if (entry instanceof Reference) {
30272 if (!this.isClassDeclarationReference(entry)) {
30273 throw createValueHasWrongTypeError(entry.node, entry, `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a class`);
30274 }
30275 refList.push(entry);
30276 }
30277 else {
30278 // TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array.
30279 throw createValueHasWrongTypeError(expr, entry, `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a reference`);
30280 }
30281 });
30282 return refList;
30283 }
30284 }
30285 function isNgModule(node, compilation) {
30286 return !compilation.directives.some(directive => directive.ref.node === node) &&
30287 !compilation.pipes.some(pipe => pipe.ref.node === node);
30288 }
30289
30290 /**
30291 * @license
30292 * Copyright Google LLC All Rights Reserved.
30293 *
30294 * Use of this source code is governed by an MIT-style license that can be
30295 * found in the LICENSE file at https://angular.io/license
30296 */
30297 const EMPTY_MAP = new Map();
30298 const EMPTY_ARRAY = [];
30299 /**
30300 * Represents an Angular component.
30301 */
30302 class ComponentSymbol extends DirectiveSymbol {
30303 constructor() {
30304 super(...arguments);
30305 this.usedDirectives = [];
30306 this.usedPipes = [];
30307 this.isRemotelyScoped = false;
30308 }
30309 isEmitAffected(previousSymbol, publicApiAffected) {
30310 if (!(previousSymbol instanceof ComponentSymbol)) {
30311 return true;
30312 }
30313 // Create an equality function that considers symbols equal if they represent the same
30314 // declaration, but only if the symbol in the current compilation does not have its public API
30315 // affected.
30316 const isSymbolUnaffected = (current, previous) => isReferenceEqual(current, previous) && !publicApiAffected.has(current.symbol);
30317 // The emit of a component is affected if either of the following is true:
30318 // 1. The component used to be remotely scoped but no longer is, or vice versa.
30319 // 2. The list of used directives has changed or any of those directives have had their public
30320 // API changed. If the used directives have been reordered but not otherwise affected then
30321 // the component must still be re-emitted, as this may affect directive instantiation order.
30322 // 3. The list of used pipes has changed, or any of those pipes have had their public API
30323 // changed.
30324 return this.isRemotelyScoped !== previousSymbol.isRemotelyScoped ||
30325 !isArrayEqual(this.usedDirectives, previousSymbol.usedDirectives, isSymbolUnaffected) ||
30326 !isArrayEqual(this.usedPipes, previousSymbol.usedPipes, isSymbolUnaffected);
30327 }
30328 isTypeCheckBlockAffected(previousSymbol, typeCheckApiAffected) {
30329 if (!(previousSymbol instanceof ComponentSymbol)) {
30330 return true;
30331 }
30332 // To verify that a used directive is not affected we need to verify that its full inheritance
30333 // chain is not present in `typeCheckApiAffected`.
30334 const isInheritanceChainAffected = (symbol) => {
30335 let currentSymbol = symbol;
30336 while (currentSymbol instanceof DirectiveSymbol) {
30337 if (typeCheckApiAffected.has(currentSymbol)) {
30338 return true;
30339 }
30340 currentSymbol = currentSymbol.baseClass;
30341 }
30342 return false;
30343 };
30344 // Create an equality function that considers directives equal if they represent the same
30345 // declaration and if the symbol and all symbols it inherits from in the current compilation
30346 // do not have their type-check API affected.
30347 const isDirectiveUnaffected = (current, previous) => isReferenceEqual(current, previous) && !isInheritanceChainAffected(current.symbol);
30348 // Create an equality function that considers pipes equal if they represent the same
30349 // declaration and if the symbol in the current compilation does not have its type-check
30350 // API affected.
30351 const isPipeUnaffected = (current, previous) => isReferenceEqual(current, previous) && !typeCheckApiAffected.has(current.symbol);
30352 // The emit of a type-check block of a component is affected if either of the following is true:
30353 // 1. The list of used directives has changed or any of those directives have had their
30354 // type-check API changed.
30355 // 2. The list of used pipes has changed, or any of those pipes have had their type-check API
30356 // changed.
30357 return !isArrayEqual(this.usedDirectives, previousSymbol.usedDirectives, isDirectiveUnaffected) ||
30358 !isArrayEqual(this.usedPipes, previousSymbol.usedPipes, isPipeUnaffected);
30359 }
30360 }
30361 /**
30362 * `DecoratorHandler` which handles the `@Component` annotation.
30363 */
30364 class ComponentDecoratorHandler {
30365 constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, cycleHandlingStrategy, refEmitter, depTracker, injectableRegistry, semanticDepGraphUpdater, annotateForClosureCompiler, perf) {
30366 this.reflector = reflector;
30367 this.evaluator = evaluator;
30368 this.metaRegistry = metaRegistry;
30369 this.metaReader = metaReader;
30370 this.scopeReader = scopeReader;
30371 this.scopeRegistry = scopeRegistry;
30372 this.typeCheckScopeRegistry = typeCheckScopeRegistry;
30373 this.resourceRegistry = resourceRegistry;
30374 this.isCore = isCore;
30375 this.resourceLoader = resourceLoader;
30376 this.rootDirs = rootDirs;
30377 this.defaultPreserveWhitespaces = defaultPreserveWhitespaces;
30378 this.i18nUseExternalIds = i18nUseExternalIds;
30379 this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
30380 this.usePoisonedData = usePoisonedData;
30381 this.i18nNormalizeLineEndingsInICUs = i18nNormalizeLineEndingsInICUs;
30382 this.moduleResolver = moduleResolver;
30383 this.cycleAnalyzer = cycleAnalyzer;
30384 this.cycleHandlingStrategy = cycleHandlingStrategy;
30385 this.refEmitter = refEmitter;
30386 this.depTracker = depTracker;
30387 this.injectableRegistry = injectableRegistry;
30388 this.semanticDepGraphUpdater = semanticDepGraphUpdater;
30389 this.annotateForClosureCompiler = annotateForClosureCompiler;
30390 this.perf = perf;
30391 this.literalCache = new Map();
30392 this.elementSchemaRegistry = new DomElementSchemaRegistry();
30393 /**
30394 * During the asynchronous preanalyze phase, it's necessary to parse the template to extract
30395 * any potential <link> tags which might need to be loaded. This cache ensures that work is not
30396 * thrown away, and the parsed template is reused during the analyze phase.
30397 */
30398 this.preanalyzeTemplateCache = new Map();
30399 this.preanalyzeStylesCache = new Map();
30400 this.precedence = HandlerPrecedence.PRIMARY;
30401 this.name = ComponentDecoratorHandler.name;
30402 }
30403 detect(node, decorators) {
30404 if (!decorators) {
30405 return undefined;
30406 }
30407 const decorator = findAngularDecorator(decorators, 'Component', this.isCore);
30408 if (decorator !== undefined) {
30409 return {
30410 trigger: decorator.node,
30411 decorator,
30412 metadata: decorator,
30413 };
30414 }
30415 else {
30416 return undefined;
30417 }
30418 }
30419 preanalyze(node, decorator) {
30420 // In preanalyze, resource URLs associated with the component are asynchronously preloaded via
30421 // the resourceLoader. This is the only time async operations are allowed for a component.
30422 // These resources are:
30423 //
30424 // - the templateUrl, if there is one
30425 // - any styleUrls if present
30426 // - any stylesheets referenced from <link> tags in the template itself
30427 //
30428 // As a result of the last one, the template must be parsed as part of preanalysis to extract
30429 // <link> tags, which may involve waiting for the templateUrl to be resolved first.
30430 // If preloading isn't possible, then skip this step.
30431 if (!this.resourceLoader.canPreload) {
30432 return undefined;
30433 }
30434 const meta = this._resolveLiteral(decorator);
30435 const component = reflectObjectLiteral(meta);
30436 const containingFile = node.getSourceFile().fileName;
30437 const resolveStyleUrl = (styleUrl) => {
30438 try {
30439 const resourceUrl = this.resourceLoader.resolve(styleUrl, containingFile);
30440 return this.resourceLoader.preload(resourceUrl, { type: 'style', containingFile });
30441 }
30442 catch {
30443 // Don't worry about failures to preload. We can handle this problem during analysis by
30444 // producing a diagnostic.
30445 return undefined;
30446 }
30447 };
30448 // A Promise that waits for the template and all <link>ed styles within it to be preloaded.
30449 const templateAndTemplateStyleResources = this._preloadAndParseTemplate(node, decorator, component, containingFile)
30450 .then((template) => {
30451 if (template === null) {
30452 return undefined;
30453 }
30454 return Promise.all(template.styleUrls.map(styleUrl => resolveStyleUrl(styleUrl)))
30455 .then(() => undefined);
30456 });
30457 // Extract all the styleUrls in the decorator.
30458 const componentStyleUrls = this._extractComponentStyleUrls(component);
30459 // Extract inline styles, process, and cache for use in synchronous analyze phase
30460 let inlineStyles;
30461 if (component.has('styles')) {
30462 const litStyles = parseFieldArrayValue(component, 'styles', this.evaluator);
30463 if (litStyles === null) {
30464 this.preanalyzeStylesCache.set(node, null);
30465 }
30466 else {
30467 inlineStyles = Promise
30468 .all(litStyles.map(style => this.resourceLoader.preprocessInline(style, { type: 'style', containingFile })))
30469 .then(styles => {
30470 this.preanalyzeStylesCache.set(node, styles);
30471 });
30472 }
30473 }
30474 else {
30475 this.preanalyzeStylesCache.set(node, null);
30476 }
30477 // Wait for both the template and all styleUrl resources to resolve.
30478 return Promise
30479 .all([
30480 templateAndTemplateStyleResources, inlineStyles,
30481 ...componentStyleUrls.map(styleUrl => resolveStyleUrl(styleUrl.url))
30482 ])
30483 .then(() => undefined);
30484 }
30485 analyze(node, decorator, flags = HandlerFlags.NONE) {
30486 this.perf.eventCount(PerfEvent.AnalyzeComponent);
30487 const containingFile = node.getSourceFile().fileName;
30488 this.literalCache.delete(decorator);
30489 let diagnostics;
30490 let isPoisoned = false;
30491 // @Component inherits @Directive, so begin by extracting the @Directive metadata and building
30492 // on it.
30493 const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.isCore, flags, this.annotateForClosureCompiler, this.elementSchemaRegistry.getDefaultComponentElementName());
30494 if (directiveResult === undefined) {
30495 // `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
30496 // case, compilation of the decorator is skipped. Returning an empty object signifies
30497 // that no analysis was produced.
30498 return {};
30499 }
30500 // Next, read the `@Component`-specific fields.
30501 const { decorator: component, metadata, inputs, outputs } = directiveResult;
30502 const encapsulation = this._resolveEnumValue(component, 'encapsulation', 'ViewEncapsulation') ??
30503 ViewEncapsulation.Emulated;
30504 const changeDetection = this._resolveEnumValue(component, 'changeDetection', 'ChangeDetectionStrategy');
30505 let animations = null;
30506 if (component.has('animations')) {
30507 animations = new WrappedNodeExpr(component.get('animations'));
30508 }
30509 // Go through the root directories for this project, and select the one with the smallest
30510 // relative path representation.
30511 const relativeContextFilePath = this.rootDirs.reduce((previous, rootDir) => {
30512 const candidate = relative(absoluteFrom(rootDir), absoluteFrom(containingFile));
30513 if (previous === undefined || candidate.length < previous.length) {
30514 return candidate;
30515 }
30516 else {
30517 return previous;
30518 }
30519 }, undefined);
30520 // Note that we could technically combine the `viewProvidersRequiringFactory` and
30521 // `providersRequiringFactory` into a single set, but we keep the separate so that
30522 // we can distinguish where an error is coming from when logging the diagnostics in `resolve`.
30523 let viewProvidersRequiringFactory = null;
30524 let providersRequiringFactory = null;
30525 let wrappedViewProviders = null;
30526 if (component.has('viewProviders')) {
30527 const viewProviders = component.get('viewProviders');
30528 viewProvidersRequiringFactory =
30529 resolveProvidersRequiringFactory(viewProviders, this.reflector, this.evaluator);
30530 wrappedViewProviders = new WrappedNodeExpr(this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(viewProviders) :
30531 viewProviders);
30532 }
30533 if (component.has('providers')) {
30534 providersRequiringFactory = resolveProvidersRequiringFactory(component.get('providers'), this.reflector, this.evaluator);
30535 }
30536 // Parse the template.
30537 // If a preanalyze phase was executed, the template may already exist in parsed form, so check
30538 // the preanalyzeTemplateCache.
30539 // Extract a closure of the template parsing code so that it can be reparsed with different
30540 // options if needed, like in the indexing pipeline.
30541 let template;
30542 if (this.preanalyzeTemplateCache.has(node)) {
30543 // The template was parsed in preanalyze. Use it and delete it to save memory.
30544 const preanalyzed = this.preanalyzeTemplateCache.get(node);
30545 this.preanalyzeTemplateCache.delete(node);
30546 template = preanalyzed;
30547 }
30548 else {
30549 const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
30550 template = this.extractTemplate(node, templateDecl);
30551 }
30552 const templateResource = template.declaration.isInline ? { path: null, expression: component.get('template') } : {
30553 path: absoluteFrom(template.declaration.resolvedTemplateUrl),
30554 expression: template.sourceMapping.node
30555 };
30556 // Figure out the set of styles. The ordering here is important: external resources (styleUrls)
30557 // precede inline styles, and styles defined in the template override styles defined in the
30558 // component.
30559 let styles = [];
30560 const styleResources = this._extractStyleResources(component, containingFile);
30561 const styleUrls = [
30562 ...this._extractComponentStyleUrls(component), ...this._extractTemplateStyleUrls(template)
30563 ];
30564 for (const styleUrl of styleUrls) {
30565 try {
30566 const resourceUrl = this.resourceLoader.resolve(styleUrl.url, containingFile);
30567 const resourceStr = this.resourceLoader.load(resourceUrl);
30568 styles.push(resourceStr);
30569 if (this.depTracker !== null) {
30570 this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl));
30571 }
30572 }
30573 catch {
30574 if (diagnostics === undefined) {
30575 diagnostics = [];
30576 }
30577 const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
30578 2 /* StylesheetFromDecorator */ :
30579 1 /* StylesheetFromTemplate */;
30580 diagnostics.push(this.makeResourceNotFoundError(styleUrl.url, styleUrl.nodeForError, resourceType)
30581 .toDiagnostic());
30582 }
30583 }
30584 if (encapsulation === ViewEncapsulation.ShadowDom && metadata.selector !== null) {
30585 const selectorError = checkCustomElementSelectorForErrors(metadata.selector);
30586 if (selectorError !== null) {
30587 if (diagnostics === undefined) {
30588 diagnostics = [];
30589 }
30590 diagnostics.push(makeDiagnostic(ErrorCode.COMPONENT_INVALID_SHADOW_DOM_SELECTOR, component.get('selector'), selectorError));
30591 }
30592 }
30593 // If inline styles were preprocessed use those
30594 let inlineStyles = null;
30595 if (this.preanalyzeStylesCache.has(node)) {
30596 inlineStyles = this.preanalyzeStylesCache.get(node);
30597 this.preanalyzeStylesCache.delete(node);
30598 if (inlineStyles !== null) {
30599 styles.push(...inlineStyles);
30600 }
30601 }
30602 else {
30603 // Preprocessing is only supported asynchronously
30604 // If no style cache entry is present asynchronous preanalyze was not executed.
30605 // This protects against accidental differences in resource contents when preanalysis
30606 // is not used with a provided transformResource hook on the ResourceHost.
30607 if (this.resourceLoader.canPreprocess) {
30608 throw new Error('Inline resource processing requires asynchronous preanalyze.');
30609 }
30610 if (component.has('styles')) {
30611 const litStyles = parseFieldArrayValue(component, 'styles', this.evaluator);
30612 if (litStyles !== null) {
30613 inlineStyles = [...litStyles];
30614 styles.push(...litStyles);
30615 }
30616 }
30617 }
30618 if (template.styles.length > 0) {
30619 styles.push(...template.styles);
30620 }
30621 const output = {
30622 analysis: {
30623 baseClass: readBaseClass(node, this.reflector, this.evaluator),
30624 inputs,
30625 outputs,
30626 meta: {
30627 ...metadata,
30628 template: {
30629 nodes: template.nodes,
30630 ngContentSelectors: template.ngContentSelectors,
30631 },
30632 encapsulation,
30633 interpolation: template.interpolationConfig ?? DEFAULT_INTERPOLATION_CONFIG,
30634 styles,
30635 // These will be replaced during the compilation step, after all `NgModule`s have been
30636 // analyzed and the full compilation scope for the component can be realized.
30637 animations,
30638 viewProviders: wrappedViewProviders,
30639 i18nUseExternalIds: this.i18nUseExternalIds,
30640 relativeContextFilePath,
30641 },
30642 typeCheckMeta: extractDirectiveTypeCheckMeta(node, inputs, this.reflector),
30643 classMetadata: extractClassMetadata(node, this.reflector, this.isCore, this.annotateForClosureCompiler, dec => this._transformDecoratorToInlineResources(dec, component, styles, template)),
30644 template,
30645 providersRequiringFactory,
30646 viewProvidersRequiringFactory,
30647 inlineStyles,
30648 styleUrls,
30649 resources: {
30650 styles: styleResources,
30651 template: templateResource,
30652 },
30653 isPoisoned,
30654 },
30655 diagnostics,
30656 };
30657 if (changeDetection !== null) {
30658 output.analysis.meta.changeDetection = changeDetection;
30659 }
30660 return output;
30661 }
30662 symbol(node, analysis) {
30663 const typeParameters = extractSemanticTypeParameters(node);
30664 return new ComponentSymbol(node, analysis.meta.selector, analysis.inputs, analysis.outputs, analysis.meta.exportAs, analysis.typeCheckMeta, typeParameters);
30665 }
30666 register(node, analysis) {
30667 // Register this component's information with the `MetadataRegistry`. This ensures that
30668 // the information about the component is available during the compile() phase.
30669 const ref = new Reference(node);
30670 this.metaRegistry.registerDirectiveMetadata({
30671 type: MetaType.Directive,
30672 ref,
30673 name: node.name.text,
30674 selector: analysis.meta.selector,
30675 exportAs: analysis.meta.exportAs,
30676 inputs: analysis.inputs,
30677 outputs: analysis.outputs,
30678 queries: analysis.meta.queries.map(query => query.propertyName),
30679 isComponent: true,
30680 baseClass: analysis.baseClass,
30681 ...analysis.typeCheckMeta,
30682 isPoisoned: analysis.isPoisoned,
30683 isStructural: false,
30684 });
30685 this.resourceRegistry.registerResources(analysis.resources, node);
30686 this.injectableRegistry.registerInjectable(node);
30687 }
30688 index(context, node, analysis) {
30689 if (analysis.isPoisoned && !this.usePoisonedData) {
30690 return null;
30691 }
30692 const scope = this.scopeReader.getScopeForComponent(node);
30693 const selector = analysis.meta.selector;
30694 const matcher = new SelectorMatcher();
30695 if (scope !== null) {
30696 if ((scope.compilation.isPoisoned || scope.exported.isPoisoned) && !this.usePoisonedData) {
30697 // Don't bother indexing components which had erroneous scopes, unless specifically
30698 // requested.
30699 return null;
30700 }
30701 for (const directive of scope.compilation.directives) {
30702 if (directive.selector !== null) {
30703 matcher.addSelectables(CssSelector.parse(directive.selector), directive);
30704 }
30705 }
30706 }
30707 const binder = new R3TargetBinder(matcher);
30708 const boundTemplate = binder.bind({ template: analysis.template.diagNodes });
30709 context.addComponent({
30710 declaration: node,
30711 selector,
30712 boundTemplate,
30713 templateMeta: {
30714 isInline: analysis.template.declaration.isInline,
30715 file: analysis.template.file,
30716 },
30717 });
30718 }
30719 typeCheck(ctx, node, meta) {
30720 if (this.typeCheckScopeRegistry === null || !ts__default["default"].isClassDeclaration(node)) {
30721 return;
30722 }
30723 if (meta.isPoisoned && !this.usePoisonedData) {
30724 return;
30725 }
30726 const scope = this.typeCheckScopeRegistry.getTypeCheckScope(node);
30727 if (scope.isPoisoned && !this.usePoisonedData) {
30728 // Don't type-check components that had errors in their scopes, unless requested.
30729 return;
30730 }
30731 const binder = new R3TargetBinder(scope.matcher);
30732 ctx.addTemplate(new Reference(node), binder, meta.template.diagNodes, scope.pipes, scope.schemas, meta.template.sourceMapping, meta.template.file, meta.template.errors);
30733 }
30734 extendedTemplateCheck(component, extendedTemplateChecker) {
30735 return extendedTemplateChecker.getDiagnosticsForComponent(component);
30736 }
30737 resolve(node, analysis, symbol) {
30738 if (this.semanticDepGraphUpdater !== null && analysis.baseClass instanceof Reference) {
30739 symbol.baseClass = this.semanticDepGraphUpdater.getSymbol(analysis.baseClass.node);
30740 }
30741 if (analysis.isPoisoned && !this.usePoisonedData) {
30742 return {};
30743 }
30744 const context = node.getSourceFile();
30745 // Check whether this component was registered with an NgModule. If so, it should be compiled
30746 // under that module's compilation scope.
30747 const scope = this.scopeReader.getScopeForComponent(node);
30748 let metadata = analysis.meta;
30749 const data = {
30750 directives: EMPTY_ARRAY,
30751 pipes: EMPTY_MAP,
30752 declarationListEmitMode: 0 /* Direct */,
30753 };
30754 if (scope !== null && (!scope.compilation.isPoisoned || this.usePoisonedData)) {
30755 const matcher = new SelectorMatcher();
30756 for (const dir of scope.compilation.directives) {
30757 if (dir.selector !== null) {
30758 matcher.addSelectables(CssSelector.parse(dir.selector), dir);
30759 }
30760 }
30761 const pipes = new Map();
30762 for (const pipe of scope.compilation.pipes) {
30763 pipes.set(pipe.name, pipe.ref);
30764 }
30765 // Next, the component template AST is bound using the R3TargetBinder. This produces a
30766 // BoundTarget, which is similar to a ts.TypeChecker.
30767 const binder = new R3TargetBinder(matcher);
30768 const bound = binder.bind({ template: metadata.template.nodes });
30769 const usedDirectives = bound.getUsedDirectives().map(directive => {
30770 const type = this.refEmitter.emit(directive.ref, context);
30771 return {
30772 ref: directive.ref,
30773 type: type.expression,
30774 importedFile: type.importedFile,
30775 selector: directive.selector,
30776 inputs: directive.inputs.propertyNames,
30777 outputs: directive.outputs.propertyNames,
30778 exportAs: directive.exportAs,
30779 isComponent: directive.isComponent,
30780 };
30781 });
30782 const usedPipes = [];
30783 for (const pipeName of bound.getUsedPipes()) {
30784 if (!pipes.has(pipeName)) {
30785 continue;
30786 }
30787 const pipe = pipes.get(pipeName);
30788 const type = this.refEmitter.emit(pipe, context);
30789 usedPipes.push({
30790 ref: pipe,
30791 pipeName,
30792 expression: type.expression,
30793 importedFile: type.importedFile,
30794 });
30795 }
30796 if (this.semanticDepGraphUpdater !== null) {
30797 symbol.usedDirectives = usedDirectives.map(dir => this.semanticDepGraphUpdater.getSemanticReference(dir.ref.node, dir.type));
30798 symbol.usedPipes = usedPipes.map(pipe => this.semanticDepGraphUpdater.getSemanticReference(pipe.ref.node, pipe.expression));
30799 }
30800 // Scan through the directives/pipes actually used in the template and check whether any
30801 // import which needs to be generated would create a cycle.
30802 const cyclesFromDirectives = new Map();
30803 for (const usedDirective of usedDirectives) {
30804 const cycle = this._checkForCyclicImport(usedDirective.importedFile, usedDirective.type, context);
30805 if (cycle !== null) {
30806 cyclesFromDirectives.set(usedDirective, cycle);
30807 }
30808 }
30809 const cyclesFromPipes = new Map();
30810 for (const usedPipe of usedPipes) {
30811 const cycle = this._checkForCyclicImport(usedPipe.importedFile, usedPipe.expression, context);
30812 if (cycle !== null) {
30813 cyclesFromPipes.set(usedPipe, cycle);
30814 }
30815 }
30816 const cycleDetected = cyclesFromDirectives.size !== 0 || cyclesFromPipes.size !== 0;
30817 if (!cycleDetected) {
30818 // No cycle was detected. Record the imports that need to be created in the cycle detector
30819 // so that future cyclic import checks consider their production.
30820 for (const { type, importedFile } of usedDirectives) {
30821 this._recordSyntheticImport(importedFile, type, context);
30822 }
30823 for (const { expression, importedFile } of usedPipes) {
30824 this._recordSyntheticImport(importedFile, expression, context);
30825 }
30826 // Check whether the directive/pipe arrays in ɵcmp need to be wrapped in closures.
30827 // This is required if any directive/pipe reference is to a declaration in the same file
30828 // but declared after this component.
30829 const wrapDirectivesAndPipesInClosure = usedDirectives.some(dir => isExpressionForwardReference(dir.type, node.name, context)) ||
30830 usedPipes.some(pipe => isExpressionForwardReference(pipe.expression, node.name, context));
30831 data.directives = usedDirectives;
30832 data.pipes = new Map(usedPipes.map(pipe => [pipe.pipeName, pipe.expression]));
30833 data.declarationListEmitMode = wrapDirectivesAndPipesInClosure ?
30834 1 /* Closure */ :
30835 0 /* Direct */;
30836 }
30837 else {
30838 if (this.cycleHandlingStrategy === 0 /* UseRemoteScoping */) {
30839 // Declaring the directiveDefs/pipeDefs arrays directly would require imports that would
30840 // create a cycle. Instead, mark this component as requiring remote scoping, so that the
30841 // NgModule file will take care of setting the directives for the component.
30842 this.scopeRegistry.setComponentRemoteScope(node, usedDirectives.map(dir => dir.ref), usedPipes.map(pipe => pipe.ref));
30843 symbol.isRemotelyScoped = true;
30844 // If a semantic graph is being tracked, record the fact that this component is remotely
30845 // scoped with the declaring NgModule symbol as the NgModule's emit becomes dependent on
30846 // the directive/pipe usages of this component.
30847 if (this.semanticDepGraphUpdater !== null) {
30848 const moduleSymbol = this.semanticDepGraphUpdater.getSymbol(scope.ngModule);
30849 if (!(moduleSymbol instanceof NgModuleSymbol)) {
30850 throw new Error(`AssertionError: Expected ${scope.ngModule.name} to be an NgModuleSymbol.`);
30851 }
30852 moduleSymbol.addRemotelyScopedComponent(symbol, symbol.usedDirectives, symbol.usedPipes);
30853 }
30854 }
30855 else {
30856 // We are not able to handle this cycle so throw an error.
30857 const relatedMessages = [];
30858 for (const [dir, cycle] of cyclesFromDirectives) {
30859 relatedMessages.push(makeCyclicImportInfo(dir.ref, dir.isComponent ? 'component' : 'directive', cycle));
30860 }
30861 for (const [pipe, cycle] of cyclesFromPipes) {
30862 relatedMessages.push(makeCyclicImportInfo(pipe.ref, 'pipe', cycle));
30863 }
30864 throw new FatalDiagnosticError(ErrorCode.IMPORT_CYCLE_DETECTED, node, 'One or more import cycles would need to be created to compile this component, ' +
30865 'which is not supported by the current compiler configuration.', relatedMessages);
30866 }
30867 }
30868 }
30869 const diagnostics = [];
30870 if (analysis.providersRequiringFactory !== null &&
30871 analysis.meta.providers instanceof WrappedNodeExpr) {
30872 const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.meta.providers.node, this.injectableRegistry);
30873 diagnostics.push(...providerDiagnostics);
30874 }
30875 if (analysis.viewProvidersRequiringFactory !== null &&
30876 analysis.meta.viewProviders instanceof WrappedNodeExpr) {
30877 const viewProviderDiagnostics = getProviderDiagnostics(analysis.viewProvidersRequiringFactory, analysis.meta.viewProviders.node, this.injectableRegistry);
30878 diagnostics.push(...viewProviderDiagnostics);
30879 }
30880 const directiveDiagnostics = getDirectiveDiagnostics(node, this.metaReader, this.evaluator, this.reflector, this.scopeRegistry, 'Component');
30881 if (directiveDiagnostics !== null) {
30882 diagnostics.push(...directiveDiagnostics);
30883 }
30884 if (diagnostics.length > 0) {
30885 return { diagnostics };
30886 }
30887 return { data };
30888 }
30889 xi18n(ctx, node, analysis) {
30890 ctx.updateFromTemplate(analysis.template.content, analysis.template.declaration.resolvedTemplateUrl, analysis.template.interpolationConfig ?? DEFAULT_INTERPOLATION_CONFIG);
30891 }
30892 updateResources(node, analysis) {
30893 const containingFile = node.getSourceFile().fileName;
30894 // If the template is external, re-parse it.
30895 const templateDecl = analysis.template.declaration;
30896 if (!templateDecl.isInline) {
30897 analysis.template = this.extractTemplate(node, templateDecl);
30898 }
30899 // Update any external stylesheets and rebuild the combined 'styles' list.
30900 // TODO(alxhub): write tests for styles when the primary compiler uses the updateResources path
30901 let styles = [];
30902 if (analysis.styleUrls !== null) {
30903 for (const styleUrl of analysis.styleUrls) {
30904 try {
30905 const resolvedStyleUrl = this.resourceLoader.resolve(styleUrl.url, containingFile);
30906 const styleText = this.resourceLoader.load(resolvedStyleUrl);
30907 styles.push(styleText);
30908 }
30909 catch (e) {
30910 // Resource resolve failures should already be in the diagnostics list from the analyze
30911 // stage. We do not need to do anything with them when updating resources.
30912 }
30913 }
30914 }
30915 if (analysis.inlineStyles !== null) {
30916 for (const styleText of analysis.inlineStyles) {
30917 styles.push(styleText);
30918 }
30919 }
30920 for (const styleText of analysis.template.styles) {
30921 styles.push(styleText);
30922 }
30923 analysis.meta.styles = styles;
30924 }
30925 compileFull(node, analysis, resolution, pool) {
30926 if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
30927 return [];
30928 }
30929 const meta = { ...analysis.meta, ...resolution };
30930 const fac = compileNgFactoryDefField(toFactoryMetadata(meta, FactoryTarget$1.Component));
30931 const def = compileComponentFromMetadata(meta, pool, makeBindingParser());
30932 const classMetadata = analysis.classMetadata !== null ?
30933 compileClassMetadata(analysis.classMetadata).toStmt() :
30934 null;
30935 return compileResults(fac, def, classMetadata, 'ɵcmp');
30936 }
30937 compilePartial(node, analysis, resolution) {
30938 if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
30939 return [];
30940 }
30941 const templateInfo = {
30942 content: analysis.template.content,
30943 sourceUrl: analysis.template.declaration.resolvedTemplateUrl,
30944 isInline: analysis.template.declaration.isInline,
30945 inlineTemplateLiteralExpression: analysis.template.sourceMapping.type === 'direct' ?
30946 new WrappedNodeExpr(analysis.template.sourceMapping.node) :
30947 null,
30948 };
30949 const meta = { ...analysis.meta, ...resolution };
30950 const fac = compileDeclareFactory(toFactoryMetadata(meta, FactoryTarget$1.Component));
30951 const def = compileDeclareComponentFromMetadata(meta, analysis.template, templateInfo);
30952 const classMetadata = analysis.classMetadata !== null ?
30953 compileDeclareClassMetadata(analysis.classMetadata).toStmt() :
30954 null;
30955 return compileResults(fac, def, classMetadata, 'ɵcmp');
30956 }
30957 /**
30958 * Transforms the given decorator to inline external resources. i.e. if the decorator
30959 * resolves to `@Component`, the `templateUrl` and `styleUrls` metadata fields will be
30960 * transformed to their semantically-equivalent inline variants.
30961 *
30962 * This method is used for serializing decorators into the class metadata. The emitted
30963 * class metadata should not refer to external resources as this would be inconsistent
30964 * with the component definitions/declarations which already inline external resources.
30965 *
30966 * Additionally, the references to external resources would require libraries to ship
30967 * external resources exclusively for the class metadata.
30968 */
30969 _transformDecoratorToInlineResources(dec, component, styles, template) {
30970 if (dec.name !== 'Component') {
30971 return dec;
30972 }
30973 // If no external resources are referenced, preserve the original decorator
30974 // for the best source map experience when the decorator is emitted in TS.
30975 if (!component.has('templateUrl') && !component.has('styleUrls')) {
30976 return dec;
30977 }
30978 const metadata = new Map(component);
30979 // Set the `template` property if the `templateUrl` property is set.
30980 if (metadata.has('templateUrl')) {
30981 metadata.delete('templateUrl');
30982 metadata.set('template', ts__default["default"].createStringLiteral(template.content));
30983 }
30984 // Set the `styles` property if the `styleUrls` property is set.
30985 if (metadata.has('styleUrls')) {
30986 metadata.delete('styleUrls');
30987 metadata.set('styles', ts__default["default"].createArrayLiteral(styles.map(s => ts__default["default"].createStringLiteral(s))));
30988 }
30989 // Convert the metadata to TypeScript AST object literal element nodes.
30990 const newMetadataFields = [];
30991 for (const [name, value] of metadata.entries()) {
30992 newMetadataFields.push(ts__default["default"].createPropertyAssignment(name, value));
30993 }
30994 // Return the original decorator with the overridden metadata argument.
30995 return { ...dec, args: [ts__default["default"].createObjectLiteral(newMetadataFields)] };
30996 }
30997 _resolveLiteral(decorator) {
30998 if (this.literalCache.has(decorator)) {
30999 return this.literalCache.get(decorator);
31000 }
31001 if (decorator.args === null || decorator.args.length !== 1) {
31002 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @Component decorator`);
31003 }
31004 const meta = unwrapExpression(decorator.args[0]);
31005 if (!ts__default["default"].isObjectLiteralExpression(meta)) {
31006 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, `Decorator argument must be literal.`);
31007 }
31008 this.literalCache.set(decorator, meta);
31009 return meta;
31010 }
31011 _resolveEnumValue(component, field, enumSymbolName) {
31012 let resolved = null;
31013 if (component.has(field)) {
31014 const expr = component.get(field);
31015 const value = this.evaluator.evaluate(expr);
31016 if (value instanceof EnumValue && isAngularCoreReference(value.enumRef, enumSymbolName)) {
31017 resolved = value.resolved;
31018 }
31019 else {
31020 throw createValueHasWrongTypeError(expr, value, `${field} must be a member of ${enumSymbolName} enum from @angular/core`);
31021 }
31022 }
31023 return resolved;
31024 }
31025 _extractComponentStyleUrls(component) {
31026 if (!component.has('styleUrls')) {
31027 return [];
31028 }
31029 return this._extractStyleUrlsFromExpression(component.get('styleUrls'));
31030 }
31031 _extractStyleUrlsFromExpression(styleUrlsExpr) {
31032 const styleUrls = [];
31033 if (ts__default["default"].isArrayLiteralExpression(styleUrlsExpr)) {
31034 for (const styleUrlExpr of styleUrlsExpr.elements) {
31035 if (ts__default["default"].isSpreadElement(styleUrlExpr)) {
31036 styleUrls.push(...this._extractStyleUrlsFromExpression(styleUrlExpr.expression));
31037 }
31038 else {
31039 const styleUrl = this.evaluator.evaluate(styleUrlExpr);
31040 if (typeof styleUrl !== 'string') {
31041 throw createValueHasWrongTypeError(styleUrlExpr, styleUrl, 'styleUrl must be a string');
31042 }
31043 styleUrls.push({
31044 url: styleUrl,
31045 source: 2 /* StylesheetFromDecorator */,
31046 nodeForError: styleUrlExpr,
31047 });
31048 }
31049 }
31050 }
31051 else {
31052 const evaluatedStyleUrls = this.evaluator.evaluate(styleUrlsExpr);
31053 if (!isStringArray(evaluatedStyleUrls)) {
31054 throw createValueHasWrongTypeError(styleUrlsExpr, evaluatedStyleUrls, 'styleUrls must be an array of strings');
31055 }
31056 for (const styleUrl of evaluatedStyleUrls) {
31057 styleUrls.push({
31058 url: styleUrl,
31059 source: 2 /* StylesheetFromDecorator */,
31060 nodeForError: styleUrlsExpr,
31061 });
31062 }
31063 }
31064 return styleUrls;
31065 }
31066 _extractStyleResources(component, containingFile) {
31067 const styles = new Set();
31068 function stringLiteralElements(array) {
31069 return array.elements.filter((e) => ts__default["default"].isStringLiteralLike(e));
31070 }
31071 // If styleUrls is a literal array, process each resource url individually and
31072 // register ones that are string literals.
31073 const styleUrlsExpr = component.get('styleUrls');
31074 if (styleUrlsExpr !== undefined && ts__default["default"].isArrayLiteralExpression(styleUrlsExpr)) {
31075 for (const expression of stringLiteralElements(styleUrlsExpr)) {
31076 try {
31077 const resourceUrl = this.resourceLoader.resolve(expression.text, containingFile);
31078 styles.add({ path: absoluteFrom(resourceUrl), expression });
31079 }
31080 catch {
31081 // Errors in style resource extraction do not need to be handled here. We will produce
31082 // diagnostics for each one that fails in the analysis, after we evaluate the `styleUrls`
31083 // expression to determine _all_ style resources, not just the string literals.
31084 }
31085 }
31086 }
31087 const stylesExpr = component.get('styles');
31088 if (stylesExpr !== undefined && ts__default["default"].isArrayLiteralExpression(stylesExpr)) {
31089 for (const expression of stringLiteralElements(stylesExpr)) {
31090 styles.add({ path: null, expression });
31091 }
31092 }
31093 return styles;
31094 }
31095 _preloadAndParseTemplate(node, decorator, component, containingFile) {
31096 if (component.has('templateUrl')) {
31097 // Extract the templateUrl and preload it.
31098 const templateUrlExpr = component.get('templateUrl');
31099 const templateUrl = this.evaluator.evaluate(templateUrlExpr);
31100 if (typeof templateUrl !== 'string') {
31101 throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
31102 }
31103 try {
31104 const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile);
31105 const templatePromise = this.resourceLoader.preload(resourceUrl, { type: 'template', containingFile });
31106 // If the preload worked, then actually load and parse the template, and wait for any style
31107 // URLs to resolve.
31108 if (templatePromise !== undefined) {
31109 return templatePromise.then(() => {
31110 const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
31111 const template = this.extractTemplate(node, templateDecl);
31112 this.preanalyzeTemplateCache.set(node, template);
31113 return template;
31114 });
31115 }
31116 else {
31117 return Promise.resolve(null);
31118 }
31119 }
31120 catch (e) {
31121 throw this.makeResourceNotFoundError(templateUrl, templateUrlExpr, 0 /* Template */);
31122 }
31123 }
31124 else {
31125 const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
31126 const template = this.extractTemplate(node, templateDecl);
31127 this.preanalyzeTemplateCache.set(node, template);
31128 return Promise.resolve(template);
31129 }
31130 }
31131 extractTemplate(node, template) {
31132 if (template.isInline) {
31133 let sourceStr;
31134 let sourceParseRange = null;
31135 let templateContent;
31136 let sourceMapping;
31137 let escapedString = false;
31138 let sourceMapUrl;
31139 // We only support SourceMaps for inline templates that are simple string literals.
31140 if (ts__default["default"].isStringLiteral(template.expression) ||
31141 ts__default["default"].isNoSubstitutionTemplateLiteral(template.expression)) {
31142 // the start and end of the `templateExpr` node includes the quotation marks, which we must
31143 // strip
31144 sourceParseRange = getTemplateRange(template.expression);
31145 sourceStr = template.expression.getSourceFile().text;
31146 templateContent = template.expression.text;
31147 escapedString = true;
31148 sourceMapping = {
31149 type: 'direct',
31150 node: template.expression,
31151 };
31152 sourceMapUrl = template.resolvedTemplateUrl;
31153 }
31154 else {
31155 const resolvedTemplate = this.evaluator.evaluate(template.expression);
31156 if (typeof resolvedTemplate !== 'string') {
31157 throw createValueHasWrongTypeError(template.expression, resolvedTemplate, 'template must be a string');
31158 }
31159 // We do not parse the template directly from the source file using a lexer range, so
31160 // the template source and content are set to the statically resolved template.
31161 sourceStr = resolvedTemplate;
31162 templateContent = resolvedTemplate;
31163 sourceMapping = {
31164 type: 'indirect',
31165 node: template.expression,
31166 componentClass: node,
31167 template: templateContent,
31168 };
31169 // Indirect templates cannot be mapped to a particular byte range of any input file, since
31170 // they're computed by expressions that may span many files. Don't attempt to map them back
31171 // to a given file.
31172 sourceMapUrl = null;
31173 }
31174 return {
31175 ...this._parseTemplate(template, sourceStr, sourceParseRange, escapedString, sourceMapUrl),
31176 content: templateContent,
31177 sourceMapping,
31178 declaration: template,
31179 };
31180 }
31181 else {
31182 const templateContent = this.resourceLoader.load(template.resolvedTemplateUrl);
31183 if (this.depTracker !== null) {
31184 this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(template.resolvedTemplateUrl));
31185 }
31186 return {
31187 ...this._parseTemplate(template, /* sourceStr */ templateContent, /* sourceParseRange */ null,
31188 /* escapedString */ false,
31189 /* sourceMapUrl */ template.resolvedTemplateUrl),
31190 content: templateContent,
31191 sourceMapping: {
31192 type: 'external',
31193 componentClass: node,
31194 // TODO(alxhub): TS in g3 is unable to make this inference on its own, so cast it here
31195 // until g3 is able to figure this out.
31196 node: template.templateUrlExpression,
31197 template: templateContent,
31198 templateUrl: template.resolvedTemplateUrl,
31199 },
31200 declaration: template,
31201 };
31202 }
31203 }
31204 _parseTemplate(template, sourceStr, sourceParseRange, escapedString, sourceMapUrl) {
31205 // We always normalize line endings if the template has been escaped (i.e. is inline).
31206 const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs;
31207 const parsedTemplate = parseTemplate(sourceStr, sourceMapUrl ?? '', {
31208 preserveWhitespaces: template.preserveWhitespaces,
31209 interpolationConfig: template.interpolationConfig,
31210 range: sourceParseRange ?? undefined,
31211 escapedString,
31212 enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
31213 i18nNormalizeLineEndingsInICUs,
31214 alwaysAttemptHtmlToR3AstConversion: this.usePoisonedData,
31215 });
31216 // Unfortunately, the primary parse of the template above may not contain accurate source map
31217 // information. If used directly, it would result in incorrect code locations in template
31218 // errors, etc. There are three main problems:
31219 //
31220 // 1. `preserveWhitespaces: false` annihilates the correctness of template source mapping, as
31221 // the whitespace transformation changes the contents of HTML text nodes before they're
31222 // parsed into Angular expressions.
31223 // 2. `preserveLineEndings: false` causes growing misalignments in templates that use '\r\n'
31224 // line endings, by normalizing them to '\n'.
31225 // 3. By default, the template parser strips leading trivia characters (like spaces, tabs, and
31226 // newlines). This also destroys source mapping information.
31227 //
31228 // In order to guarantee the correctness of diagnostics, templates are parsed a second time
31229 // with the above options set to preserve source mappings.
31230 const { nodes: diagNodes } = parseTemplate(sourceStr, sourceMapUrl ?? '', {
31231 preserveWhitespaces: true,
31232 preserveLineEndings: true,
31233 interpolationConfig: template.interpolationConfig,
31234 range: sourceParseRange ?? undefined,
31235 escapedString,
31236 enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
31237 i18nNormalizeLineEndingsInICUs,
31238 leadingTriviaChars: [],
31239 alwaysAttemptHtmlToR3AstConversion: this.usePoisonedData,
31240 });
31241 return {
31242 ...parsedTemplate,
31243 diagNodes,
31244 file: new ParseSourceFile(sourceStr, sourceMapUrl ?? ''),
31245 };
31246 }
31247 parseTemplateDeclaration(decorator, component, containingFile) {
31248 let preserveWhitespaces = this.defaultPreserveWhitespaces;
31249 if (component.has('preserveWhitespaces')) {
31250 const expr = component.get('preserveWhitespaces');
31251 const value = this.evaluator.evaluate(expr);
31252 if (typeof value !== 'boolean') {
31253 throw createValueHasWrongTypeError(expr, value, 'preserveWhitespaces must be a boolean');
31254 }
31255 preserveWhitespaces = value;
31256 }
31257 let interpolationConfig = DEFAULT_INTERPOLATION_CONFIG;
31258 if (component.has('interpolation')) {
31259 const expr = component.get('interpolation');
31260 const value = this.evaluator.evaluate(expr);
31261 if (!Array.isArray(value) || value.length !== 2 ||
31262 !value.every(element => typeof element === 'string')) {
31263 throw createValueHasWrongTypeError(expr, value, 'interpolation must be an array with 2 elements of string type');
31264 }
31265 interpolationConfig = InterpolationConfig.fromArray(value);
31266 }
31267 if (component.has('templateUrl')) {
31268 const templateUrlExpr = component.get('templateUrl');
31269 const templateUrl = this.evaluator.evaluate(templateUrlExpr);
31270 if (typeof templateUrl !== 'string') {
31271 throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
31272 }
31273 try {
31274 const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile);
31275 return {
31276 isInline: false,
31277 interpolationConfig,
31278 preserveWhitespaces,
31279 templateUrl,
31280 templateUrlExpression: templateUrlExpr,
31281 resolvedTemplateUrl: resourceUrl,
31282 };
31283 }
31284 catch (e) {
31285 throw this.makeResourceNotFoundError(templateUrl, templateUrlExpr, 0 /* Template */);
31286 }
31287 }
31288 else if (component.has('template')) {
31289 return {
31290 isInline: true,
31291 interpolationConfig,
31292 preserveWhitespaces,
31293 expression: component.get('template'),
31294 templateUrl: containingFile,
31295 resolvedTemplateUrl: containingFile,
31296 };
31297 }
31298 else {
31299 throw new FatalDiagnosticError(ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), 'component is missing a template');
31300 }
31301 }
31302 _resolveImportedFile(importedFile, expr, origin) {
31303 // If `importedFile` is not 'unknown' then it accurately reflects the source file that is
31304 // being imported.
31305 if (importedFile !== 'unknown') {
31306 return importedFile;
31307 }
31308 // Otherwise `expr` has to be inspected to determine the file that is being imported. If `expr`
31309 // is not an `ExternalExpr` then it does not correspond with an import, so return null in that
31310 // case.
31311 if (!(expr instanceof ExternalExpr)) {
31312 return null;
31313 }
31314 // Figure out what file is being imported.
31315 return this.moduleResolver.resolveModule(expr.value.moduleName, origin.fileName);
31316 }
31317 /**
31318 * Check whether adding an import from `origin` to the source-file corresponding to `expr` would
31319 * create a cyclic import.
31320 *
31321 * @returns a `Cycle` object if a cycle would be created, otherwise `null`.
31322 */
31323 _checkForCyclicImport(importedFile, expr, origin) {
31324 const imported = this._resolveImportedFile(importedFile, expr, origin);
31325 if (imported === null) {
31326 return null;
31327 }
31328 // Check whether the import is legal.
31329 return this.cycleAnalyzer.wouldCreateCycle(origin, imported);
31330 }
31331 _recordSyntheticImport(importedFile, expr, origin) {
31332 const imported = this._resolveImportedFile(importedFile, expr, origin);
31333 if (imported === null) {
31334 return;
31335 }
31336 this.cycleAnalyzer.recordSyntheticImport(origin, imported);
31337 }
31338 makeResourceNotFoundError(file, nodeForError, resourceType) {
31339 let errorText;
31340 switch (resourceType) {
31341 case 0 /* Template */:
31342 errorText = `Could not find template file '${file}'.`;
31343 break;
31344 case 1 /* StylesheetFromTemplate */:
31345 errorText = `Could not find stylesheet file '${file}' linked from the template.`;
31346 break;
31347 case 2 /* StylesheetFromDecorator */:
31348 errorText = `Could not find stylesheet file '${file}'.`;
31349 break;
31350 }
31351 return new FatalDiagnosticError(ErrorCode.COMPONENT_RESOURCE_NOT_FOUND, nodeForError, errorText);
31352 }
31353 _extractTemplateStyleUrls(template) {
31354 if (template.styleUrls === null) {
31355 return [];
31356 }
31357 const nodeForError = getTemplateDeclarationNodeForError(template.declaration);
31358 return template.styleUrls.map(url => ({ url, source: 1 /* StylesheetFromTemplate */, nodeForError }));
31359 }
31360 }
31361 function getTemplateRange(templateExpr) {
31362 const startPos = templateExpr.getStart() + 1;
31363 const { line, character } = ts__default["default"].getLineAndCharacterOfPosition(templateExpr.getSourceFile(), startPos);
31364 return {
31365 startPos,
31366 startLine: line,
31367 startCol: character,
31368 endPos: templateExpr.getEnd() - 1,
31369 };
31370 }
31371 /** Determines if the result of an evaluation is a string array. */
31372 function isStringArray(resolvedValue) {
31373 return Array.isArray(resolvedValue) && resolvedValue.every(elem => typeof elem === 'string');
31374 }
31375 /** Determines the node to use for debugging purposes for the given TemplateDeclaration. */
31376 function getTemplateDeclarationNodeForError(declaration) {
31377 // TODO(zarend): Change this to if/else when that is compatible with g3. This uses a switch
31378 // because if/else fails to compile on g3. That is because g3 compiles this in non-strict mode
31379 // where type inference does not work correctly.
31380 switch (declaration.isInline) {
31381 case true:
31382 return declaration.expression;
31383 case false:
31384 return declaration.templateUrlExpression;
31385 }
31386 }
31387 /**
31388 * Generate a diagnostic related information object that describes a potential cyclic import path.
31389 */
31390 function makeCyclicImportInfo(ref, type, cycle) {
31391 const name = ref.debugName || '(unknown)';
31392 const path = cycle.getPath().map(sf => sf.fileName).join(' -> ');
31393 const message = `The ${type} '${name}' is used in the template but importing it would create a cycle: `;
31394 return makeRelatedInformation(ref.node, message + path);
31395 }
31396 /**
31397 * Checks whether a selector is a valid custom element tag name.
31398 * Based loosely on https://github.com/sindresorhus/validate-element-name.
31399 */
31400 function checkCustomElementSelectorForErrors(selector) {
31401 // Avoid flagging components with an attribute or class selector. This isn't bulletproof since it
31402 // won't catch cases like `foo[]bar`, but we don't need it to be. This is mainly to avoid flagging
31403 // something like `foo-bar[baz]` incorrectly.
31404 if (selector.includes('.') || (selector.includes('[') && selector.includes(']'))) {
31405 return null;
31406 }
31407 if (!(/^[a-z]/.test(selector))) {
31408 return 'Selector of a ShadowDom-encapsulated component must start with a lower case letter.';
31409 }
31410 if (/[A-Z]/.test(selector)) {
31411 return 'Selector of a ShadowDom-encapsulated component must all be in lower case.';
31412 }
31413 if (!selector.includes('-')) {
31414 return 'Selector of a component that uses ViewEncapsulation.ShadowDom must contain a hyphen.';
31415 }
31416 return null;
31417 }
31418
31419 /**
31420 * @license
31421 * Copyright Google LLC All Rights Reserved.
31422 *
31423 * Use of this source code is governed by an MIT-style license that can be
31424 * found in the LICENSE file at https://angular.io/license
31425 */
31426 /**
31427 * Adapts the `compileInjectable` compiler for `@Injectable` decorators to the Ivy compiler.
31428 */
31429 class InjectableDecoratorHandler {
31430 constructor(reflector, isCore, strictCtorDeps, injectableRegistry, perf,
31431 /**
31432 * What to do if the injectable already contains a ɵprov property.
31433 *
31434 * If true then an error diagnostic is reported.
31435 * If false then there is no error and a new ɵprov property is not added.
31436 */
31437 errorOnDuplicateProv = true) {
31438 this.reflector = reflector;
31439 this.isCore = isCore;
31440 this.strictCtorDeps = strictCtorDeps;
31441 this.injectableRegistry = injectableRegistry;
31442 this.perf = perf;
31443 this.errorOnDuplicateProv = errorOnDuplicateProv;
31444 this.precedence = HandlerPrecedence.SHARED;
31445 this.name = InjectableDecoratorHandler.name;
31446 }
31447 detect(node, decorators) {
31448 if (!decorators) {
31449 return undefined;
31450 }
31451 const decorator = findAngularDecorator(decorators, 'Injectable', this.isCore);
31452 if (decorator !== undefined) {
31453 return {
31454 trigger: decorator.node,
31455 decorator: decorator,
31456 metadata: decorator,
31457 };
31458 }
31459 else {
31460 return undefined;
31461 }
31462 }
31463 analyze(node, decorator) {
31464 this.perf.eventCount(PerfEvent.AnalyzeInjectable);
31465 const meta = extractInjectableMetadata(node, decorator, this.reflector);
31466 const decorators = this.reflector.getDecoratorsOfDeclaration(node);
31467 return {
31468 analysis: {
31469 meta,
31470 ctorDeps: extractInjectableCtorDeps(node, meta, decorator, this.reflector, this.isCore, this.strictCtorDeps),
31471 classMetadata: extractClassMetadata(node, this.reflector, this.isCore),
31472 // Avoid generating multiple factories if a class has
31473 // more Angular decorators, apart from Injectable.
31474 needsFactory: !decorators ||
31475 decorators.every(current => !isAngularCore(current) || current.name === 'Injectable')
31476 },
31477 };
31478 }
31479 symbol() {
31480 return null;
31481 }
31482 register(node) {
31483 this.injectableRegistry.registerInjectable(node);
31484 }
31485 compileFull(node, analysis) {
31486 return this.compile(compileNgFactoryDefField, meta => compileInjectable(meta, false), compileClassMetadata, node, analysis);
31487 }
31488 compilePartial(node, analysis) {
31489 return this.compile(compileDeclareFactory, compileDeclareInjectableFromMetadata, compileDeclareClassMetadata, node, analysis);
31490 }
31491 compile(compileFactoryFn, compileInjectableFn, compileClassMetadataFn, node, analysis) {
31492 const results = [];
31493 if (analysis.needsFactory) {
31494 const meta = analysis.meta;
31495 const factoryRes = compileFactoryFn(toFactoryMetadata({ ...meta, deps: analysis.ctorDeps }, FactoryTarget$1.Injectable));
31496 if (analysis.classMetadata !== null) {
31497 factoryRes.statements.push(compileClassMetadataFn(analysis.classMetadata).toStmt());
31498 }
31499 results.push(factoryRes);
31500 }
31501 const ɵprov = this.reflector.getMembersOfClass(node).find(member => member.name === 'ɵprov');
31502 if (ɵprov !== undefined && this.errorOnDuplicateProv) {
31503 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.');
31504 }
31505 if (ɵprov === undefined) {
31506 // Only add a new ɵprov if there is not one already
31507 const res = compileInjectableFn(analysis.meta);
31508 results.push({ name: 'ɵprov', initializer: res.expression, statements: res.statements, type: res.type });
31509 }
31510 return results;
31511 }
31512 }
31513 /**
31514 * Read metadata from the `@Injectable` decorator and produce the `IvyInjectableMetadata`, the
31515 * input metadata needed to run `compileInjectable`.
31516 *
31517 * A `null` return value indicates this is @Injectable has invalid data.
31518 */
31519 function extractInjectableMetadata(clazz, decorator, reflector) {
31520 const name = clazz.name.text;
31521 const type = wrapTypeReference(reflector, clazz);
31522 const internalType = new WrappedNodeExpr(reflector.getInternalNameOfClass(clazz));
31523 const typeArgumentCount = reflector.getGenericArityOfClass(clazz) || 0;
31524 if (decorator.args === null) {
31525 throw new FatalDiagnosticError(ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), '@Injectable must be called');
31526 }
31527 if (decorator.args.length === 0) {
31528 return {
31529 name,
31530 type,
31531 typeArgumentCount,
31532 internalType,
31533 providedIn: createMayBeForwardRefExpression(new LiteralExpr(null), 0 /* None */),
31534 };
31535 }
31536 else if (decorator.args.length === 1) {
31537 const metaNode = decorator.args[0];
31538 // Firstly make sure the decorator argument is an inline literal - if not, it's illegal to
31539 // transport references from one location to another. This is the problem that lowering
31540 // used to solve - if this restriction proves too undesirable we can re-implement lowering.
31541 if (!ts__default["default"].isObjectLiteralExpression(metaNode)) {
31542 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, metaNode, `@Injectable argument must be an object literal`);
31543 }
31544 // Resolve the fields of the literal into a map of field name to expression.
31545 const meta = reflectObjectLiteral(metaNode);
31546 const providedIn = meta.has('providedIn') ?
31547 getProviderExpression(meta.get('providedIn'), reflector) :
31548 createMayBeForwardRefExpression(new LiteralExpr(null), 0 /* None */);
31549 let deps = undefined;
31550 if ((meta.has('useClass') || meta.has('useFactory')) && meta.has('deps')) {
31551 const depsExpr = meta.get('deps');
31552 if (!ts__default["default"].isArrayLiteralExpression(depsExpr)) {
31553 throw new FatalDiagnosticError(ErrorCode.VALUE_NOT_LITERAL, depsExpr, `@Injectable deps metadata must be an inline array`);
31554 }
31555 deps = depsExpr.elements.map(dep => getDep(dep, reflector));
31556 }
31557 const result = { name, type, typeArgumentCount, internalType, providedIn };
31558 if (meta.has('useValue')) {
31559 result.useValue = getProviderExpression(meta.get('useValue'), reflector);
31560 }
31561 else if (meta.has('useExisting')) {
31562 result.useExisting = getProviderExpression(meta.get('useExisting'), reflector);
31563 }
31564 else if (meta.has('useClass')) {
31565 result.useClass = getProviderExpression(meta.get('useClass'), reflector);
31566 result.deps = deps;
31567 }
31568 else if (meta.has('useFactory')) {
31569 result.useFactory = new WrappedNodeExpr(meta.get('useFactory'));
31570 result.deps = deps;
31571 }
31572 return result;
31573 }
31574 else {
31575 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, decorator.args[2], 'Too many arguments to @Injectable');
31576 }
31577 }
31578 /**
31579 * Get the `R3ProviderExpression` for this `expression`.
31580 *
31581 * The `useValue`, `useExisting` and `useClass` properties might be wrapped in a `ForwardRef`, which
31582 * needs to be unwrapped. This function will do that unwrapping and set a flag on the returned
31583 * object to indicate whether the value needed unwrapping.
31584 */
31585 function getProviderExpression(expression, reflector) {
31586 const forwardRefValue = tryUnwrapForwardRef(expression, reflector);
31587 return createMayBeForwardRefExpression(new WrappedNodeExpr(forwardRefValue ?? expression), forwardRefValue !== null ? 2 /* Unwrapped */ : 0 /* None */);
31588 }
31589 function extractInjectableCtorDeps(clazz, meta, decorator, reflector, isCore, strictCtorDeps) {
31590 if (decorator.args === null) {
31591 throw new FatalDiagnosticError(ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), '@Injectable must be called');
31592 }
31593 let ctorDeps = null;
31594 if (decorator.args.length === 0) {
31595 // Ideally, using @Injectable() would have the same effect as using @Injectable({...}), and be
31596 // subject to the same validation. However, existing Angular code abuses @Injectable, applying
31597 // it to things like abstract classes with constructors that were never meant for use with
31598 // Angular's DI.
31599 //
31600 // To deal with this, @Injectable() without an argument is more lenient, and if the
31601 // constructor signature does not work for DI then a factory definition (ɵfac) that throws is
31602 // generated.
31603 if (strictCtorDeps) {
31604 ctorDeps = getValidConstructorDependencies(clazz, reflector, isCore);
31605 }
31606 else {
31607 ctorDeps =
31608 unwrapConstructorDependencies(getConstructorDependencies(clazz, reflector, isCore));
31609 }
31610 return ctorDeps;
31611 }
31612 else if (decorator.args.length === 1) {
31613 const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore);
31614 if (strictCtorDeps && meta.useValue === undefined && meta.useExisting === undefined &&
31615 meta.useClass === undefined && meta.useFactory === undefined) {
31616 // Since use* was not provided, validate the deps according to strictCtorDeps.
31617 ctorDeps = validateConstructorDependencies(clazz, rawCtorDeps);
31618 }
31619 else {
31620 ctorDeps = unwrapConstructorDependencies(rawCtorDeps);
31621 }
31622 }
31623 return ctorDeps;
31624 }
31625 function getDep(dep, reflector) {
31626 const meta = {
31627 token: new WrappedNodeExpr(dep),
31628 attributeNameType: null,
31629 host: false,
31630 optional: false,
31631 self: false,
31632 skipSelf: false,
31633 };
31634 function maybeUpdateDecorator(dec, reflector, token) {
31635 const source = reflector.getImportOfIdentifier(dec);
31636 if (source === null || source.from !== '@angular/core') {
31637 return false;
31638 }
31639 switch (source.name) {
31640 case 'Inject':
31641 if (token !== undefined) {
31642 meta.token = new WrappedNodeExpr(token);
31643 }
31644 break;
31645 case 'Optional':
31646 meta.optional = true;
31647 break;
31648 case 'SkipSelf':
31649 meta.skipSelf = true;
31650 break;
31651 case 'Self':
31652 meta.self = true;
31653 break;
31654 default:
31655 return false;
31656 }
31657 return true;
31658 }
31659 if (ts__default["default"].isArrayLiteralExpression(dep)) {
31660 dep.elements.forEach(el => {
31661 let isDecorator = false;
31662 if (ts__default["default"].isIdentifier(el)) {
31663 isDecorator = maybeUpdateDecorator(el, reflector);
31664 }
31665 else if (ts__default["default"].isNewExpression(el) && ts__default["default"].isIdentifier(el.expression)) {
31666 const token = el.arguments && el.arguments.length > 0 && el.arguments[0] || undefined;
31667 isDecorator = maybeUpdateDecorator(el.expression, reflector, token);
31668 }
31669 if (!isDecorator) {
31670 meta.token = new WrappedNodeExpr(el);
31671 }
31672 });
31673 }
31674 return meta;
31675 }
31676
31677 /**
31678 * @license
31679 * Copyright Google LLC All Rights Reserved.
31680 *
31681 * Use of this source code is governed by an MIT-style license that can be
31682 * found in the LICENSE file at https://angular.io/license
31683 */
31684 /**
31685 * Represents an Angular pipe.
31686 */
31687 class PipeSymbol extends SemanticSymbol {
31688 constructor(decl, name) {
31689 super(decl);
31690 this.name = name;
31691 }
31692 isPublicApiAffected(previousSymbol) {
31693 if (!(previousSymbol instanceof PipeSymbol)) {
31694 return true;
31695 }
31696 return this.name !== previousSymbol.name;
31697 }
31698 isTypeCheckApiAffected(previousSymbol) {
31699 return this.isPublicApiAffected(previousSymbol);
31700 }
31701 }
31702 class PipeDecoratorHandler {
31703 constructor(reflector, evaluator, metaRegistry, scopeRegistry, injectableRegistry, isCore, perf) {
31704 this.reflector = reflector;
31705 this.evaluator = evaluator;
31706 this.metaRegistry = metaRegistry;
31707 this.scopeRegistry = scopeRegistry;
31708 this.injectableRegistry = injectableRegistry;
31709 this.isCore = isCore;
31710 this.perf = perf;
31711 this.precedence = HandlerPrecedence.PRIMARY;
31712 this.name = PipeDecoratorHandler.name;
31713 }
31714 detect(node, decorators) {
31715 if (!decorators) {
31716 return undefined;
31717 }
31718 const decorator = findAngularDecorator(decorators, 'Pipe', this.isCore);
31719 if (decorator !== undefined) {
31720 return {
31721 trigger: decorator.node,
31722 decorator: decorator,
31723 metadata: decorator,
31724 };
31725 }
31726 else {
31727 return undefined;
31728 }
31729 }
31730 analyze(clazz, decorator) {
31731 this.perf.eventCount(PerfEvent.AnalyzePipe);
31732 const name = clazz.name.text;
31733 const type = wrapTypeReference(this.reflector, clazz);
31734 const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(clazz));
31735 if (decorator.args === null) {
31736 throw new FatalDiagnosticError(ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), `@Pipe must be called`);
31737 }
31738 if (decorator.args.length !== 1) {
31739 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), '@Pipe must have exactly one argument');
31740 }
31741 const meta = unwrapExpression(decorator.args[0]);
31742 if (!ts__default["default"].isObjectLiteralExpression(meta)) {
31743 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@Pipe must have a literal argument');
31744 }
31745 const pipe = reflectObjectLiteral(meta);
31746 if (!pipe.has('name')) {
31747 throw new FatalDiagnosticError(ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`);
31748 }
31749 const pipeNameExpr = pipe.get('name');
31750 const pipeName = this.evaluator.evaluate(pipeNameExpr);
31751 if (typeof pipeName !== 'string') {
31752 throw createValueHasWrongTypeError(pipeNameExpr, pipeName, `@Pipe.name must be a string`);
31753 }
31754 let pure = true;
31755 if (pipe.has('pure')) {
31756 const expr = pipe.get('pure');
31757 const pureValue = this.evaluator.evaluate(expr);
31758 if (typeof pureValue !== 'boolean') {
31759 throw createValueHasWrongTypeError(expr, pureValue, `@Pipe.pure must be a boolean`);
31760 }
31761 pure = pureValue;
31762 }
31763 return {
31764 analysis: {
31765 meta: {
31766 name,
31767 type,
31768 internalType,
31769 typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0,
31770 pipeName,
31771 deps: getValidConstructorDependencies(clazz, this.reflector, this.isCore),
31772 pure,
31773 },
31774 classMetadata: extractClassMetadata(clazz, this.reflector, this.isCore),
31775 pipeNameExpr,
31776 },
31777 };
31778 }
31779 symbol(node, analysis) {
31780 return new PipeSymbol(node, analysis.meta.name);
31781 }
31782 register(node, analysis) {
31783 const ref = new Reference(node);
31784 this.metaRegistry.registerPipeMetadata({ type: MetaType.Pipe, ref, name: analysis.meta.pipeName, nameExpr: analysis.pipeNameExpr });
31785 this.injectableRegistry.registerInjectable(node);
31786 }
31787 resolve(node) {
31788 const duplicateDeclData = this.scopeRegistry.getDuplicateDeclarations(node);
31789 if (duplicateDeclData !== null) {
31790 // This pipe was declared twice (or more).
31791 return {
31792 diagnostics: [makeDuplicateDeclarationError(node, duplicateDeclData, 'Pipe')],
31793 };
31794 }
31795 return {};
31796 }
31797 compileFull(node, analysis) {
31798 const fac = compileNgFactoryDefField(toFactoryMetadata(analysis.meta, FactoryTarget$1.Pipe));
31799 const def = compilePipeFromMetadata(analysis.meta);
31800 const classMetadata = analysis.classMetadata !== null ?
31801 compileClassMetadata(analysis.classMetadata).toStmt() :
31802 null;
31803 return compileResults(fac, def, classMetadata, 'ɵpipe');
31804 }
31805 compilePartial(node, analysis) {
31806 const fac = compileDeclareFactory(toFactoryMetadata(analysis.meta, FactoryTarget$1.Pipe));
31807 const def = compileDeclarePipeFromMetadata(analysis.meta);
31808 const classMetadata = analysis.classMetadata !== null ?
31809 compileDeclareClassMetadata(analysis.classMetadata).toStmt() :
31810 null;
31811 return compileResults(fac, def, classMetadata, 'ɵpipe');
31812 }
31813 }
31814
31815 /**
31816 * @license
31817 * Copyright Google LLC All Rights Reserved.
31818 *
31819 * Use of this source code is governed by an MIT-style license that can be
31820 * found in the LICENSE file at https://angular.io/license
31821 */
31822 /**
31823 * This registry does nothing, since ngtsc does not currently need
31824 * this functionality.
31825 * The ngcc tool implements a working version for its purposes.
31826 */
31827 class NoopReferencesRegistry {
31828 add(source, ...references) { }
31829 }
31830
31831 /**
31832 * @license
31833 * Copyright Google LLC All Rights Reserved.
31834 *
31835 * Use of this source code is governed by an MIT-style license that can be
31836 * found in the LICENSE file at https://angular.io/license
31837 */
31838 /**
31839 * Analyzes a `ts.Program` for cycles.
31840 */
31841 class CycleAnalyzer {
31842 constructor(importGraph) {
31843 this.importGraph = importGraph;
31844 /**
31845 * Cycle detection is requested with the same `from` source file for all used directives and pipes
31846 * within a component, which makes it beneficial to cache the results as long as the `from` source
31847 * file has not changed. This avoids visiting the import graph that is reachable from multiple
31848 * directives/pipes more than once.
31849 */
31850 this.cachedResults = null;
31851 }
31852 /**
31853 * Check for a cycle to be created in the `ts.Program` by adding an import between `from` and
31854 * `to`.
31855 *
31856 * @returns a `Cycle` object if an import between `from` and `to` would create a cycle; `null`
31857 * otherwise.
31858 */
31859 wouldCreateCycle(from, to) {
31860 // Try to reuse the cached results as long as the `from` source file is the same.
31861 if (this.cachedResults === null || this.cachedResults.from !== from) {
31862 this.cachedResults = new CycleResults(from, this.importGraph);
31863 }
31864 // Import of 'from' -> 'to' is illegal if an edge 'to' -> 'from' already exists.
31865 return this.cachedResults.wouldBeCyclic(to) ? new Cycle(this.importGraph, from, to) : null;
31866 }
31867 /**
31868 * Record a synthetic import from `from` to `to`.
31869 *
31870 * This is an import that doesn't exist in the `ts.Program` but will be considered as part of the
31871 * import graph for cycle creation.
31872 */
31873 recordSyntheticImport(from, to) {
31874 this.cachedResults = null;
31875 this.importGraph.addSyntheticImport(from, to);
31876 }
31877 }
31878 const NgCyclicResult = Symbol('NgCyclicResult');
31879 /**
31880 * Stores the results of cycle detection in a memory efficient manner. A symbol is attached to
31881 * source files that indicate what the cyclic analysis result is, as indicated by two markers that
31882 * are unique to this instance. This alleviates memory pressure in large import graphs, as each
31883 * execution is able to store its results in the same memory location (i.e. in the symbol
31884 * on the source file) as earlier executions.
31885 */
31886 class CycleResults {
31887 constructor(from, importGraph) {
31888 this.from = from;
31889 this.importGraph = importGraph;
31890 this.cyclic = {};
31891 this.acyclic = {};
31892 }
31893 wouldBeCyclic(sf) {
31894 const cached = this.getCachedResult(sf);
31895 if (cached !== null) {
31896 // The result for this source file has already been computed, so return its result.
31897 return cached;
31898 }
31899 if (sf === this.from) {
31900 // We have reached the source file that we want to create an import from, which means that
31901 // doing so would create a cycle.
31902 return true;
31903 }
31904 // Assume for now that the file will be acyclic; this prevents infinite recursion in the case
31905 // that `sf` is visited again as part of an existing cycle in the graph.
31906 this.markAcyclic(sf);
31907 const imports = this.importGraph.importsOf(sf);
31908 for (const imported of imports) {
31909 if (this.wouldBeCyclic(imported)) {
31910 this.markCyclic(sf);
31911 return true;
31912 }
31913 }
31914 return false;
31915 }
31916 /**
31917 * Returns whether the source file is already known to be cyclic, or `null` if the result is not
31918 * yet known.
31919 */
31920 getCachedResult(sf) {
31921 const result = sf[NgCyclicResult];
31922 if (result === this.cyclic) {
31923 return true;
31924 }
31925 else if (result === this.acyclic) {
31926 return false;
31927 }
31928 else {
31929 // Either the symbol is missing or its value does not correspond with one of the current
31930 // result markers. As such, the result is unknown.
31931 return null;
31932 }
31933 }
31934 markCyclic(sf) {
31935 sf[NgCyclicResult] = this.cyclic;
31936 }
31937 markAcyclic(sf) {
31938 sf[NgCyclicResult] = this.acyclic;
31939 }
31940 }
31941 /**
31942 * Represents an import cycle between `from` and `to` in the program.
31943 *
31944 * This class allows us to do the work to compute the cyclic path between `from` and `to` only if
31945 * needed.
31946 */
31947 class Cycle {
31948 constructor(importGraph, from, to) {
31949 this.importGraph = importGraph;
31950 this.from = from;
31951 this.to = to;
31952 }
31953 /**
31954 * Compute an array of source-files that illustrates the cyclic path between `from` and `to`.
31955 *
31956 * Note that a `Cycle` will not be created unless a path is available between `to` and `from`,
31957 * so `findPath()` will never return `null`.
31958 */
31959 getPath() {
31960 return [this.from, ...this.importGraph.findPath(this.to, this.from)];
31961 }
31962 }
31963
31964 /**
31965 * @license
31966 * Copyright Google LLC All Rights Reserved.
31967 *
31968 * Use of this source code is governed by an MIT-style license that can be
31969 * found in the LICENSE file at https://angular.io/license
31970 */
31971 /**
31972 * A cached graph of imports in the `ts.Program`.
31973 *
31974 * The `ImportGraph` keeps track of dependencies (imports) of individual `ts.SourceFile`s. Only
31975 * dependencies within the same program are tracked; imports into packages on NPM are not.
31976 */
31977 class ImportGraph {
31978 constructor(checker, perf) {
31979 this.checker = checker;
31980 this.perf = perf;
31981 this.imports = new Map();
31982 }
31983 /**
31984 * List the direct (not transitive) imports of a given `ts.SourceFile`.
31985 *
31986 * This operation is cached.
31987 */
31988 importsOf(sf) {
31989 if (!this.imports.has(sf)) {
31990 this.imports.set(sf, this.scanImports(sf));
31991 }
31992 return this.imports.get(sf);
31993 }
31994 /**
31995 * Find an import path from the `start` SourceFile to the `end` SourceFile.
31996 *
31997 * This function implements a breadth first search that results in finding the
31998 * shortest path between the `start` and `end` points.
31999 *
32000 * @param start the starting point of the path.
32001 * @param end the ending point of the path.
32002 * @returns an array of source files that connect the `start` and `end` source files, or `null` if
32003 * no path could be found.
32004 */
32005 findPath(start, end) {
32006 if (start === end) {
32007 // Escape early for the case where `start` and `end` are the same.
32008 return [start];
32009 }
32010 const found = new Set([start]);
32011 const queue = [new Found(start, null)];
32012 while (queue.length > 0) {
32013 const current = queue.shift();
32014 const imports = this.importsOf(current.sourceFile);
32015 for (const importedFile of imports) {
32016 if (!found.has(importedFile)) {
32017 const next = new Found(importedFile, current);
32018 if (next.sourceFile === end) {
32019 // We have hit the target `end` path so we can stop here.
32020 return next.toPath();
32021 }
32022 found.add(importedFile);
32023 queue.push(next);
32024 }
32025 }
32026 }
32027 return null;
32028 }
32029 /**
32030 * Add a record of an import from `sf` to `imported`, that's not present in the original
32031 * `ts.Program` but will be remembered by the `ImportGraph`.
32032 */
32033 addSyntheticImport(sf, imported) {
32034 if (isLocalFile(imported)) {
32035 this.importsOf(sf).add(imported);
32036 }
32037 }
32038 scanImports(sf) {
32039 return this.perf.inPhase(PerfPhase.CycleDetection, () => {
32040 const imports = new Set();
32041 // Look through the source file for import and export statements.
32042 for (const stmt of sf.statements) {
32043 if ((!ts__default["default"].isImportDeclaration(stmt) && !ts__default["default"].isExportDeclaration(stmt)) ||
32044 stmt.moduleSpecifier === undefined) {
32045 continue;
32046 }
32047 if (ts__default["default"].isImportDeclaration(stmt) && stmt.importClause !== undefined &&
32048 stmt.importClause.isTypeOnly) {
32049 // Exclude type-only imports as they are always elided, so they don't contribute to
32050 // cycles.
32051 continue;
32052 }
32053 const symbol = this.checker.getSymbolAtLocation(stmt.moduleSpecifier);
32054 if (symbol === undefined || symbol.valueDeclaration === undefined) {
32055 // No symbol could be found to skip over this import/export.
32056 continue;
32057 }
32058 const moduleFile = symbol.valueDeclaration;
32059 if (ts__default["default"].isSourceFile(moduleFile) && isLocalFile(moduleFile)) {
32060 // Record this local import.
32061 imports.add(moduleFile);
32062 }
32063 }
32064 return imports;
32065 });
32066 }
32067 }
32068 function isLocalFile(sf) {
32069 return !sf.isDeclarationFile;
32070 }
32071 /**
32072 * A helper class to track which SourceFiles are being processed when searching for a path in
32073 * `getPath()` above.
32074 */
32075 class Found {
32076 constructor(sourceFile, parent) {
32077 this.sourceFile = sourceFile;
32078 this.parent = parent;
32079 }
32080 /**
32081 * Back track through this found SourceFile and its ancestors to generate an array of
32082 * SourceFiles that form am import path between two SourceFiles.
32083 */
32084 toPath() {
32085 const array = [];
32086 let current = this;
32087 while (current !== null) {
32088 array.push(current.sourceFile);
32089 current = current.parent;
32090 }
32091 // Pushing and then reversing, O(n), rather than unshifting repeatedly, O(n^2), avoids
32092 // manipulating the array on every iteration: https://stackoverflow.com/a/26370620
32093 return array.reverse();
32094 }
32095 }
32096
32097 /**
32098 * @license
32099 * Copyright Google LLC All Rights Reserved.
32100 *
32101 * Use of this source code is governed by an MIT-style license that can be
32102 * found in the LICENSE file at https://angular.io/license
32103 */
32104 /**
32105 * Produce `ts.Diagnostic`s for classes that are visible from exported types (e.g. directives
32106 * exposed by exported `NgModule`s) that are not themselves exported.
32107 *
32108 * This function reconciles two concepts:
32109 *
32110 * A class is Exported if it's exported from the main library `entryPoint` file.
32111 * A class is Visible if, via Angular semantics, a downstream consumer can import an Exported class
32112 * and be affected by the class in question. For example, an Exported NgModule may expose a
32113 * directive class to its consumers. Consumers that import the NgModule may have the directive
32114 * applied to elements in their templates. In this case, the directive is considered Visible.
32115 *
32116 * `checkForPrivateExports` attempts to verify that all Visible classes are Exported, and report
32117 * `ts.Diagnostic`s for those that aren't.
32118 *
32119 * @param entryPoint `ts.SourceFile` of the library's entrypoint, which should export the library's
32120 * public API.
32121 * @param checker `ts.TypeChecker` for the current program.
32122 * @param refGraph `ReferenceGraph` tracking the visibility of Angular types.
32123 * @returns an array of `ts.Diagnostic`s representing errors when visible classes are not exported
32124 * properly.
32125 */
32126 function checkForPrivateExports(entryPoint, checker, refGraph) {
32127 const diagnostics = [];
32128 // Firstly, compute the exports of the entry point. These are all the Exported classes.
32129 const topLevelExports = new Set();
32130 // Do this via `ts.TypeChecker.getExportsOfModule`.
32131 const moduleSymbol = checker.getSymbolAtLocation(entryPoint);
32132 if (moduleSymbol === undefined) {
32133 throw new Error(`Internal error: failed to get symbol for entrypoint`);
32134 }
32135 const exportedSymbols = checker.getExportsOfModule(moduleSymbol);
32136 // Loop through the exported symbols, de-alias if needed, and add them to `topLevelExports`.
32137 // TODO(alxhub): use proper iteration when build.sh is removed. (#27762)
32138 exportedSymbols.forEach(symbol => {
32139 if (symbol.flags & ts__default["default"].SymbolFlags.Alias) {
32140 symbol = checker.getAliasedSymbol(symbol);
32141 }
32142 const decl = symbol.valueDeclaration;
32143 if (decl !== undefined) {
32144 topLevelExports.add(decl);
32145 }
32146 });
32147 // Next, go through each exported class and expand it to the set of classes it makes Visible,
32148 // using the `ReferenceGraph`. For each Visible class, verify that it's also Exported, and queue
32149 // an error if it isn't. `checkedSet` ensures only one error is queued per class.
32150 const checkedSet = new Set();
32151 // Loop through each Exported class.
32152 // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
32153 topLevelExports.forEach(mainExport => {
32154 // Loop through each class made Visible by the Exported class.
32155 refGraph.transitiveReferencesOf(mainExport).forEach(transitiveReference => {
32156 // Skip classes which have already been checked.
32157 if (checkedSet.has(transitiveReference)) {
32158 return;
32159 }
32160 checkedSet.add(transitiveReference);
32161 // Verify that the Visible class is also Exported.
32162 if (!topLevelExports.has(transitiveReference)) {
32163 // This is an error, `mainExport` makes `transitiveReference` Visible, but
32164 // `transitiveReference` is not Exported from the entrypoint. Construct a diagnostic to
32165 // give to the user explaining the situation.
32166 const descriptor = getDescriptorOfDeclaration(transitiveReference);
32167 const name = getNameOfDeclaration(transitiveReference);
32168 // Construct the path of visibility, from `mainExport` to `transitiveReference`.
32169 let visibleVia = 'NgModule exports';
32170 const transitivePath = refGraph.pathFrom(mainExport, transitiveReference);
32171 if (transitivePath !== null) {
32172 visibleVia = transitivePath.map(seg => getNameOfDeclaration(seg)).join(' -> ');
32173 }
32174 const diagnostic = {
32175 category: ts__default["default"].DiagnosticCategory.Error,
32176 code: ngErrorCode(ErrorCode.SYMBOL_NOT_EXPORTED),
32177 file: transitiveReference.getSourceFile(),
32178 ...getPosOfDeclaration(transitiveReference),
32179 messageText: `Unsupported private ${descriptor} ${name}. This ${descriptor} is visible to consumers via ${visibleVia}, but is not exported from the top-level library entrypoint.`,
32180 };
32181 diagnostics.push(diagnostic);
32182 }
32183 });
32184 });
32185 return diagnostics;
32186 }
32187 function getPosOfDeclaration(decl) {
32188 const node = getIdentifierOfDeclaration(decl) || decl;
32189 return {
32190 start: node.getStart(),
32191 length: node.getEnd() + 1 - node.getStart(),
32192 };
32193 }
32194 function getIdentifierOfDeclaration(decl) {
32195 if ((ts__default["default"].isClassDeclaration(decl) || ts__default["default"].isVariableDeclaration(decl) ||
32196 ts__default["default"].isFunctionDeclaration(decl)) &&
32197 decl.name !== undefined && ts__default["default"].isIdentifier(decl.name)) {
32198 return decl.name;
32199 }
32200 else {
32201 return null;
32202 }
32203 }
32204 function getNameOfDeclaration(decl) {
32205 const id = getIdentifierOfDeclaration(decl);
32206 return id !== null ? id.text : '(unnamed)';
32207 }
32208 function getDescriptorOfDeclaration(decl) {
32209 switch (decl.kind) {
32210 case ts__default["default"].SyntaxKind.ClassDeclaration:
32211 return 'class';
32212 case ts__default["default"].SyntaxKind.FunctionDeclaration:
32213 return 'function';
32214 case ts__default["default"].SyntaxKind.VariableDeclaration:
32215 return 'variable';
32216 case ts__default["default"].SyntaxKind.EnumDeclaration:
32217 return 'enum';
32218 default:
32219 return 'declaration';
32220 }
32221 }
32222
32223 /**
32224 * @license
32225 * Copyright Google LLC All Rights Reserved.
32226 *
32227 * Use of this source code is governed by an MIT-style license that can be
32228 * found in the LICENSE file at https://angular.io/license
32229 */
32230 class ReferenceGraph {
32231 constructor() {
32232 this.references = new Map();
32233 }
32234 add(from, to) {
32235 if (!this.references.has(from)) {
32236 this.references.set(from, new Set());
32237 }
32238 this.references.get(from).add(to);
32239 }
32240 transitiveReferencesOf(target) {
32241 const set = new Set();
32242 this.collectTransitiveReferences(set, target);
32243 return set;
32244 }
32245 pathFrom(source, target) {
32246 return this.collectPathFrom(source, target, new Set());
32247 }
32248 collectPathFrom(source, target, seen) {
32249 if (source === target) {
32250 // Looking for a path from the target to itself - that path is just the target. This is the
32251 // "base case" of the search.
32252 return [target];
32253 }
32254 else if (seen.has(source)) {
32255 // The search has already looked through this source before.
32256 return null;
32257 }
32258 // Consider outgoing edges from `source`.
32259 seen.add(source);
32260 if (!this.references.has(source)) {
32261 // There are no outgoing edges from `source`.
32262 return null;
32263 }
32264 else {
32265 // Look through the outgoing edges of `source`.
32266 // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
32267 let candidatePath = null;
32268 this.references.get(source).forEach(edge => {
32269 // Early exit if a path has already been found.
32270 if (candidatePath !== null) {
32271 return;
32272 }
32273 // Look for a path from this outgoing edge to `target`.
32274 const partialPath = this.collectPathFrom(edge, target, seen);
32275 if (partialPath !== null) {
32276 // A path exists from `edge` to `target`. Insert `source` at the beginning.
32277 candidatePath = [source, ...partialPath];
32278 }
32279 });
32280 return candidatePath;
32281 }
32282 }
32283 collectTransitiveReferences(set, decl) {
32284 if (this.references.has(decl)) {
32285 // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
32286 this.references.get(decl).forEach(ref => {
32287 if (!set.has(ref)) {
32288 set.add(ref);
32289 this.collectTransitiveReferences(set, ref);
32290 }
32291 });
32292 }
32293 }
32294 }
32295
32296 /**
32297 * @license
32298 * Copyright Google LLC All Rights Reserved.
32299 *
32300 * Use of this source code is governed by an MIT-style license that can be
32301 * found in the LICENSE file at https://angular.io/license
32302 */
32303 const NgOriginalFile = Symbol('NgOriginalFile');
32304 var UpdateMode;
32305 (function (UpdateMode) {
32306 /**
32307 * A complete update creates a completely new overlay of type-checking code on top of the user's
32308 * original program, which doesn't include type-checking code from previous calls to
32309 * `updateFiles`.
32310 */
32311 UpdateMode[UpdateMode["Complete"] = 0] = "Complete";
32312 /**
32313 * An incremental update changes the contents of some files in the type-checking program without
32314 * reverting any prior changes.
32315 */
32316 UpdateMode[UpdateMode["Incremental"] = 1] = "Incremental";
32317 })(UpdateMode || (UpdateMode = {}));
32318
32319 /**
32320 * @license
32321 * Copyright Google LLC All Rights Reserved.
32322 *
32323 * Use of this source code is governed by an MIT-style license that can be
32324 * found in the LICENSE file at https://angular.io/license
32325 */
32326 /**
32327 * A `Symbol` which is used to patch extension data onto `ts.SourceFile`s.
32328 */
32329 const NgExtension = Symbol('NgExtension');
32330 /**
32331 * Narrows a `ts.SourceFile` if it has an `NgExtension` property.
32332 */
32333 function isExtended(sf) {
32334 return sf[NgExtension] !== undefined;
32335 }
32336 /**
32337 * Check whether `sf` is a shim `ts.SourceFile` (either a per-file shim or a top-level shim).
32338 */
32339 function isShim(sf) {
32340 return isExtended(sf) && (sf[NgExtension].fileShim !== null || sf[NgExtension].isTopLevelShim);
32341 }
32342
32343 /**
32344 * @license
32345 * Copyright Google LLC All Rights Reserved.
32346 *
32347 * Use of this source code is governed by an MIT-style license that can be
32348 * found in the LICENSE file at https://angular.io/license
32349 */
32350 const STRIP_NG_FACTORY = /(.*)NgFactory$/;
32351 function generatedFactoryTransform(factoryMap, importRewriter) {
32352 return (context) => {
32353 return (file) => {
32354 return transformFactorySourceFile(factoryMap, context, importRewriter, file);
32355 };
32356 };
32357 }
32358 function transformFactorySourceFile(factoryMap, context, importRewriter, file) {
32359 // If this is not a generated file, it won't have factory info associated with it.
32360 if (!factoryMap.has(file.fileName)) {
32361 // Don't transform non-generated code.
32362 return file;
32363 }
32364 const { moduleSymbols, sourceFilePath } = factoryMap.get(file.fileName);
32365 // Not every exported factory statement is valid. They were generated before the program was
32366 // analyzed, and before ngtsc knew which symbols were actually NgModules. factoryMap contains
32367 // that knowledge now, so this transform filters the statement list and removes exported factories
32368 // that aren't actually factories.
32369 //
32370 // This could leave the generated factory file empty. To prevent this (it causes issues with
32371 // closure compiler) a 'ɵNonEmptyModule' export was added when the factory shim was created.
32372 // Preserve that export if needed, and remove it otherwise.
32373 //
32374 // Additionally, an import to @angular/core is generated, but the current compilation unit could
32375 // actually be @angular/core, in which case such an import is invalid and should be replaced with
32376 // the proper path to access Ivy symbols in core.
32377 // The filtered set of statements.
32378 const transformedStatements = [];
32379 // The statement identified as the ɵNonEmptyModule export.
32380 let nonEmptyExport = null;
32381 // Extracted identifiers which refer to import statements from @angular/core.
32382 const coreImportIdentifiers = new Set();
32383 // Consider all the statements.
32384 for (const stmt of file.statements) {
32385 // Look for imports to @angular/core.
32386 if (ts__default["default"].isImportDeclaration(stmt) && ts__default["default"].isStringLiteral(stmt.moduleSpecifier) &&
32387 stmt.moduleSpecifier.text === '@angular/core') {
32388 // Update the import path to point to the correct file using the ImportRewriter.
32389 const rewrittenModuleSpecifier = importRewriter.rewriteSpecifier('@angular/core', sourceFilePath);
32390 if (rewrittenModuleSpecifier !== stmt.moduleSpecifier.text) {
32391 transformedStatements.push(ts__default["default"].updateImportDeclaration(stmt, stmt.decorators, stmt.modifiers, stmt.importClause, ts__default["default"].createStringLiteral(rewrittenModuleSpecifier)));
32392 // Record the identifier by which this imported module goes, so references to its symbols
32393 // can be discovered later.
32394 if (stmt.importClause !== undefined && stmt.importClause.namedBindings !== undefined &&
32395 ts__default["default"].isNamespaceImport(stmt.importClause.namedBindings)) {
32396 coreImportIdentifiers.add(stmt.importClause.namedBindings.name.text);
32397 }
32398 }
32399 else {
32400 transformedStatements.push(stmt);
32401 }
32402 }
32403 else if (ts__default["default"].isVariableStatement(stmt) && stmt.declarationList.declarations.length === 1) {
32404 const decl = stmt.declarationList.declarations[0];
32405 // If this is the ɵNonEmptyModule export, then save it for later.
32406 if (ts__default["default"].isIdentifier(decl.name)) {
32407 if (decl.name.text === 'ɵNonEmptyModule') {
32408 nonEmptyExport = stmt;
32409 continue;
32410 }
32411 // Otherwise, check if this export is a factory for a known NgModule, and retain it if so.
32412 const match = STRIP_NG_FACTORY.exec(decl.name.text);
32413 const module = match ? moduleSymbols.get(match[1]) : null;
32414 if (module) {
32415 // If the module can be tree shaken, then the factory should be wrapped in a
32416 // `noSideEffects()` call which tells Closure to treat the expression as pure, allowing
32417 // it to be removed if the result is not used.
32418 //
32419 // `NgModule`s with an `id` property will be lazy loaded. Google-internal lazy loading
32420 // infra relies on a side effect from the `new NgModuleFactory()` call, which registers
32421 // the module globally. Because of this, we **cannot** tree shake any module which has
32422 // an `id` property. Doing so would cause lazy loaded modules to never be registered.
32423 const moduleIsTreeShakable = !module.hasId;
32424 const newStmt = !moduleIsTreeShakable ?
32425 stmt :
32426 updateInitializers(stmt, (init) => init ? wrapInNoSideEffects(init) : undefined);
32427 transformedStatements.push(newStmt);
32428 }
32429 }
32430 else {
32431 // Leave the statement alone, as it can't be understood.
32432 transformedStatements.push(stmt);
32433 }
32434 }
32435 else {
32436 // Include non-variable statements (imports, etc).
32437 transformedStatements.push(stmt);
32438 }
32439 }
32440 // Check whether the empty module export is still needed.
32441 if (!transformedStatements.some(ts__default["default"].isVariableStatement) && nonEmptyExport !== null) {
32442 // If the resulting file has no factories, include an empty export to
32443 // satisfy closure compiler.
32444 transformedStatements.push(nonEmptyExport);
32445 }
32446 file = ts__default["default"].updateSourceFileNode(file, transformedStatements);
32447 // If any imports to @angular/core were detected and rewritten (which happens when compiling
32448 // @angular/core), go through the SourceFile and rewrite references to symbols imported from core.
32449 if (coreImportIdentifiers.size > 0) {
32450 const visit = (node) => {
32451 node = ts__default["default"].visitEachChild(node, child => visit(child), context);
32452 // Look for expressions of the form "i.s" where 'i' is a detected name for an @angular/core
32453 // import that was changed above. Rewrite 's' using the ImportResolver.
32454 if (ts__default["default"].isPropertyAccessExpression(node) && ts__default["default"].isIdentifier(node.expression) &&
32455 coreImportIdentifiers.has(node.expression.text)) {
32456 // This is an import of a symbol from @angular/core. Transform it with the importRewriter.
32457 const rewrittenSymbol = importRewriter.rewriteSymbol(node.name.text, '@angular/core');
32458 if (rewrittenSymbol !== node.name.text) {
32459 const updated = ts__default["default"].updatePropertyAccess(node, node.expression, ts__default["default"].createIdentifier(rewrittenSymbol));
32460 node = updated;
32461 }
32462 }
32463 return node;
32464 };
32465 file = visit(file);
32466 }
32467 return file;
32468 }
32469 /**
32470 * Wraps the given expression in a call to `ɵnoSideEffects()`, which tells
32471 * Closure we don't care about the side effects of this expression and it should
32472 * be treated as "pure". Closure is free to tree shake this expression if its
32473 * result is not used.
32474 *
32475 * Example: Takes `1 + 2` and returns `i0.ɵnoSideEffects(() => 1 + 2)`.
32476 */
32477 function wrapInNoSideEffects(expr) {
32478 const noSideEffects = ts__default["default"].createPropertyAccess(ts__default["default"].createIdentifier('i0'), 'ɵnoSideEffects');
32479 return ts__default["default"].createCall(noSideEffects,
32480 /* typeArguments */ [],
32481 /* arguments */
32482 [
32483 ts__default["default"].createFunctionExpression(
32484 /* modifiers */ [],
32485 /* asteriskToken */ undefined,
32486 /* name */ undefined,
32487 /* typeParameters */ [],
32488 /* parameters */ [],
32489 /* type */ undefined,
32490 /* body */ ts__default["default"].createBlock([
32491 ts__default["default"].createReturn(expr),
32492 ])),
32493 ]);
32494 }
32495 /**
32496 * Clones and updates the initializers for a given statement to use the new
32497 * expression provided. Does not mutate the input statement.
32498 */
32499 function updateInitializers(stmt, update) {
32500 return ts__default["default"].updateVariableStatement(stmt, stmt.modifiers, ts__default["default"].updateVariableDeclarationList(stmt.declarationList, stmt.declarationList.declarations.map((decl) => ts__default["default"].updateVariableDeclaration(decl, decl.name, decl.type, update(decl.initializer)))));
32501 }
32502
32503 /**
32504 * @license
32505 * Copyright Google LLC All Rights Reserved.
32506 *
32507 * Use of this source code is governed by an MIT-style license that can be
32508 * found in the LICENSE file at https://angular.io/license
32509 */
32510 /**
32511 * An implementation of the `DependencyTracker` dependency graph API.
32512 *
32513 * The `FileDependencyGraph`'s primary job is to determine whether a given file has "logically"
32514 * changed, given the set of physical changes (direct changes to files on disk).
32515 *
32516 * A file is logically changed if at least one of three conditions is met:
32517 *
32518 * 1. The file itself has physically changed.
32519 * 2. One of its dependencies has physically changed.
32520 * 3. One of its resource dependencies has physically changed.
32521 */
32522 class FileDependencyGraph {
32523 constructor() {
32524 this.nodes = new Map();
32525 }
32526 addDependency(from, on) {
32527 this.nodeFor(from).dependsOn.add(absoluteFromSourceFile(on));
32528 }
32529 addResourceDependency(from, resource) {
32530 this.nodeFor(from).usesResources.add(resource);
32531 }
32532 recordDependencyAnalysisFailure(file) {
32533 this.nodeFor(file).failedAnalysis = true;
32534 }
32535 getResourceDependencies(from) {
32536 const node = this.nodes.get(from);
32537 return node ? [...node.usesResources] : [];
32538 }
32539 /**
32540 * Update the current dependency graph from a previous one, incorporating a set of physical
32541 * changes.
32542 *
32543 * This method performs two tasks:
32544 *
32545 * 1. For files which have not logically changed, their dependencies from `previous` are added to
32546 * `this` graph.
32547 * 2. For files which have logically changed, they're added to a set of logically changed files
32548 * which is eventually returned.
32549 *
32550 * In essence, for build `n`, this method performs:
32551 *
32552 * G(n) + L(n) = G(n - 1) + P(n)
32553 *
32554 * where:
32555 *
32556 * G(n) = the dependency graph of build `n`
32557 * L(n) = the logically changed files from build n - 1 to build n.
32558 * P(n) = the physically changed files from build n - 1 to build n.
32559 */
32560 updateWithPhysicalChanges(previous, changedTsPaths, deletedTsPaths, changedResources) {
32561 const logicallyChanged = new Set();
32562 for (const sf of previous.nodes.keys()) {
32563 const sfPath = absoluteFromSourceFile(sf);
32564 const node = previous.nodeFor(sf);
32565 if (isLogicallyChanged(sf, node, changedTsPaths, deletedTsPaths, changedResources)) {
32566 logicallyChanged.add(sfPath);
32567 }
32568 else if (!deletedTsPaths.has(sfPath)) {
32569 this.nodes.set(sf, {
32570 dependsOn: new Set(node.dependsOn),
32571 usesResources: new Set(node.usesResources),
32572 failedAnalysis: false,
32573 });
32574 }
32575 }
32576 return logicallyChanged;
32577 }
32578 nodeFor(sf) {
32579 if (!this.nodes.has(sf)) {
32580 this.nodes.set(sf, {
32581 dependsOn: new Set(),
32582 usesResources: new Set(),
32583 failedAnalysis: false,
32584 });
32585 }
32586 return this.nodes.get(sf);
32587 }
32588 }
32589 /**
32590 * Determine whether `sf` has logically changed, given its dependencies and the set of physically
32591 * changed files and resources.
32592 */
32593 function isLogicallyChanged(sf, node, changedTsPaths, deletedTsPaths, changedResources) {
32594 // A file is assumed to have logically changed if its dependencies could not be determined
32595 // accurately.
32596 if (node.failedAnalysis) {
32597 return true;
32598 }
32599 const sfPath = absoluteFromSourceFile(sf);
32600 // A file is logically changed if it has physically changed itself (including being deleted).
32601 if (changedTsPaths.has(sfPath) || deletedTsPaths.has(sfPath)) {
32602 return true;
32603 }
32604 // A file is logically changed if one of its dependencies has physically changed.
32605 for (const dep of node.dependsOn) {
32606 if (changedTsPaths.has(dep) || deletedTsPaths.has(dep)) {
32607 return true;
32608 }
32609 }
32610 // A file is logically changed if one of its resources has physically changed.
32611 for (const dep of node.usesResources) {
32612 if (changedResources.has(dep)) {
32613 return true;
32614 }
32615 }
32616 return false;
32617 }
32618
32619 /**
32620 * @license
32621 * Copyright Google LLC All Rights Reserved.
32622 *
32623 * Use of this source code is governed by an MIT-style license that can be
32624 * found in the LICENSE file at https://angular.io/license
32625 */
32626 /**
32627 * Discriminant of the `IncrementalState` union.
32628 */
32629 var IncrementalStateKind;
32630 (function (IncrementalStateKind) {
32631 IncrementalStateKind[IncrementalStateKind["Fresh"] = 0] = "Fresh";
32632 IncrementalStateKind[IncrementalStateKind["Delta"] = 1] = "Delta";
32633 IncrementalStateKind[IncrementalStateKind["Analyzed"] = 2] = "Analyzed";
32634 })(IncrementalStateKind || (IncrementalStateKind = {}));
32635
32636 /**
32637 * @license
32638 * Copyright Google LLC All Rights Reserved.
32639 *
32640 * Use of this source code is governed by an MIT-style license that can be
32641 * found in the LICENSE file at https://angular.io/license
32642 */
32643 /**
32644 * Discriminant of the `Phase` type union.
32645 */
32646 var PhaseKind;
32647 (function (PhaseKind) {
32648 PhaseKind[PhaseKind["Analysis"] = 0] = "Analysis";
32649 PhaseKind[PhaseKind["TypeCheckAndEmit"] = 1] = "TypeCheckAndEmit";
32650 })(PhaseKind || (PhaseKind = {}));
32651 /**
32652 * Manages the incremental portion of an Angular compilation, allowing for reuse of a prior
32653 * compilation if available, and producing an output state for reuse of the current compilation in a
32654 * future one.
32655 */
32656 class IncrementalCompilation {
32657 constructor(state, depGraph, versions, step) {
32658 this.depGraph = depGraph;
32659 this.versions = versions;
32660 this.step = step;
32661 this._state = state;
32662 // The compilation begins in analysis phase.
32663 this.phase = {
32664 kind: PhaseKind.Analysis,
32665 semanticDepGraphUpdater: new SemanticDepGraphUpdater(step !== null ? step.priorState.semanticDepGraph : null),
32666 };
32667 }
32668 /**
32669 * Begin a fresh `IncrementalCompilation`.
32670 */
32671 static fresh(program, versions) {
32672 const state = {
32673 kind: IncrementalStateKind.Fresh,
32674 };
32675 return new IncrementalCompilation(state, new FileDependencyGraph(), versions, /* reuse */ null);
32676 }
32677 static incremental(program, newVersions, oldProgram, oldState, modifiedResourceFiles, perf) {
32678 return perf.inPhase(PerfPhase.Reconciliation, () => {
32679 const physicallyChangedTsFiles = new Set();
32680 const changedResourceFiles = new Set(modifiedResourceFiles ?? []);
32681 let priorAnalysis;
32682 switch (oldState.kind) {
32683 case IncrementalStateKind.Fresh:
32684 // Since this line of program has never been successfully analyzed to begin with, treat
32685 // this as a fresh compilation.
32686 return IncrementalCompilation.fresh(program, newVersions);
32687 case IncrementalStateKind.Analyzed:
32688 // The most recent program was analyzed successfully, so we can use that as our prior
32689 // state and don't need to consider any other deltas except changes in the most recent
32690 // program.
32691 priorAnalysis = oldState;
32692 break;
32693 case IncrementalStateKind.Delta:
32694 // There is an ancestor program which was analyzed successfully and can be used as a
32695 // starting point, but we need to determine what's changed since that program.
32696 priorAnalysis = oldState.lastAnalyzedState;
32697 for (const sfPath of oldState.physicallyChangedTsFiles) {
32698 physicallyChangedTsFiles.add(sfPath);
32699 }
32700 for (const resourcePath of oldState.changedResourceFiles) {
32701 changedResourceFiles.add(resourcePath);
32702 }
32703 break;
32704 }
32705 const oldVersions = priorAnalysis.versions;
32706 const oldFilesArray = oldProgram.getSourceFiles().map(toOriginalSourceFile);
32707 const oldFiles = new Set(oldFilesArray);
32708 const deletedTsFiles = new Set(oldFilesArray.map(sf => absoluteFromSourceFile(sf)));
32709 for (const possiblyRedirectedNewFile of program.getSourceFiles()) {
32710 const sf = toOriginalSourceFile(possiblyRedirectedNewFile);
32711 const sfPath = absoluteFromSourceFile(sf);
32712 // Since we're seeing a file in the incoming program with this name, it can't have been
32713 // deleted.
32714 deletedTsFiles.delete(sfPath);
32715 if (oldFiles.has(sf)) {
32716 // This source file has the same object identity as in the previous program. We need to
32717 // determine if it's really the same file, or if it might have changed versions since the
32718 // last program without changing its identity.
32719 // If there's no version information available, then this is the same file, and we can
32720 // skip it.
32721 if (oldVersions === null || newVersions === null) {
32722 continue;
32723 }
32724 // If a version is available for the file from both the prior and the current program, and
32725 // that version is the same, then this is the same file, and we can skip it.
32726 if (oldVersions.has(sfPath) && newVersions.has(sfPath) &&
32727 oldVersions.get(sfPath) === newVersions.get(sfPath)) {
32728 continue;
32729 }
32730 // Otherwise, assume that the file has changed. Either its versions didn't match, or we
32731 // were missing version information about it on one side for some reason.
32732 }
32733 // Bail out if a .d.ts file changes - the semantic dep graph is not able to process such
32734 // changes correctly yet.
32735 if (sf.isDeclarationFile) {
32736 return IncrementalCompilation.fresh(program, newVersions);
32737 }
32738 // The file has changed physically, so record it.
32739 physicallyChangedTsFiles.add(sfPath);
32740 }
32741 // Remove any files that have been deleted from the list of physical changes.
32742 for (const deletedFileName of deletedTsFiles) {
32743 physicallyChangedTsFiles.delete(resolve(deletedFileName));
32744 }
32745 // Use the prior dependency graph to project physical changes into a set of logically changed
32746 // files.
32747 const depGraph = new FileDependencyGraph();
32748 const logicallyChangedTsFiles = depGraph.updateWithPhysicalChanges(priorAnalysis.depGraph, physicallyChangedTsFiles, deletedTsFiles, changedResourceFiles);
32749 // Physically changed files aren't necessarily counted as logically changed by the dependency
32750 // graph (files do not have edges to themselves), so add them to the logical changes
32751 // explicitly.
32752 for (const sfPath of physicallyChangedTsFiles) {
32753 logicallyChangedTsFiles.add(sfPath);
32754 }
32755 // Start off in a `DeltaIncrementalState` as a delta against the previous successful analysis,
32756 // until this compilation completes its own analysis.
32757 const state = {
32758 kind: IncrementalStateKind.Delta,
32759 physicallyChangedTsFiles,
32760 changedResourceFiles,
32761 lastAnalyzedState: priorAnalysis,
32762 };
32763 return new IncrementalCompilation(state, depGraph, newVersions, {
32764 priorState: priorAnalysis,
32765 logicallyChangedTsFiles,
32766 });
32767 });
32768 }
32769 get state() {
32770 return this._state;
32771 }
32772 get semanticDepGraphUpdater() {
32773 if (this.phase.kind !== PhaseKind.Analysis) {
32774 throw new Error(`AssertionError: Cannot update the SemanticDepGraph after analysis completes`);
32775 }
32776 return this.phase.semanticDepGraphUpdater;
32777 }
32778 recordSuccessfulAnalysis(traitCompiler) {
32779 if (this.phase.kind !== PhaseKind.Analysis) {
32780 throw new Error(`AssertionError: Incremental compilation in phase ${PhaseKind[this.phase.kind]}, expected Analysis`);
32781 }
32782 const { needsEmit, needsTypeCheckEmit, newGraph } = this.phase.semanticDepGraphUpdater.finalize();
32783 // Determine the set of files which have already been emitted.
32784 let emitted;
32785 if (this.step === null) {
32786 // Since there is no prior compilation, no files have yet been emitted.
32787 emitted = new Set();
32788 }
32789 else {
32790 // Begin with the files emitted by the prior successful compilation, but remove those which we
32791 // know need to bee re-emitted.
32792 emitted = new Set(this.step.priorState.emitted);
32793 // Files need re-emitted if they've logically changed.
32794 for (const sfPath of this.step.logicallyChangedTsFiles) {
32795 emitted.delete(sfPath);
32796 }
32797 // Files need re-emitted if they've semantically changed.
32798 for (const sfPath of needsEmit) {
32799 emitted.delete(sfPath);
32800 }
32801 }
32802 // Transition to a successfully analyzed compilation. At this point, a subsequent compilation
32803 // could use this state as a starting point.
32804 this._state = {
32805 kind: IncrementalStateKind.Analyzed,
32806 versions: this.versions,
32807 depGraph: this.depGraph,
32808 semanticDepGraph: newGraph,
32809 priorAnalysis: traitCompiler.getAnalyzedRecords(),
32810 typeCheckResults: null,
32811 emitted,
32812 };
32813 // We now enter the type-check and emit phase of compilation.
32814 this.phase = {
32815 kind: PhaseKind.TypeCheckAndEmit,
32816 needsEmit,
32817 needsTypeCheckEmit,
32818 };
32819 }
32820 recordSuccessfulTypeCheck(results) {
32821 if (this._state.kind !== IncrementalStateKind.Analyzed) {
32822 throw new Error(`AssertionError: Expected successfully analyzed compilation.`);
32823 }
32824 else if (this.phase.kind !== PhaseKind.TypeCheckAndEmit) {
32825 throw new Error(`AssertionError: Incremental compilation in phase ${PhaseKind[this.phase.kind]}, expected TypeCheck`);
32826 }
32827 this._state.typeCheckResults = results;
32828 }
32829 recordSuccessfulEmit(sf) {
32830 if (this._state.kind !== IncrementalStateKind.Analyzed) {
32831 throw new Error(`AssertionError: Expected successfully analyzed compilation.`);
32832 }
32833 this._state.emitted.add(absoluteFromSourceFile(sf));
32834 }
32835 priorAnalysisFor(sf) {
32836 if (this.step === null) {
32837 return null;
32838 }
32839 const sfPath = absoluteFromSourceFile(sf);
32840 // If the file has logically changed, its previous analysis cannot be reused.
32841 if (this.step.logicallyChangedTsFiles.has(sfPath)) {
32842 return null;
32843 }
32844 const priorAnalysis = this.step.priorState.priorAnalysis;
32845 if (!priorAnalysis.has(sf)) {
32846 return null;
32847 }
32848 return priorAnalysis.get(sf);
32849 }
32850 priorTypeCheckingResultsFor(sf) {
32851 if (this.phase.kind !== PhaseKind.TypeCheckAndEmit) {
32852 throw new Error(`AssertionError: Expected successfully analyzed compilation.`);
32853 }
32854 if (this.step === null) {
32855 return null;
32856 }
32857 const sfPath = absoluteFromSourceFile(sf);
32858 // If the file has logically changed, or its template type-checking results have semantically
32859 // changed, then past type-checking results cannot be reused.
32860 if (this.step.logicallyChangedTsFiles.has(sfPath) ||
32861 this.phase.needsTypeCheckEmit.has(sfPath)) {
32862 return null;
32863 }
32864 // Past results also cannot be reused if they're not available.
32865 if (this.step.priorState.typeCheckResults === null ||
32866 !this.step.priorState.typeCheckResults.has(sfPath)) {
32867 return null;
32868 }
32869 const priorResults = this.step.priorState.typeCheckResults.get(sfPath);
32870 // If the past results relied on inlining, they're not safe for reuse.
32871 if (priorResults.hasInlines) {
32872 return null;
32873 }
32874 return priorResults;
32875 }
32876 safeToSkipEmit(sf) {
32877 // If this is a fresh compilation, it's never safe to skip an emit.
32878 if (this.step === null) {
32879 return false;
32880 }
32881 const sfPath = absoluteFromSourceFile(sf);
32882 // If the file has itself logically changed, it must be emitted.
32883 if (this.step.logicallyChangedTsFiles.has(sfPath)) {
32884 return false;
32885 }
32886 if (this.phase.kind !== PhaseKind.TypeCheckAndEmit) {
32887 throw new Error(`AssertionError: Expected successful analysis before attempting to emit files`);
32888 }
32889 // If during analysis it was determined that this file has semantically changed, it must be
32890 // emitted.
32891 if (this.phase.needsEmit.has(sfPath)) {
32892 return false;
32893 }
32894 // Generally it should be safe to assume here that the file was previously emitted by the last
32895 // successful compilation. However, as a defense-in-depth against incorrectness, we explicitly
32896 // check that the last emit included this file, and re-emit it otherwise.
32897 return this.step.priorState.emitted.has(sfPath);
32898 }
32899 }
32900 /**
32901 * To accurately detect whether a source file was affected during an incremental rebuild, the
32902 * "original" source file needs to be consistently used.
32903 *
32904 * First, TypeScript may have created source file redirects when declaration files of the same
32905 * version of a library are included multiple times. The non-redirected source file should be used
32906 * to detect changes, as otherwise the redirected source files cause a mismatch when compared to
32907 * a prior program.
32908 *
32909 * Second, the program that is used for template type checking may contain mutated source files, if
32910 * inline type constructors or inline template type-check blocks had to be used. Such source files
32911 * store their original, non-mutated source file from the original program in a symbol. For
32912 * computing the affected files in an incremental build this original source file should be used, as
32913 * the mutated source file would always be considered affected.
32914 */
32915 function toOriginalSourceFile(sf) {
32916 const unredirectedSf = toUnredirectedSourceFile(sf);
32917 const originalFile = unredirectedSf[NgOriginalFile];
32918 if (originalFile !== undefined) {
32919 return originalFile;
32920 }
32921 else {
32922 return unredirectedSf;
32923 }
32924 }
32925
32926 /**
32927 * @license
32928 * Copyright Google LLC All Rights Reserved.
32929 *
32930 * Use of this source code is governed by an MIT-style license that can be
32931 * found in the LICENSE file at https://angular.io/license
32932 */
32933 /**
32934 * Tracks an `IncrementalDriver` within the strategy itself.
32935 */
32936 class TrackedIncrementalBuildStrategy {
32937 constructor() {
32938 this.state = null;
32939 this.isSet = false;
32940 }
32941 getIncrementalState() {
32942 return this.state;
32943 }
32944 setIncrementalState(state) {
32945 this.state = state;
32946 this.isSet = true;
32947 }
32948 toNextBuildStrategy() {
32949 const strategy = new TrackedIncrementalBuildStrategy();
32950 // Only reuse state that was explicitly set via `setIncrementalState`.
32951 strategy.state = this.isSet ? this.state : null;
32952 return strategy;
32953 }
32954 }
32955
32956 /**
32957 * @license
32958 * Copyright Google LLC All Rights Reserved.
32959 *
32960 * Use of this source code is governed by an MIT-style license that can be
32961 * found in the LICENSE file at https://angular.io/license
32962 */
32963 /**
32964 * Describes the kind of identifier found in a template.
32965 */
32966 var IdentifierKind;
32967 (function (IdentifierKind) {
32968 IdentifierKind[IdentifierKind["Property"] = 0] = "Property";
32969 IdentifierKind[IdentifierKind["Method"] = 1] = "Method";
32970 IdentifierKind[IdentifierKind["Element"] = 2] = "Element";
32971 IdentifierKind[IdentifierKind["Template"] = 3] = "Template";
32972 IdentifierKind[IdentifierKind["Attribute"] = 4] = "Attribute";
32973 IdentifierKind[IdentifierKind["Reference"] = 5] = "Reference";
32974 IdentifierKind[IdentifierKind["Variable"] = 6] = "Variable";
32975 })(IdentifierKind || (IdentifierKind = {}));
32976 /**
32977 * Describes the absolute byte offsets of a text anchor in a source code.
32978 */
32979 class AbsoluteSourceSpan {
32980 constructor(start, end) {
32981 this.start = start;
32982 this.end = end;
32983 }
32984 }
32985
32986 /**
32987 * @license
32988 * Copyright Google LLC All Rights Reserved.
32989 *
32990 * Use of this source code is governed by an MIT-style license that can be
32991 * found in the LICENSE file at https://angular.io/license
32992 */
32993 /**
32994 * A context for storing indexing infromation about components of a program.
32995 *
32996 * An `IndexingContext` collects component and template analysis information from
32997 * `DecoratorHandler`s and exposes them to be indexed.
32998 */
32999 class IndexingContext {
33000 constructor() {
33001 this.components = new Set();
33002 }
33003 /**
33004 * Adds a component to the context.
33005 */
33006 addComponent(info) {
33007 this.components.add(info);
33008 }
33009 }
33010
33011 /**
33012 * @license
33013 * Copyright Google LLC All Rights Reserved.
33014 *
33015 * Use of this source code is governed by an MIT-style license that can be
33016 * found in the LICENSE file at https://angular.io/license
33017 */
33018 /**
33019 * Visits the AST of an Angular template syntax expression, finding interesting
33020 * entities (variable references, etc.). Creates an array of Entities found in
33021 * the expression, with the location of the Entities being relative to the
33022 * expression.
33023 *
33024 * Visiting `text {{prop}}` will return
33025 * `[TopLevelIdentifier {name: 'prop', span: {start: 7, end: 11}}]`.
33026 */
33027 class ExpressionVisitor$1 extends RecursiveAstVisitor {
33028 constructor(expressionStr, absoluteOffset, boundTemplate, targetToIdentifier) {
33029 super();
33030 this.expressionStr = expressionStr;
33031 this.absoluteOffset = absoluteOffset;
33032 this.boundTemplate = boundTemplate;
33033 this.targetToIdentifier = targetToIdentifier;
33034 this.identifiers = [];
33035 }
33036 /**
33037 * Returns identifiers discovered in an expression.
33038 *
33039 * @param ast expression AST to visit
33040 * @param source expression AST source code
33041 * @param absoluteOffset absolute byte offset from start of the file to the start of the AST
33042 * source code.
33043 * @param boundTemplate bound target of the entire template, which can be used to query for the
33044 * entities expressions target.
33045 * @param targetToIdentifier closure converting a template target node to its identifier.
33046 */
33047 static getIdentifiers(ast, source, absoluteOffset, boundTemplate, targetToIdentifier) {
33048 const visitor = new ExpressionVisitor$1(source, absoluteOffset, boundTemplate, targetToIdentifier);
33049 visitor.visit(ast);
33050 return visitor.identifiers;
33051 }
33052 visit(ast) {
33053 ast.visit(this);
33054 }
33055 visitPropertyRead(ast, context) {
33056 this.visitIdentifier(ast, IdentifierKind.Property);
33057 super.visitPropertyRead(ast, context);
33058 }
33059 visitPropertyWrite(ast, context) {
33060 this.visitIdentifier(ast, IdentifierKind.Property);
33061 super.visitPropertyWrite(ast, context);
33062 }
33063 /**
33064 * Visits an identifier, adding it to the identifier store if it is useful for indexing.
33065 *
33066 * @param ast expression AST the identifier is in
33067 * @param kind identifier kind
33068 */
33069 visitIdentifier(ast, kind) {
33070 // The definition of a non-top-level property such as `bar` in `{{foo.bar}}` is currently
33071 // impossible to determine by an indexer and unsupported by the indexing module.
33072 // The indexing module also does not currently support references to identifiers declared in the
33073 // template itself, which have a non-null expression target.
33074 if (!(ast.receiver instanceof ImplicitReceiver)) {
33075 return;
33076 }
33077 // The source span of the requested AST starts at a location that is offset from the expression.
33078 const identifierStart = ast.sourceSpan.start - this.absoluteOffset;
33079 if (!this.expressionStr.substring(identifierStart).startsWith(ast.name)) {
33080 throw new Error(`Impossible state: "${ast.name}" not found in "${this.expressionStr}" at location ${identifierStart}`);
33081 }
33082 // Join the relative position of the expression within a node with the absolute position
33083 // of the node to get the absolute position of the expression in the source code.
33084 const absoluteStart = this.absoluteOffset + identifierStart;
33085 const span = new AbsoluteSourceSpan(absoluteStart, absoluteStart + ast.name.length);
33086 const targetAst = this.boundTemplate.getExpressionTarget(ast);
33087 const target = targetAst ? this.targetToIdentifier(targetAst) : null;
33088 const identifier = {
33089 name: ast.name,
33090 span,
33091 kind,
33092 target,
33093 };
33094 this.identifiers.push(identifier);
33095 }
33096 }
33097 /**
33098 * Visits the AST of a parsed Angular template. Discovers and stores
33099 * identifiers of interest, deferring to an `ExpressionVisitor` as needed.
33100 */
33101 class TemplateVisitor$1 extends RecursiveVisitor {
33102 /**
33103 * Creates a template visitor for a bound template target. The bound target can be used when
33104 * deferred to the expression visitor to get information about the target of an expression.
33105 *
33106 * @param boundTemplate bound template target
33107 */
33108 constructor(boundTemplate) {
33109 super();
33110 this.boundTemplate = boundTemplate;
33111 // Identifiers of interest found in the template.
33112 this.identifiers = new Set();
33113 // Map of targets in a template to their identifiers.
33114 this.targetIdentifierCache = new Map();
33115 // Map of elements and templates to their identifiers.
33116 this.elementAndTemplateIdentifierCache = new Map();
33117 }
33118 /**
33119 * Visits a node in the template.
33120 *
33121 * @param node node to visit
33122 */
33123 visit(node) {
33124 node.visit(this);
33125 }
33126 visitAll(nodes) {
33127 nodes.forEach(node => this.visit(node));
33128 }
33129 /**
33130 * Add an identifier for an HTML element and visit its children recursively.
33131 *
33132 * @param element
33133 */
33134 visitElement(element) {
33135 const elementIdentifier = this.elementOrTemplateToIdentifier(element);
33136 this.identifiers.add(elementIdentifier);
33137 this.visitAll(element.references);
33138 this.visitAll(element.inputs);
33139 this.visitAll(element.attributes);
33140 this.visitAll(element.children);
33141 this.visitAll(element.outputs);
33142 }
33143 visitTemplate(template) {
33144 const templateIdentifier = this.elementOrTemplateToIdentifier(template);
33145 this.identifiers.add(templateIdentifier);
33146 this.visitAll(template.variables);
33147 this.visitAll(template.attributes);
33148 this.visitAll(template.templateAttrs);
33149 this.visitAll(template.children);
33150 this.visitAll(template.references);
33151 }
33152 visitBoundAttribute(attribute) {
33153 // If the bound attribute has no value, it cannot have any identifiers in the value expression.
33154 if (attribute.valueSpan === undefined) {
33155 return;
33156 }
33157 const identifiers = ExpressionVisitor$1.getIdentifiers(attribute.value, attribute.valueSpan.toString(), attribute.valueSpan.start.offset, this.boundTemplate, this.targetToIdentifier.bind(this));
33158 identifiers.forEach(id => this.identifiers.add(id));
33159 }
33160 visitBoundEvent(attribute) {
33161 this.visitExpression(attribute.handler);
33162 }
33163 visitBoundText(text) {
33164 this.visitExpression(text.value);
33165 }
33166 visitReference(reference) {
33167 const referenceIdentifer = this.targetToIdentifier(reference);
33168 this.identifiers.add(referenceIdentifer);
33169 }
33170 visitVariable(variable) {
33171 const variableIdentifier = this.targetToIdentifier(variable);
33172 this.identifiers.add(variableIdentifier);
33173 }
33174 /** Creates an identifier for a template element or template node. */
33175 elementOrTemplateToIdentifier(node) {
33176 // If this node has already been seen, return the cached result.
33177 if (this.elementAndTemplateIdentifierCache.has(node)) {
33178 return this.elementAndTemplateIdentifierCache.get(node);
33179 }
33180 let name;
33181 let kind;
33182 if (node instanceof Template) {
33183 name = node.tagName;
33184 kind = IdentifierKind.Template;
33185 }
33186 else {
33187 name = node.name;
33188 kind = IdentifierKind.Element;
33189 }
33190 const sourceSpan = node.startSourceSpan;
33191 // An element's or template's source span can be of the form `<element>`, `<element />`, or
33192 // `<element></element>`. Only the selector is interesting to the indexer, so the source is
33193 // searched for the first occurrence of the element (selector) name.
33194 const start = this.getStartLocation(name, sourceSpan);
33195 const absoluteSpan = new AbsoluteSourceSpan(start, start + name.length);
33196 // Record the nodes's attributes, which an indexer can later traverse to see if any of them
33197 // specify a used directive on the node.
33198 const attributes = node.attributes.map(({ name, sourceSpan }) => {
33199 return {
33200 name,
33201 span: new AbsoluteSourceSpan(sourceSpan.start.offset, sourceSpan.end.offset),
33202 kind: IdentifierKind.Attribute,
33203 };
33204 });
33205 const usedDirectives = this.boundTemplate.getDirectivesOfNode(node) || [];
33206 const identifier = {
33207 name,
33208 span: absoluteSpan,
33209 kind,
33210 attributes: new Set(attributes),
33211 usedDirectives: new Set(usedDirectives.map(dir => {
33212 return {
33213 node: dir.ref.node,
33214 selector: dir.selector,
33215 };
33216 })),
33217 // cast b/c pre-TypeScript 3.5 unions aren't well discriminated
33218 };
33219 this.elementAndTemplateIdentifierCache.set(node, identifier);
33220 return identifier;
33221 }
33222 /** Creates an identifier for a template reference or template variable target. */
33223 targetToIdentifier(node) {
33224 // If this node has already been seen, return the cached result.
33225 if (this.targetIdentifierCache.has(node)) {
33226 return this.targetIdentifierCache.get(node);
33227 }
33228 const { name, sourceSpan } = node;
33229 const start = this.getStartLocation(name, sourceSpan);
33230 const span = new AbsoluteSourceSpan(start, start + name.length);
33231 let identifier;
33232 if (node instanceof Reference$1) {
33233 // If the node is a reference, we care about its target. The target can be an element, a
33234 // template, a directive applied on a template or element (in which case the directive field
33235 // is non-null), or nothing at all.
33236 const refTarget = this.boundTemplate.getReferenceTarget(node);
33237 let target = null;
33238 if (refTarget) {
33239 if (refTarget instanceof Element$1 || refTarget instanceof Template) {
33240 target = {
33241 node: this.elementOrTemplateToIdentifier(refTarget),
33242 directive: null,
33243 };
33244 }
33245 else {
33246 target = {
33247 node: this.elementOrTemplateToIdentifier(refTarget.node),
33248 directive: refTarget.directive.ref.node,
33249 };
33250 }
33251 }
33252 identifier = {
33253 name,
33254 span,
33255 kind: IdentifierKind.Reference,
33256 target,
33257 };
33258 }
33259 else {
33260 identifier = {
33261 name,
33262 span,
33263 kind: IdentifierKind.Variable,
33264 };
33265 }
33266 this.targetIdentifierCache.set(node, identifier);
33267 return identifier;
33268 }
33269 /** Gets the start location of a string in a SourceSpan */
33270 getStartLocation(name, context) {
33271 const localStr = context.toString();
33272 if (!localStr.includes(name)) {
33273 throw new Error(`Impossible state: "${name}" not found in "${localStr}"`);
33274 }
33275 return context.start.offset + localStr.indexOf(name);
33276 }
33277 /**
33278 * Visits a node's expression and adds its identifiers, if any, to the visitor's state.
33279 * Only ASTs with information about the expression source and its location are visited.
33280 *
33281 * @param node node whose expression to visit
33282 */
33283 visitExpression(ast) {
33284 // Only include ASTs that have information about their source and absolute source spans.
33285 if (ast instanceof ASTWithSource && ast.source !== null) {
33286 // Make target to identifier mapping closure stateful to this visitor instance.
33287 const targetToIdentifier = this.targetToIdentifier.bind(this);
33288 const absoluteOffset = ast.sourceSpan.start;
33289 const identifiers = ExpressionVisitor$1.getIdentifiers(ast, ast.source, absoluteOffset, this.boundTemplate, targetToIdentifier);
33290 identifiers.forEach(id => this.identifiers.add(id));
33291 }
33292 }
33293 }
33294 /**
33295 * Traverses a template AST and builds identifiers discovered in it.
33296 *
33297 * @param boundTemplate bound template target, which can be used for querying expression targets.
33298 * @return identifiers in template
33299 */
33300 function getTemplateIdentifiers(boundTemplate) {
33301 const visitor = new TemplateVisitor$1(boundTemplate);
33302 if (boundTemplate.target.template !== undefined) {
33303 visitor.visitAll(boundTemplate.target.template);
33304 }
33305 return visitor.identifiers;
33306 }
33307
33308 /**
33309 * @license
33310 * Copyright Google LLC All Rights Reserved.
33311 *
33312 * Use of this source code is governed by an MIT-style license that can be
33313 * found in the LICENSE file at https://angular.io/license
33314 */
33315 /**
33316 * Generates `IndexedComponent` entries from a `IndexingContext`, which has information
33317 * about components discovered in the program registered in it.
33318 *
33319 * The context must be populated before `generateAnalysis` is called.
33320 */
33321 function generateAnalysis(context) {
33322 const analysis = new Map();
33323 context.components.forEach(({ declaration, selector, boundTemplate, templateMeta }) => {
33324 const name = declaration.name.getText();
33325 const usedComponents = new Set();
33326 const usedDirs = boundTemplate.getUsedDirectives();
33327 usedDirs.forEach(dir => {
33328 if (dir.isComponent) {
33329 usedComponents.add(dir.ref.node);
33330 }
33331 });
33332 // Get source files for the component and the template. If the template is inline, its source
33333 // file is the component's.
33334 const componentFile = new ParseSourceFile(declaration.getSourceFile().getFullText(), declaration.getSourceFile().fileName);
33335 let templateFile;
33336 if (templateMeta.isInline) {
33337 templateFile = componentFile;
33338 }
33339 else {
33340 templateFile = templateMeta.file;
33341 }
33342 analysis.set(declaration, {
33343 name,
33344 selector,
33345 file: componentFile,
33346 template: {
33347 identifiers: getTemplateIdentifiers(boundTemplate),
33348 usedComponents,
33349 isInline: templateMeta.isInline,
33350 file: templateFile,
33351 },
33352 });
33353 });
33354 return analysis;
33355 }
33356
33357 /**
33358 * @license
33359 * Copyright Google LLC All Rights Reserved.
33360 *
33361 * Use of this source code is governed by an MIT-style license that can be
33362 * found in the LICENSE file at https://angular.io/license
33363 */
33364 const CSS_PREPROCESSOR_EXT = /(\.scss|\.sass|\.less|\.styl)$/;
33365 const RESOURCE_MARKER = '.$ngresource$';
33366 const RESOURCE_MARKER_TS = RESOURCE_MARKER + '.ts';
33367 /**
33368 * `ResourceLoader` which delegates to an `NgCompilerAdapter`'s resource loading methods.
33369 */
33370 class AdapterResourceLoader {
33371 constructor(adapter, options) {
33372 this.adapter = adapter;
33373 this.options = options;
33374 this.cache = new Map();
33375 this.fetching = new Map();
33376 this.lookupResolutionHost = createLookupResolutionHost(this.adapter);
33377 this.canPreload = !!this.adapter.readResource;
33378 this.canPreprocess = !!this.adapter.transformResource;
33379 }
33380 /**
33381 * Resolve the url of a resource relative to the file that contains the reference to it.
33382 * The return value of this method can be used in the `load()` and `preload()` methods.
33383 *
33384 * Uses the provided CompilerHost if it supports mapping resources to filenames.
33385 * Otherwise, uses a fallback mechanism that searches the module resolution candidates.
33386 *
33387 * @param url The, possibly relative, url of the resource.
33388 * @param fromFile The path to the file that contains the URL of the resource.
33389 * @returns A resolved url of resource.
33390 * @throws An error if the resource cannot be resolved.
33391 */
33392 resolve(url, fromFile) {
33393 let resolvedUrl = null;
33394 if (this.adapter.resourceNameToFileName) {
33395 resolvedUrl = this.adapter.resourceNameToFileName(url, fromFile, (url, fromFile) => this.fallbackResolve(url, fromFile));
33396 }
33397 else {
33398 resolvedUrl = this.fallbackResolve(url, fromFile);
33399 }
33400 if (resolvedUrl === null) {
33401 throw new Error(`HostResourceResolver: could not resolve ${url} in context of ${fromFile})`);
33402 }
33403 return resolvedUrl;
33404 }
33405 /**
33406 * Preload the specified resource, asynchronously.
33407 *
33408 * Once the resource is loaded, its value is cached so it can be accessed synchronously via the
33409 * `load()` method.
33410 *
33411 * @param resolvedUrl The url (resolved by a call to `resolve()`) of the resource to preload.
33412 * @param context Information about the resource such as the type and containing file.
33413 * @returns A Promise that is resolved once the resource has been loaded or `undefined` if the
33414 * file has already been loaded.
33415 * @throws An Error if pre-loading is not available.
33416 */
33417 preload(resolvedUrl, context) {
33418 if (!this.adapter.readResource) {
33419 throw new Error('HostResourceLoader: the CompilerHost provided does not support pre-loading resources.');
33420 }
33421 if (this.cache.has(resolvedUrl)) {
33422 return undefined;
33423 }
33424 else if (this.fetching.has(resolvedUrl)) {
33425 return this.fetching.get(resolvedUrl);
33426 }
33427 let result = this.adapter.readResource(resolvedUrl);
33428 if (this.adapter.transformResource && context.type === 'style') {
33429 const resourceContext = {
33430 type: 'style',
33431 containingFile: context.containingFile,
33432 resourceFile: resolvedUrl,
33433 };
33434 result = Promise.resolve(result).then(async (str) => {
33435 const transformResult = await this.adapter.transformResource(str, resourceContext);
33436 return transformResult === null ? str : transformResult.content;
33437 });
33438 }
33439 if (typeof result === 'string') {
33440 this.cache.set(resolvedUrl, result);
33441 return undefined;
33442 }
33443 else {
33444 const fetchCompletion = result.then(str => {
33445 this.fetching.delete(resolvedUrl);
33446 this.cache.set(resolvedUrl, str);
33447 });
33448 this.fetching.set(resolvedUrl, fetchCompletion);
33449 return fetchCompletion;
33450 }
33451 }
33452 /**
33453 * Preprocess the content data of an inline resource, asynchronously.
33454 *
33455 * @param data The existing content data from the inline resource.
33456 * @param context Information regarding the resource such as the type and containing file.
33457 * @returns A Promise that resolves to the processed data. If no processing occurs, the
33458 * same data string that was passed to the function will be resolved.
33459 */
33460 async preprocessInline(data, context) {
33461 if (!this.adapter.transformResource || context.type !== 'style') {
33462 return data;
33463 }
33464 const transformResult = await this.adapter.transformResource(data, { type: 'style', containingFile: context.containingFile, resourceFile: null });
33465 if (transformResult === null) {
33466 return data;
33467 }
33468 return transformResult.content;
33469 }
33470 /**
33471 * Load the resource at the given url, synchronously.
33472 *
33473 * The contents of the resource may have been cached by a previous call to `preload()`.
33474 *
33475 * @param resolvedUrl The url (resolved by a call to `resolve()`) of the resource to load.
33476 * @returns The contents of the resource.
33477 */
33478 load(resolvedUrl) {
33479 if (this.cache.has(resolvedUrl)) {
33480 return this.cache.get(resolvedUrl);
33481 }
33482 const result = this.adapter.readResource ? this.adapter.readResource(resolvedUrl) :
33483 this.adapter.readFile(resolvedUrl);
33484 if (typeof result !== 'string') {
33485 throw new Error(`HostResourceLoader: loader(${resolvedUrl}) returned a Promise`);
33486 }
33487 this.cache.set(resolvedUrl, result);
33488 return result;
33489 }
33490 /**
33491 * Invalidate the entire resource cache.
33492 */
33493 invalidate() {
33494 this.cache.clear();
33495 }
33496 /**
33497 * Attempt to resolve `url` in the context of `fromFile`, while respecting the rootDirs
33498 * option from the tsconfig. First, normalize the file name.
33499 */
33500 fallbackResolve(url, fromFile) {
33501 let candidateLocations;
33502 if (url.startsWith('/')) {
33503 // This path is not really an absolute path, but instead the leading '/' means that it's
33504 // rooted in the project rootDirs. So look for it according to the rootDirs.
33505 candidateLocations = this.getRootedCandidateLocations(url);
33506 }
33507 else {
33508 // This path is a "relative" path and can be resolved as such. To make this easier on the
33509 // downstream resolver, the './' prefix is added if missing to distinguish these paths from
33510 // absolute node_modules paths.
33511 if (!url.startsWith('.')) {
33512 url = `./${url}`;
33513 }
33514 candidateLocations = this.getResolvedCandidateLocations(url, fromFile);
33515 }
33516 for (const candidate of candidateLocations) {
33517 if (this.adapter.fileExists(candidate)) {
33518 return candidate;
33519 }
33520 else if (CSS_PREPROCESSOR_EXT.test(candidate)) {
33521 /**
33522 * If the user specified styleUrl points to *.scss, but the Sass compiler was run before
33523 * Angular, then the resource may have been generated as *.css. Simply try the resolution
33524 * again.
33525 */
33526 const cssFallbackUrl = candidate.replace(CSS_PREPROCESSOR_EXT, '.css');
33527 if (this.adapter.fileExists(cssFallbackUrl)) {
33528 return cssFallbackUrl;
33529 }
33530 }
33531 }
33532 return null;
33533 }
33534 getRootedCandidateLocations(url) {
33535 // The path already starts with '/', so add a '.' to make it relative.
33536 const segment = ('.' + url);
33537 return this.adapter.rootDirs.map(rootDir => join(rootDir, segment));
33538 }
33539 /**
33540 * TypeScript provides utilities to resolve module names, but not resource files (which aren't
33541 * a part of the ts.Program). However, TypeScript's module resolution can be used creatively
33542 * to locate where resource files should be expected to exist. Since module resolution returns
33543 * a list of file names that were considered, the loader can enumerate the possible locations
33544 * for the file by setting up a module resolution for it that will fail.
33545 */
33546 getResolvedCandidateLocations(url, fromFile) {
33547 // clang-format off
33548 const failedLookup = ts__default["default"].resolveModuleName(url + RESOURCE_MARKER, fromFile, this.options, this.lookupResolutionHost);
33549 // clang-format on
33550 if (failedLookup.failedLookupLocations === undefined) {
33551 throw new Error(`Internal error: expected to find failedLookupLocations during resolution of resource '${url}' in context of ${fromFile}`);
33552 }
33553 return failedLookup.failedLookupLocations
33554 .filter(candidate => candidate.endsWith(RESOURCE_MARKER_TS))
33555 .map(candidate => candidate.slice(0, -RESOURCE_MARKER_TS.length));
33556 }
33557 }
33558 /**
33559 * Derives a `ts.ModuleResolutionHost` from a compiler adapter that recognizes the special resource
33560 * marker and does not go to the filesystem for these requests, as they are known not to exist.
33561 */
33562 function createLookupResolutionHost(adapter) {
33563 return {
33564 directoryExists(directoryName) {
33565 if (directoryName.includes(RESOURCE_MARKER)) {
33566 return false;
33567 }
33568 else if (adapter.directoryExists !== undefined) {
33569 return adapter.directoryExists(directoryName);
33570 }
33571 else {
33572 // TypeScript's module resolution logic assumes that the directory exists when no host
33573 // implementation is available.
33574 return true;
33575 }
33576 },
33577 fileExists(fileName) {
33578 if (fileName.includes(RESOURCE_MARKER)) {
33579 return false;
33580 }
33581 else {
33582 return adapter.fileExists(fileName);
33583 }
33584 },
33585 readFile: adapter.readFile.bind(adapter),
33586 getCurrentDirectory: adapter.getCurrentDirectory.bind(adapter),
33587 getDirectories: adapter.getDirectories?.bind(adapter),
33588 realpath: adapter.realpath?.bind(adapter),
33589 trace: adapter.trace?.bind(adapter),
33590 };
33591 }
33592
33593 /**
33594 * @license
33595 * Copyright Google LLC All Rights Reserved.
33596 *
33597 * Use of this source code is governed by an MIT-style license that can be
33598 * found in the LICENSE file at https://angular.io/license
33599 */
33600 /**
33601 * Reads Angular metadata from classes declared in .d.ts files and computes an `ExportScope`.
33602 *
33603 * Given an NgModule declared in a .d.ts file, this resolver can produce a transitive `ExportScope`
33604 * of all of the directives/pipes it exports. It does this by reading metadata off of Ivy static
33605 * fields on directives, components, pipes, and NgModules.
33606 */
33607 class MetadataDtsModuleScopeResolver {
33608 /**
33609 * @param dtsMetaReader a `MetadataReader` which can read metadata from `.d.ts` files.
33610 */
33611 constructor(dtsMetaReader, aliasingHost) {
33612 this.dtsMetaReader = dtsMetaReader;
33613 this.aliasingHost = aliasingHost;
33614 /**
33615 * Cache which holds fully resolved scopes for NgModule classes from .d.ts files.
33616 */
33617 this.cache = new Map();
33618 }
33619 /**
33620 * Resolve a `Reference`'d NgModule from a .d.ts file and produce a transitive `ExportScope`
33621 * listing the directives and pipes which that NgModule exports to others.
33622 *
33623 * This operation relies on a `Reference` instead of a direct TypeScrpt node as the `Reference`s
33624 * produced depend on how the original NgModule was imported.
33625 */
33626 resolve(ref) {
33627 const clazz = ref.node;
33628 const sourceFile = clazz.getSourceFile();
33629 if (!sourceFile.isDeclarationFile) {
33630 throw new Error(`Debug error: DtsModuleScopeResolver.read(${ref.debugName} from ${sourceFile.fileName}), but not a .d.ts file`);
33631 }
33632 if (this.cache.has(clazz)) {
33633 return this.cache.get(clazz);
33634 }
33635 // Build up the export scope - those directives and pipes made visible by this module.
33636 const directives = [];
33637 const pipes = [];
33638 const ngModules = new Set([clazz]);
33639 const meta = this.dtsMetaReader.getNgModuleMetadata(ref);
33640 if (meta === null) {
33641 this.cache.set(clazz, null);
33642 return null;
33643 }
33644 const declarations = new Set();
33645 for (const declRef of meta.declarations) {
33646 declarations.add(declRef.node);
33647 }
33648 // Only the 'exports' field of the NgModule's metadata is important. Imports and declarations
33649 // don't affect the export scope.
33650 for (const exportRef of meta.exports) {
33651 // Attempt to process the export as a directive.
33652 const directive = this.dtsMetaReader.getDirectiveMetadata(exportRef);
33653 if (directive !== null) {
33654 const isReExport = !declarations.has(exportRef.node);
33655 directives.push(this.maybeAlias(directive, sourceFile, isReExport));
33656 continue;
33657 }
33658 // Attempt to process the export as a pipe.
33659 const pipe = this.dtsMetaReader.getPipeMetadata(exportRef);
33660 if (pipe !== null) {
33661 const isReExport = !declarations.has(exportRef.node);
33662 pipes.push(this.maybeAlias(pipe, sourceFile, isReExport));
33663 continue;
33664 }
33665 // Attempt to process the export as a module.
33666 const exportScope = this.resolve(exportRef);
33667 if (exportScope !== null) {
33668 // It is a module. Add exported directives and pipes to the current scope. This might
33669 // involve rewriting the `Reference`s to those types to have an alias expression if one is
33670 // required.
33671 if (this.aliasingHost === null) {
33672 // Fast path when aliases aren't required.
33673 directives.push(...exportScope.exported.directives);
33674 pipes.push(...exportScope.exported.pipes);
33675 }
33676 else {
33677 // It's necessary to rewrite the `Reference`s to add alias expressions. This way, imports
33678 // generated to these directives and pipes will use a shallow import to `sourceFile`
33679 // instead of a deep import directly to the directive or pipe class.
33680 //
33681 // One important check here is whether the directive/pipe is declared in the same
33682 // source file as the re-exporting NgModule. This can happen if both a directive, its
33683 // NgModule, and the re-exporting NgModule are all in the same file. In this case,
33684 // no import alias is needed as it would go to the same file anyway.
33685 for (const directive of exportScope.exported.directives) {
33686 directives.push(this.maybeAlias(directive, sourceFile, /* isReExport */ true));
33687 }
33688 for (const pipe of exportScope.exported.pipes) {
33689 pipes.push(this.maybeAlias(pipe, sourceFile, /* isReExport */ true));
33690 }
33691 for (const ngModule of exportScope.exported.ngModules) {
33692 ngModules.add(ngModule);
33693 }
33694 }
33695 }
33696 continue;
33697 // The export was not a directive, a pipe, or a module. This is an error.
33698 // TODO(alxhub): produce a ts.Diagnostic
33699 }
33700 const exportScope = {
33701 exported: {
33702 directives,
33703 pipes,
33704 ngModules: Array.from(ngModules),
33705 isPoisoned: false,
33706 },
33707 };
33708 this.cache.set(clazz, exportScope);
33709 return exportScope;
33710 }
33711 maybeAlias(dirOrPipe, maybeAliasFrom, isReExport) {
33712 const ref = dirOrPipe.ref;
33713 if (this.aliasingHost === null || ref.node.getSourceFile() === maybeAliasFrom) {
33714 return dirOrPipe;
33715 }
33716 const alias = this.aliasingHost.getAliasIn(ref.node, maybeAliasFrom, isReExport);
33717 if (alias === null) {
33718 return dirOrPipe;
33719 }
33720 // TypeScript incorrectly narrows the type here:
33721 // https://github.com/microsoft/TypeScript/issues/43966.
33722 // TODO: Remove/Update once https://github.com/microsoft/TypeScript/issues/43966 is resolved.
33723 return {
33724 ...dirOrPipe,
33725 ref: ref.cloneWithAlias(alias),
33726 };
33727 }
33728 }
33729
33730 /**
33731 * @license
33732 * Copyright Google LLC All Rights Reserved.
33733 *
33734 * Use of this source code is governed by an MIT-style license that can be
33735 * found in the LICENSE file at https://angular.io/license
33736 */
33737 /**
33738 * A registry which collects information about NgModules, Directives, Components, and Pipes which
33739 * are local (declared in the ts.Program being compiled), and can produce `LocalModuleScope`s
33740 * which summarize the compilation scope of a component.
33741 *
33742 * This class implements the logic of NgModule declarations, imports, and exports and can produce,
33743 * for a given component, the set of directives and pipes which are "visible" in that component's
33744 * template.
33745 *
33746 * The `LocalModuleScopeRegistry` has two "modes" of operation. During analysis, data for each
33747 * individual NgModule, Directive, Component, and Pipe is added to the registry. No attempt is made
33748 * to traverse or validate the NgModule graph (imports, exports, etc). After analysis, one of
33749 * `getScopeOfModule` or `getScopeForComponent` can be called, which traverses the NgModule graph
33750 * and applies the NgModule logic to generate a `LocalModuleScope`, the full scope for the given
33751 * module or component.
33752 *
33753 * The `LocalModuleScopeRegistry` is also capable of producing `ts.Diagnostic` errors when Angular
33754 * semantics are violated.
33755 */
33756 class LocalModuleScopeRegistry {
33757 constructor(localReader, dependencyScopeReader, refEmitter, aliasingHost) {
33758 this.localReader = localReader;
33759 this.dependencyScopeReader = dependencyScopeReader;
33760 this.refEmitter = refEmitter;
33761 this.aliasingHost = aliasingHost;
33762 /**
33763 * Tracks whether the registry has been asked to produce scopes for a module or component. Once
33764 * this is true, the registry cannot accept registrations of new directives/pipes/modules as it
33765 * would invalidate the cached scope data.
33766 */
33767 this.sealed = false;
33768 /**
33769 * A map of components from the current compilation unit to the NgModule which declared them.
33770 *
33771 * As components and directives are not distinguished at the NgModule level, this map may also
33772 * contain directives. This doesn't cause any problems but isn't useful as there is no concept of
33773 * a directive's compilation scope.
33774 */
33775 this.declarationToModule = new Map();
33776 /**
33777 * This maps from the directive/pipe class to a map of data for each NgModule that declares the
33778 * directive/pipe. This data is needed to produce an error for the given class.
33779 */
33780 this.duplicateDeclarations = new Map();
33781 this.moduleToRef = new Map();
33782 /**
33783 * A cache of calculated `LocalModuleScope`s for each NgModule declared in the current program.
33784
33785 */
33786 this.cache = new Map();
33787 /**
33788 * Tracks the `RemoteScope` for components requiring "remote scoping".
33789 *
33790 * Remote scoping is when the set of directives which apply to a given component is set in the
33791 * NgModule's file instead of directly on the component def (which is sometimes needed to get
33792 * around cyclic import issues). This is not used in calculation of `LocalModuleScope`s, but is
33793 * tracked here for convenience.
33794 */
33795 this.remoteScoping = new Map();
33796 /**
33797 * Tracks errors accumulated in the processing of scopes for each module declaration.
33798 */
33799 this.scopeErrors = new Map();
33800 /**
33801 * Tracks which NgModules have directives/pipes that are declared in more than one module.
33802 */
33803 this.modulesWithStructuralErrors = new Set();
33804 }
33805 /**
33806 * Add an NgModule's data to the registry.
33807 */
33808 registerNgModuleMetadata(data) {
33809 this.assertCollecting();
33810 const ngModule = data.ref.node;
33811 this.moduleToRef.set(data.ref.node, data.ref);
33812 // Iterate over the module's declarations, and add them to declarationToModule. If duplicates
33813 // are found, they're instead tracked in duplicateDeclarations.
33814 for (const decl of data.declarations) {
33815 this.registerDeclarationOfModule(ngModule, decl, data.rawDeclarations);
33816 }
33817 }
33818 registerDirectiveMetadata(directive) { }
33819 registerPipeMetadata(pipe) { }
33820 getScopeForComponent(clazz) {
33821 const scope = !this.declarationToModule.has(clazz) ?
33822 null :
33823 this.getScopeOfModule(this.declarationToModule.get(clazz).ngModule);
33824 return scope;
33825 }
33826 /**
33827 * If `node` is declared in more than one NgModule (duplicate declaration), then get the
33828 * `DeclarationData` for each offending declaration.
33829 *
33830 * Ordinarily a class is only declared in one NgModule, in which case this function returns
33831 * `null`.
33832 */
33833 getDuplicateDeclarations(node) {
33834 if (!this.duplicateDeclarations.has(node)) {
33835 return null;
33836 }
33837 return Array.from(this.duplicateDeclarations.get(node).values());
33838 }
33839 /**
33840 * Collects registered data for a module and its directives/pipes and convert it into a full
33841 * `LocalModuleScope`.
33842 *
33843 * This method implements the logic of NgModule imports and exports. It returns the
33844 * `LocalModuleScope` for the given NgModule if one can be produced, `null` if no scope was ever
33845 * defined, or the string `'error'` if the scope contained errors.
33846 */
33847 getScopeOfModule(clazz) {
33848 return this.moduleToRef.has(clazz) ?
33849 this.getScopeOfModuleReference(this.moduleToRef.get(clazz)) :
33850 null;
33851 }
33852 /**
33853 * Retrieves any `ts.Diagnostic`s produced during the calculation of the `LocalModuleScope` for
33854 * the given NgModule, or `null` if no errors were present.
33855 */
33856 getDiagnosticsOfModule(clazz) {
33857 // Required to ensure the errors are populated for the given class. If it has been processed
33858 // before, this will be a no-op due to the scope cache.
33859 this.getScopeOfModule(clazz);
33860 if (this.scopeErrors.has(clazz)) {
33861 return this.scopeErrors.get(clazz);
33862 }
33863 else {
33864 return null;
33865 }
33866 }
33867 registerDeclarationOfModule(ngModule, decl, rawDeclarations) {
33868 const declData = {
33869 ngModule,
33870 ref: decl,
33871 rawDeclarations,
33872 };
33873 // First, check for duplicate declarations of the same directive/pipe.
33874 if (this.duplicateDeclarations.has(decl.node)) {
33875 // This directive/pipe has already been identified as being duplicated. Add this module to the
33876 // map of modules for which a duplicate declaration exists.
33877 this.duplicateDeclarations.get(decl.node).set(ngModule, declData);
33878 }
33879 else if (this.declarationToModule.has(decl.node) &&
33880 this.declarationToModule.get(decl.node).ngModule !== ngModule) {
33881 // This directive/pipe is already registered as declared in another module. Mark it as a
33882 // duplicate instead.
33883 const duplicateDeclMap = new Map();
33884 const firstDeclData = this.declarationToModule.get(decl.node);
33885 // Mark both modules as having duplicate declarations.
33886 this.modulesWithStructuralErrors.add(firstDeclData.ngModule);
33887 this.modulesWithStructuralErrors.add(ngModule);
33888 // Being detected as a duplicate means there are two NgModules (for now) which declare this
33889 // directive/pipe. Add both of them to the duplicate tracking map.
33890 duplicateDeclMap.set(firstDeclData.ngModule, firstDeclData);
33891 duplicateDeclMap.set(ngModule, declData);
33892 this.duplicateDeclarations.set(decl.node, duplicateDeclMap);
33893 // Remove the directive/pipe from `declarationToModule` as it's a duplicate declaration, and
33894 // therefore not valid.
33895 this.declarationToModule.delete(decl.node);
33896 }
33897 else {
33898 // This is the first declaration of this directive/pipe, so map it.
33899 this.declarationToModule.set(decl.node, declData);
33900 }
33901 }
33902 /**
33903 * Implementation of `getScopeOfModule` which accepts a reference to a class.
33904 */
33905 getScopeOfModuleReference(ref) {
33906 if (this.cache.has(ref.node)) {
33907 return this.cache.get(ref.node);
33908 }
33909 // Seal the registry to protect the integrity of the `LocalModuleScope` cache.
33910 this.sealed = true;
33911 // `ref` should be an NgModule previously added to the registry. If not, a scope for it
33912 // cannot be produced.
33913 const ngModule = this.localReader.getNgModuleMetadata(ref);
33914 if (ngModule === null) {
33915 this.cache.set(ref.node, null);
33916 return null;
33917 }
33918 // Modules which contributed to the compilation scope of this module.
33919 const compilationModules = new Set([ngModule.ref.node]);
33920 // Modules which contributed to the export scope of this module.
33921 const exportedModules = new Set([ngModule.ref.node]);
33922 // Errors produced during computation of the scope are recorded here. At the end, if this array
33923 // isn't empty then `undefined` will be cached and returned to indicate this scope is invalid.
33924 const diagnostics = [];
33925 // At this point, the goal is to produce two distinct transitive sets:
33926 // - the directives and pipes which are visible to components declared in the NgModule.
33927 // - the directives and pipes which are exported to any NgModules which import this one.
33928 // Directives and pipes in the compilation scope.
33929 const compilationDirectives = new Map();
33930 const compilationPipes = new Map();
33931 const declared = new Set();
33932 // Directives and pipes exported to any importing NgModules.
33933 const exportDirectives = new Map();
33934 const exportPipes = new Map();
33935 // The algorithm is as follows:
33936 // 1) Add all of the directives/pipes from each NgModule imported into the current one to the
33937 // compilation scope.
33938 // 2) Add directives/pipes declared in the NgModule to the compilation scope. At this point, the
33939 // compilation scope is complete.
33940 // 3) For each entry in the NgModule's exports:
33941 // a) Attempt to resolve it as an NgModule with its own exported directives/pipes. If it is
33942 // one, add them to the export scope of this NgModule.
33943 // b) Otherwise, it should be a class in the compilation scope of this NgModule. If it is,
33944 // add it to the export scope.
33945 // c) If it's neither an NgModule nor a directive/pipe in the compilation scope, then this
33946 // is an error.
33947 //
33948 let isPoisoned = false;
33949 if (this.modulesWithStructuralErrors.has(ngModule.ref.node)) {
33950 // If the module contains declarations that are duplicates, then it's considered poisoned.
33951 isPoisoned = true;
33952 }
33953 // 1) process imports.
33954 for (const decl of ngModule.imports) {
33955 const importScope = this.getExportedScope(decl, diagnostics, ref.node, 'import');
33956 if (importScope === null) {
33957 // An import wasn't an NgModule, so record an error.
33958 diagnostics.push(invalidRef(ref.node, decl, 'import'));
33959 isPoisoned = true;
33960 continue;
33961 }
33962 else if (importScope === 'invalid' || importScope.exported.isPoisoned) {
33963 // An import was an NgModule but contained errors of its own. Record this as an error too,
33964 // because this scope is always going to be incorrect if one of its imports could not be
33965 // read.
33966 diagnostics.push(invalidTransitiveNgModuleRef(ref.node, decl, 'import'));
33967 isPoisoned = true;
33968 if (importScope === 'invalid') {
33969 continue;
33970 }
33971 }
33972 for (const directive of importScope.exported.directives) {
33973 compilationDirectives.set(directive.ref.node, directive);
33974 }
33975 for (const pipe of importScope.exported.pipes) {
33976 compilationPipes.set(pipe.ref.node, pipe);
33977 }
33978 for (const importedModule of importScope.exported.ngModules) {
33979 compilationModules.add(importedModule);
33980 }
33981 }
33982 // 2) add declarations.
33983 for (const decl of ngModule.declarations) {
33984 const directive = this.localReader.getDirectiveMetadata(decl);
33985 const pipe = this.localReader.getPipeMetadata(decl);
33986 if (directive !== null) {
33987 compilationDirectives.set(decl.node, { ...directive, ref: decl });
33988 if (directive.isPoisoned) {
33989 isPoisoned = true;
33990 }
33991 }
33992 else if (pipe !== null) {
33993 compilationPipes.set(decl.node, { ...pipe, ref: decl });
33994 }
33995 else {
33996 const errorNode = decl.getOriginForDiagnostics(ngModule.rawDeclarations);
33997 diagnostics.push(makeDiagnostic(ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, `The class '${decl.node.name.text}' is listed in the declarations ` +
33998 `of the NgModule '${ngModule.ref.node.name
33999 .text}', but is not a directive, a component, or a pipe. ` +
34000 `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.`)]));
34001 isPoisoned = true;
34002 continue;
34003 }
34004 declared.add(decl.node);
34005 }
34006 // 3) process exports.
34007 // Exports can contain modules, components, or directives. They're processed differently.
34008 // Modules are straightforward. Directives and pipes from exported modules are added to the
34009 // export maps. Directives/pipes are different - they might be exports of declared types or
34010 // imported types.
34011 for (const decl of ngModule.exports) {
34012 // Attempt to resolve decl as an NgModule.
34013 const exportScope = this.getExportedScope(decl, diagnostics, ref.node, 'export');
34014 if (exportScope === 'invalid' || (exportScope !== null && exportScope.exported.isPoisoned)) {
34015 // An export was an NgModule but contained errors of its own. Record this as an error too,
34016 // because this scope is always going to be incorrect if one of its exports could not be
34017 // read.
34018 diagnostics.push(invalidTransitiveNgModuleRef(ref.node, decl, 'export'));
34019 isPoisoned = true;
34020 if (exportScope === 'invalid') {
34021 continue;
34022 }
34023 }
34024 else if (exportScope !== null) {
34025 // decl is an NgModule.
34026 for (const directive of exportScope.exported.directives) {
34027 exportDirectives.set(directive.ref.node, directive);
34028 }
34029 for (const pipe of exportScope.exported.pipes) {
34030 exportPipes.set(pipe.ref.node, pipe);
34031 }
34032 for (const exportedModule of exportScope.exported.ngModules) {
34033 exportedModules.add(exportedModule);
34034 }
34035 }
34036 else if (compilationDirectives.has(decl.node)) {
34037 // decl is a directive or component in the compilation scope of this NgModule.
34038 const directive = compilationDirectives.get(decl.node);
34039 exportDirectives.set(decl.node, directive);
34040 }
34041 else if (compilationPipes.has(decl.node)) {
34042 // decl is a pipe in the compilation scope of this NgModule.
34043 const pipe = compilationPipes.get(decl.node);
34044 exportPipes.set(decl.node, pipe);
34045 }
34046 else {
34047 // decl is an unknown export.
34048 if (this.localReader.getDirectiveMetadata(decl) !== null ||
34049 this.localReader.getPipeMetadata(decl) !== null) {
34050 diagnostics.push(invalidReexport(ref.node, decl));
34051 }
34052 else {
34053 diagnostics.push(invalidRef(ref.node, decl, 'export'));
34054 }
34055 isPoisoned = true;
34056 continue;
34057 }
34058 }
34059 const exported = {
34060 directives: Array.from(exportDirectives.values()),
34061 pipes: Array.from(exportPipes.values()),
34062 ngModules: Array.from(exportedModules),
34063 isPoisoned,
34064 };
34065 const reexports = this.getReexports(ngModule, ref, declared, exported, diagnostics);
34066 // Finally, produce the `LocalModuleScope` with both the compilation and export scopes.
34067 const scope = {
34068 ngModule: ngModule.ref.node,
34069 compilation: {
34070 directives: Array.from(compilationDirectives.values()),
34071 pipes: Array.from(compilationPipes.values()),
34072 ngModules: Array.from(compilationModules),
34073 isPoisoned,
34074 },
34075 exported,
34076 reexports,
34077 schemas: ngModule.schemas,
34078 };
34079 // Check if this scope had any errors during production.
34080 if (diagnostics.length > 0) {
34081 // Save the errors for retrieval.
34082 this.scopeErrors.set(ref.node, diagnostics);
34083 // Mark this module as being tainted.
34084 this.modulesWithStructuralErrors.add(ref.node);
34085 }
34086 this.cache.set(ref.node, scope);
34087 return scope;
34088 }
34089 /**
34090 * Check whether a component requires remote scoping.
34091 */
34092 getRemoteScope(node) {
34093 return this.remoteScoping.has(node) ? this.remoteScoping.get(node) : null;
34094 }
34095 /**
34096 * Set a component as requiring remote scoping, with the given directives and pipes to be
34097 * registered remotely.
34098 */
34099 setComponentRemoteScope(node, directives, pipes) {
34100 this.remoteScoping.set(node, { directives, pipes });
34101 }
34102 /**
34103 * Look up the `ExportScope` of a given `Reference` to an NgModule.
34104 *
34105 * The NgModule in question may be declared locally in the current ts.Program, or it may be
34106 * declared in a .d.ts file.
34107 *
34108 * @returns `null` if no scope could be found, or `'invalid'` if the `Reference` is not a valid
34109 * NgModule.
34110 *
34111 * May also contribute diagnostics of its own by adding to the given `diagnostics`
34112 * array parameter.
34113 */
34114 getExportedScope(ref, diagnostics, ownerForErrors, type) {
34115 if (ref.node.getSourceFile().isDeclarationFile) {
34116 // The NgModule is declared in a .d.ts file. Resolve it with the `DependencyScopeReader`.
34117 if (!ts__default["default"].isClassDeclaration(ref.node)) {
34118 // The NgModule is in a .d.ts file but is not declared as a ts.ClassDeclaration. This is an
34119 // error in the .d.ts metadata.
34120 const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT :
34121 ErrorCode.NGMODULE_INVALID_EXPORT;
34122 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`));
34123 return 'invalid';
34124 }
34125 return this.dependencyScopeReader.resolve(ref);
34126 }
34127 else {
34128 // The NgModule is declared locally in the current program. Resolve it from the registry.
34129 return this.getScopeOfModuleReference(ref);
34130 }
34131 }
34132 getReexports(ngModule, ref, declared, exported, diagnostics) {
34133 let reexports = null;
34134 const sourceFile = ref.node.getSourceFile();
34135 if (this.aliasingHost === null) {
34136 return null;
34137 }
34138 reexports = [];
34139 // Track re-exports by symbol name, to produce diagnostics if two alias re-exports would share
34140 // the same name.
34141 const reexportMap = new Map();
34142 // Alias ngModuleRef added for readability below.
34143 const ngModuleRef = ref;
34144 const addReexport = (exportRef) => {
34145 if (exportRef.node.getSourceFile() === sourceFile) {
34146 return;
34147 }
34148 const isReExport = !declared.has(exportRef.node);
34149 const exportName = this.aliasingHost.maybeAliasSymbolAs(exportRef, sourceFile, ngModule.ref.node.name.text, isReExport);
34150 if (exportName === null) {
34151 return;
34152 }
34153 if (!reexportMap.has(exportName)) {
34154 if (exportRef.alias && exportRef.alias instanceof ExternalExpr) {
34155 reexports.push({
34156 fromModule: exportRef.alias.value.moduleName,
34157 symbolName: exportRef.alias.value.name,
34158 asAlias: exportName,
34159 });
34160 }
34161 else {
34162 const expr = this.refEmitter.emit(exportRef.cloneWithNoIdentifiers(), sourceFile).expression;
34163 if (!(expr instanceof ExternalExpr) || expr.value.moduleName === null ||
34164 expr.value.name === null) {
34165 throw new Error('Expected ExternalExpr');
34166 }
34167 reexports.push({
34168 fromModule: expr.value.moduleName,
34169 symbolName: expr.value.name,
34170 asAlias: exportName,
34171 });
34172 }
34173 reexportMap.set(exportName, exportRef);
34174 }
34175 else {
34176 // Another re-export already used this name. Produce a diagnostic.
34177 const prevRef = reexportMap.get(exportName);
34178 diagnostics.push(reexportCollision(ngModuleRef.node, prevRef, exportRef));
34179 }
34180 };
34181 for (const { ref } of exported.directives) {
34182 addReexport(ref);
34183 }
34184 for (const { ref } of exported.pipes) {
34185 addReexport(ref);
34186 }
34187 return reexports;
34188 }
34189 assertCollecting() {
34190 if (this.sealed) {
34191 throw new Error(`Assertion: LocalModuleScopeRegistry is not COLLECTING`);
34192 }
34193 }
34194 }
34195 /**
34196 * Produce a `ts.Diagnostic` for an invalid import or export from an NgModule.
34197 */
34198 function invalidRef(clazz, decl, type) {
34199 const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT;
34200 const resolveTarget = type === 'import' ? 'NgModule' : 'NgModule, Component, Directive, or Pipe';
34201 let message = `Appears in the NgModule.${type}s of ${nodeNameForError(clazz)}, but could not be resolved to an ${resolveTarget} class.` +
34202 '\n\n';
34203 const library = decl.ownedByModuleGuess !== null ? ` (${decl.ownedByModuleGuess})` : '';
34204 const sf = decl.node.getSourceFile();
34205 // Provide extra context to the error for the user.
34206 if (!sf.isDeclarationFile) {
34207 // This is a file in the user's program.
34208 const annotationType = type === 'import' ? '@NgModule' : 'Angular';
34209 message += `Is it missing an ${annotationType} annotation?`;
34210 }
34211 else if (sf.fileName.indexOf('node_modules') !== -1) {
34212 // This file comes from a third-party library in node_modules.
34213 message +=
34214 `This likely means that the library${library} which declares ${decl.debugName} has not ` +
34215 'been processed correctly by ngcc, or is not compatible with Angular Ivy. Check if a ' +
34216 'newer version of the library is available, and update if so. Also consider checking ' +
34217 'with the library\'s authors to see if the library is expected to be compatible with Ivy.';
34218 }
34219 else {
34220 // This is a monorepo style local dependency. Unfortunately these are too different to really
34221 // offer much more advice than this.
34222 message += `This likely means that the dependency${library} which declares ${decl.debugName} has not been processed correctly by ngcc.`;
34223 }
34224 return makeDiagnostic(code, identifierOfNode(decl.node) || decl.node, message);
34225 }
34226 /**
34227 * Produce a `ts.Diagnostic` for an import or export which itself has errors.
34228 */
34229 function invalidTransitiveNgModuleRef(clazz, decl, type) {
34230 const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT;
34231 return makeDiagnostic(code, identifierOfNode(decl.node) || decl.node, `Appears in the NgModule.${type}s of ${nodeNameForError(clazz)}, but itself has errors`);
34232 }
34233 /**
34234 * Produce a `ts.Diagnostic` for an exported directive or pipe which was not declared or imported
34235 * by the NgModule in question.
34236 */
34237 function invalidReexport(clazz, decl) {
34238 return makeDiagnostic(ErrorCode.NGMODULE_INVALID_REEXPORT, identifierOfNode(decl.node) || decl.node, `Present in the NgModule.exports of ${nodeNameForError(clazz)} but neither declared nor imported`);
34239 }
34240 /**
34241 * Produce a `ts.Diagnostic` for a collision in re-export names between two directives/pipes.
34242 */
34243 function reexportCollision(module, refA, refB) {
34244 const childMessageText = `This directive/pipe is part of the exports of '${module.name.text}' and shares the same name as another exported directive/pipe.`;
34245 return makeDiagnostic(ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION, module.name, `
34246 There was a name collision between two classes named '${refA.node.name.text}', which are both part of the exports of '${module.name.text}'.
34247
34248 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.
34249
34250 To fix this problem please re-export one or both classes directly from this file.
34251 `.trim(), [
34252 makeRelatedInformation(refA.node.name, childMessageText),
34253 makeRelatedInformation(refB.node.name, childMessageText),
34254 ]);
34255 }
34256
34257 /**
34258 * @license
34259 * Copyright Google LLC All Rights Reserved.
34260 *
34261 * Use of this source code is governed by an MIT-style license that can be
34262 * found in the LICENSE file at https://angular.io/license
34263 */
34264 /**
34265 * Computes scope information to be used in template type checking.
34266 */
34267 class TypeCheckScopeRegistry {
34268 constructor(scopeReader, metaReader) {
34269 this.scopeReader = scopeReader;
34270 this.metaReader = metaReader;
34271 /**
34272 * Cache of flattened directive metadata. Because flattened metadata is scope-invariant it's
34273 * cached individually, such that all scopes refer to the same flattened metadata.
34274 */
34275 this.flattenedDirectiveMetaCache = new Map();
34276 /**
34277 * Cache of the computed type check scope per NgModule declaration.
34278 */
34279 this.scopeCache = new Map();
34280 }
34281 /**
34282 * Computes the type-check scope information for the component declaration. If the NgModule
34283 * contains an error, then 'error' is returned. If the component is not declared in any NgModule,
34284 * an empty type-check scope is returned.
34285 */
34286 getTypeCheckScope(node) {
34287 const matcher = new SelectorMatcher();
34288 const directives = [];
34289 const pipes = new Map();
34290 const scope = this.scopeReader.getScopeForComponent(node);
34291 if (scope === null) {
34292 return {
34293 matcher,
34294 directives,
34295 pipes,
34296 schemas: [],
34297 isPoisoned: false,
34298 };
34299 }
34300 if (this.scopeCache.has(scope.ngModule)) {
34301 return this.scopeCache.get(scope.ngModule);
34302 }
34303 for (const meta of scope.compilation.directives) {
34304 if (meta.selector !== null) {
34305 const extMeta = this.getTypeCheckDirectiveMetadata(meta.ref);
34306 matcher.addSelectables(CssSelector.parse(meta.selector), extMeta);
34307 directives.push(extMeta);
34308 }
34309 }
34310 for (const { name, ref } of scope.compilation.pipes) {
34311 if (!ts__default["default"].isClassDeclaration(ref.node)) {
34312 throw new Error(`Unexpected non-class declaration ${ts__default["default"].SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`);
34313 }
34314 pipes.set(name, ref);
34315 }
34316 const typeCheckScope = {
34317 matcher,
34318 directives,
34319 pipes,
34320 schemas: scope.schemas,
34321 isPoisoned: scope.compilation.isPoisoned || scope.exported.isPoisoned,
34322 };
34323 this.scopeCache.set(scope.ngModule, typeCheckScope);
34324 return typeCheckScope;
34325 }
34326 getTypeCheckDirectiveMetadata(ref) {
34327 const clazz = ref.node;
34328 if (this.flattenedDirectiveMetaCache.has(clazz)) {
34329 return this.flattenedDirectiveMetaCache.get(clazz);
34330 }
34331 const meta = flattenInheritedDirectiveMetadata(this.metaReader, ref);
34332 this.flattenedDirectiveMetaCache.set(clazz, meta);
34333 return meta;
34334 }
34335 }
34336
34337 /**
34338 * @license
34339 * Copyright Google LLC All Rights Reserved.
34340 *
34341 * Use of this source code is governed by an MIT-style license that can be
34342 * found in the LICENSE file at https://angular.io/license
34343 */
34344 const IVY_SWITCH_PRE_SUFFIX = '__PRE_R3__';
34345 const IVY_SWITCH_POST_SUFFIX = '__POST_R3__';
34346 function ivySwitchTransform(_) {
34347 return flipIvySwitchInFile;
34348 }
34349 function flipIvySwitchInFile(sf) {
34350 // To replace the statements array, it must be copied. This only needs to happen if a statement
34351 // must actually be replaced within the array, so the newStatements array is lazily initialized.
34352 let newStatements = undefined;
34353 // Iterate over the statements in the file.
34354 for (let i = 0; i < sf.statements.length; i++) {
34355 const statement = sf.statements[i];
34356 // Skip over everything that isn't a variable statement.
34357 if (!ts__default["default"].isVariableStatement(statement) || !hasIvySwitches(statement)) {
34358 continue;
34359 }
34360 // This statement needs to be replaced. Check if the newStatements array needs to be lazily
34361 // initialized to a copy of the original statements.
34362 if (newStatements === undefined) {
34363 newStatements = [...sf.statements];
34364 }
34365 // Flip any switches in the VariableStatement. If there were any, a new statement will be
34366 // returned; otherwise the old statement will be.
34367 newStatements[i] = flipIvySwitchesInVariableStatement(statement, sf.statements);
34368 }
34369 // Only update the statements in the SourceFile if any have changed.
34370 if (newStatements !== undefined) {
34371 return ts__default["default"].updateSourceFileNode(sf, newStatements);
34372 }
34373 return sf;
34374 }
34375 /**
34376 * Look for the ts.Identifier of a ts.Declaration with this name.
34377 *
34378 * The real identifier is needed (rather than fabricating one) as TypeScript decides how to
34379 * reference this identifier based on information stored against its node in the AST, which a
34380 * synthetic node would not have. In particular, since the post-switch variable is often exported,
34381 * TypeScript needs to know this so it can write `exports.VAR` instead of just `VAR` when emitting
34382 * code.
34383 *
34384 * Only variable, function, and class declarations are currently searched.
34385 */
34386 function findPostSwitchIdentifier(statements, name) {
34387 for (const stmt of statements) {
34388 if (ts__default["default"].isVariableStatement(stmt)) {
34389 const decl = stmt.declarationList.declarations.find(decl => ts__default["default"].isIdentifier(decl.name) && decl.name.text === name);
34390 if (decl !== undefined) {
34391 return decl.name;
34392 }
34393 }
34394 else if (ts__default["default"].isFunctionDeclaration(stmt) || ts__default["default"].isClassDeclaration(stmt)) {
34395 if (stmt.name !== undefined && ts__default["default"].isIdentifier(stmt.name) && stmt.name.text === name) {
34396 return stmt.name;
34397 }
34398 }
34399 }
34400 return null;
34401 }
34402 /**
34403 * Flip any Ivy switches which are discovered in the given ts.VariableStatement.
34404 */
34405 function flipIvySwitchesInVariableStatement(stmt, statements) {
34406 // Build a new list of variable declarations. Specific declarations that are initialized to a
34407 // pre-switch identifier will be replaced with a declaration initialized to the post-switch
34408 // identifier.
34409 const newDeclarations = [...stmt.declarationList.declarations];
34410 for (let i = 0; i < newDeclarations.length; i++) {
34411 const decl = newDeclarations[i];
34412 // Skip declarations that aren't initialized to an identifier.
34413 if (decl.initializer === undefined || !ts__default["default"].isIdentifier(decl.initializer)) {
34414 continue;
34415 }
34416 // Skip declarations that aren't Ivy switches.
34417 if (!decl.initializer.text.endsWith(IVY_SWITCH_PRE_SUFFIX)) {
34418 continue;
34419 }
34420 // Determine the name of the post-switch variable.
34421 const postSwitchName = decl.initializer.text.replace(IVY_SWITCH_PRE_SUFFIX, IVY_SWITCH_POST_SUFFIX);
34422 // Find the post-switch variable identifier. If one can't be found, it's an error. This is
34423 // reported as a thrown error and not a diagnostic as transformers cannot output diagnostics.
34424 const newIdentifier = findPostSwitchIdentifier(statements, postSwitchName);
34425 if (newIdentifier === null) {
34426 throw new Error(`Unable to find identifier ${postSwitchName} in ${stmt.getSourceFile().fileName} for the Ivy switch.`);
34427 }
34428 newDeclarations[i] = ts__default["default"].updateVariableDeclaration(
34429 /* node */ decl,
34430 /* name */ decl.name,
34431 /* type */ decl.type,
34432 /* initializer */ newIdentifier);
34433 }
34434 const newDeclList = ts__default["default"].updateVariableDeclarationList(
34435 /* declarationList */ stmt.declarationList,
34436 /* declarations */ newDeclarations);
34437 const newStmt = ts__default["default"].updateVariableStatement(
34438 /* statement */ stmt,
34439 /* modifiers */ stmt.modifiers,
34440 /* declarationList */ newDeclList);
34441 return newStmt;
34442 }
34443 /**
34444 * Check whether the given VariableStatement has any Ivy switch variables.
34445 */
34446 function hasIvySwitches(stmt) {
34447 return stmt.declarationList.declarations.some(decl => decl.initializer !== undefined && ts__default["default"].isIdentifier(decl.initializer) &&
34448 decl.initializer.text.endsWith(IVY_SWITCH_PRE_SUFFIX));
34449 }
34450
34451 /**
34452 * @license
34453 * Copyright Google LLC All Rights Reserved.
34454 *
34455 * Use of this source code is governed by an MIT-style license that can be
34456 * found in the LICENSE file at https://angular.io/license
34457 */
34458 /**
34459 * Describes the scope of the caller's interest in template type-checking results.
34460 */
34461 var OptimizeFor;
34462 (function (OptimizeFor) {
34463 /**
34464 * Indicates that a consumer of a `TemplateTypeChecker` is only interested in results for a given
34465 * file, and wants them as fast as possible.
34466 *
34467 * Calling `TemplateTypeChecker` methods successively for multiple files while specifying
34468 * `OptimizeFor.SingleFile` can result in significant unnecessary overhead overall.
34469 */
34470 OptimizeFor[OptimizeFor["SingleFile"] = 0] = "SingleFile";
34471 /**
34472 * Indicates that a consumer of a `TemplateTypeChecker` intends to query for results pertaining to
34473 * the entire user program, and so the type-checker should internally optimize for this case.
34474 *
34475 * Initial calls to retrieve type-checking information may take longer, but repeated calls to
34476 * gather information for the whole user program will be significantly faster with this mode of
34477 * optimization.
34478 */
34479 OptimizeFor[OptimizeFor["WholeProgram"] = 1] = "WholeProgram";
34480 })(OptimizeFor || (OptimizeFor = {}));
34481
34482 /**
34483 * @license
34484 * Copyright Google LLC All Rights Reserved.
34485 *
34486 * Use of this source code is governed by an MIT-style license that can be
34487 * found in the LICENSE file at https://angular.io/license
34488 */
34489 /**
34490 * Discriminant of an autocompletion source (a `Completion`).
34491 */
34492 var CompletionKind;
34493 (function (CompletionKind) {
34494 CompletionKind[CompletionKind["Reference"] = 0] = "Reference";
34495 CompletionKind[CompletionKind["Variable"] = 1] = "Variable";
34496 })(CompletionKind || (CompletionKind = {}));
34497
34498 /**
34499 * @license
34500 * Copyright Google LLC All Rights Reserved.
34501 *
34502 * Use of this source code is governed by an MIT-style license that can be
34503 * found in the LICENSE file at https://angular.io/license
34504 */
34505 var SymbolKind;
34506 (function (SymbolKind) {
34507 SymbolKind[SymbolKind["Input"] = 0] = "Input";
34508 SymbolKind[SymbolKind["Output"] = 1] = "Output";
34509 SymbolKind[SymbolKind["Binding"] = 2] = "Binding";
34510 SymbolKind[SymbolKind["Reference"] = 3] = "Reference";
34511 SymbolKind[SymbolKind["Variable"] = 4] = "Variable";
34512 SymbolKind[SymbolKind["Directive"] = 5] = "Directive";
34513 SymbolKind[SymbolKind["Element"] = 6] = "Element";
34514 SymbolKind[SymbolKind["Template"] = 7] = "Template";
34515 SymbolKind[SymbolKind["Expression"] = 8] = "Expression";
34516 SymbolKind[SymbolKind["DomBinding"] = 9] = "DomBinding";
34517 SymbolKind[SymbolKind["Pipe"] = 10] = "Pipe";
34518 })(SymbolKind || (SymbolKind = {}));
34519
34520 /**
34521 * @license
34522 * Copyright Google LLC All Rights Reserved.
34523 *
34524 * Use of this source code is governed by an MIT-style license that can be
34525 * found in the LICENSE file at https://angular.io/license
34526 */
34527 /**
34528 * Constructs a `ts.Diagnostic` for a given `ParseSourceSpan` within a template.
34529 */
34530 function makeTemplateDiagnostic(templateId, mapping, span, category, code, messageText, relatedMessages) {
34531 if (mapping.type === 'direct') {
34532 let relatedInformation = undefined;
34533 if (relatedMessages !== undefined) {
34534 relatedInformation = [];
34535 for (const relatedMessage of relatedMessages) {
34536 relatedInformation.push({
34537 category: ts__default["default"].DiagnosticCategory.Message,
34538 code: 0,
34539 file: relatedMessage.sourceFile,
34540 start: relatedMessage.start,
34541 length: relatedMessage.end - relatedMessage.start,
34542 messageText: relatedMessage.text,
34543 });
34544 }
34545 }
34546 // For direct mappings, the error is shown inline as ngtsc was able to pinpoint a string
34547 // constant within the `@Component` decorator for the template. This allows us to map the error
34548 // directly into the bytes of the source file.
34549 return {
34550 source: 'ngtsc',
34551 code,
34552 category,
34553 messageText,
34554 file: mapping.node.getSourceFile(),
34555 componentFile: mapping.node.getSourceFile(),
34556 templateId,
34557 start: span.start.offset,
34558 length: span.end.offset - span.start.offset,
34559 relatedInformation,
34560 };
34561 }
34562 else if (mapping.type === 'indirect' || mapping.type === 'external') {
34563 // For indirect mappings (template was declared inline, but ngtsc couldn't map it directly
34564 // to a string constant in the decorator), the component's file name is given with a suffix
34565 // indicating it's not the TS file being displayed, but a template.
34566 // For external temoplates, the HTML filename is used.
34567 const componentSf = mapping.componentClass.getSourceFile();
34568 const componentName = mapping.componentClass.name.text;
34569 // TODO(alxhub): remove cast when TS in g3 supports this narrowing.
34570 const fileName = mapping.type === 'indirect' ?
34571 `${componentSf.fileName} (${componentName} template)` :
34572 mapping.templateUrl;
34573 // TODO(alxhub): investigate creating a fake `ts.SourceFile` here instead of invoking the TS
34574 // parser against the template (HTML is just really syntactically invalid TypeScript code ;).
34575 // Also investigate caching the file to avoid running the parser multiple times.
34576 const sf = ts__default["default"].createSourceFile(fileName, mapping.template, ts__default["default"].ScriptTarget.Latest, false, ts__default["default"].ScriptKind.JSX);
34577 let relatedInformation = [];
34578 if (relatedMessages !== undefined) {
34579 for (const relatedMessage of relatedMessages) {
34580 relatedInformation.push({
34581 category: ts__default["default"].DiagnosticCategory.Message,
34582 code: 0,
34583 file: relatedMessage.sourceFile,
34584 start: relatedMessage.start,
34585 length: relatedMessage.end - relatedMessage.start,
34586 messageText: relatedMessage.text,
34587 });
34588 }
34589 }
34590 relatedInformation.push({
34591 category: ts__default["default"].DiagnosticCategory.Message,
34592 code: 0,
34593 file: componentSf,
34594 // mapping.node represents either the 'template' or 'templateUrl' expression. getStart()
34595 // and getEnd() are used because they don't include surrounding whitespace.
34596 start: mapping.node.getStart(),
34597 length: mapping.node.getEnd() - mapping.node.getStart(),
34598 messageText: `Error occurs in the template of component ${componentName}.`,
34599 });
34600 return {
34601 source: 'ngtsc',
34602 category,
34603 code,
34604 messageText,
34605 file: sf,
34606 componentFile: componentSf,
34607 templateId,
34608 start: span.start.offset,
34609 length: span.end.offset - span.start.offset,
34610 // Show a secondary message indicating the component whose template contains the error.
34611 relatedInformation,
34612 };
34613 }
34614 else {
34615 throw new Error(`Unexpected source mapping type: ${mapping.type}`);
34616 }
34617 }
34618
34619 /**
34620 * @license
34621 * Copyright Google LLC All Rights Reserved.
34622 *
34623 * Use of this source code is governed by an MIT-style license that can be
34624 * found in the LICENSE file at https://angular.io/license
34625 */
34626 const TEMPLATE_ID = Symbol('ngTemplateId');
34627 const NEXT_TEMPLATE_ID = Symbol('ngNextTemplateId');
34628 function getTemplateId$1(clazz) {
34629 const node = clazz;
34630 if (node[TEMPLATE_ID] === undefined) {
34631 node[TEMPLATE_ID] = allocateTemplateId(node.getSourceFile());
34632 }
34633 return node[TEMPLATE_ID];
34634 }
34635 function allocateTemplateId(sf) {
34636 if (sf[NEXT_TEMPLATE_ID] === undefined) {
34637 sf[NEXT_TEMPLATE_ID] = 1;
34638 }
34639 return (`tcb${sf[NEXT_TEMPLATE_ID]++}`);
34640 }
34641
34642 /**
34643 * @license
34644 * Copyright Google LLC All Rights Reserved.
34645 *
34646 * Use of this source code is governed by an MIT-style license that can be
34647 * found in the LICENSE file at https://angular.io/license
34648 */
34649 const parseSpanComment = /^(\d+),(\d+)$/;
34650 /**
34651 * Reads the trailing comments and finds the first match which is a span comment (i.e. 4,10) on a
34652 * node and returns it as an `AbsoluteSourceSpan`.
34653 *
34654 * Will return `null` if no trailing comments on the node match the expected form of a source span.
34655 */
34656 function readSpanComment(node, sourceFile = node.getSourceFile()) {
34657 return ts__default["default"].forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => {
34658 if (kind !== ts__default["default"].SyntaxKind.MultiLineCommentTrivia) {
34659 return null;
34660 }
34661 const commentText = sourceFile.text.substring(pos + 2, end - 2);
34662 const match = commentText.match(parseSpanComment);
34663 if (match === null) {
34664 return null;
34665 }
34666 return new AbsoluteSourceSpan$1(+match[1], +match[2]);
34667 }) || null;
34668 }
34669 /** Used to identify what type the comment is. */
34670 var CommentTriviaType;
34671 (function (CommentTriviaType) {
34672 CommentTriviaType["DIAGNOSTIC"] = "D";
34673 CommentTriviaType["EXPRESSION_TYPE_IDENTIFIER"] = "T";
34674 })(CommentTriviaType || (CommentTriviaType = {}));
34675 /** Identifies what the TCB expression is for (for example, a directive declaration). */
34676 var ExpressionIdentifier;
34677 (function (ExpressionIdentifier) {
34678 ExpressionIdentifier["DIRECTIVE"] = "DIR";
34679 ExpressionIdentifier["COMPONENT_COMPLETION"] = "COMPCOMP";
34680 ExpressionIdentifier["EVENT_PARAMETER"] = "EP";
34681 })(ExpressionIdentifier || (ExpressionIdentifier = {}));
34682 /** Tags the node with the given expression identifier. */
34683 function addExpressionIdentifier(node, identifier) {
34684 ts__default["default"].addSyntheticTrailingComment(node, ts__default["default"].SyntaxKind.MultiLineCommentTrivia, `${CommentTriviaType.EXPRESSION_TYPE_IDENTIFIER}:${identifier}`,
34685 /* hasTrailingNewLine */ false);
34686 }
34687 const IGNORE_FOR_DIAGNOSTICS_MARKER = `${CommentTriviaType.DIAGNOSTIC}:ignore`;
34688 /**
34689 * Tag the `ts.Node` with an indication that any errors arising from the evaluation of the node
34690 * should be ignored.
34691 */
34692 function markIgnoreDiagnostics(node) {
34693 ts__default["default"].addSyntheticTrailingComment(node, ts__default["default"].SyntaxKind.MultiLineCommentTrivia, IGNORE_FOR_DIAGNOSTICS_MARKER,
34694 /* hasTrailingNewLine */ false);
34695 }
34696 /** Returns true if the node has a marker that indicates diagnostics errors should be ignored. */
34697 function hasIgnoreForDiagnosticsMarker(node, sourceFile) {
34698 return ts__default["default"].forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => {
34699 if (kind !== ts__default["default"].SyntaxKind.MultiLineCommentTrivia) {
34700 return null;
34701 }
34702 const commentText = sourceFile.text.substring(pos + 2, end - 2);
34703 return commentText === IGNORE_FOR_DIAGNOSTICS_MARKER;
34704 }) === true;
34705 }
34706 function makeRecursiveVisitor(visitor) {
34707 function recursiveVisitor(node) {
34708 const res = visitor(node);
34709 return res !== null ? res : node.forEachChild(recursiveVisitor);
34710 }
34711 return recursiveVisitor;
34712 }
34713 function getSpanFromOptions(opts) {
34714 let withSpan = null;
34715 if (opts.withSpan !== undefined) {
34716 if (opts.withSpan instanceof AbsoluteSourceSpan$1) {
34717 withSpan = opts.withSpan;
34718 }
34719 else {
34720 withSpan = { start: opts.withSpan.start.offset, end: opts.withSpan.end.offset };
34721 }
34722 }
34723 return withSpan;
34724 }
34725 /**
34726 * Given a `ts.Node` with finds the first node whose matching the criteria specified
34727 * by the `FindOptions`.
34728 *
34729 * Returns `null` when no `ts.Node` matches the given conditions.
34730 */
34731 function findFirstMatchingNode(tcb, opts) {
34732 const withSpan = getSpanFromOptions(opts);
34733 const withExpressionIdentifier = opts.withExpressionIdentifier;
34734 const sf = tcb.getSourceFile();
34735 const visitor = makeRecursiveVisitor(node => {
34736 if (!opts.filter(node)) {
34737 return null;
34738 }
34739 if (withSpan !== null) {
34740 const comment = readSpanComment(node, sf);
34741 if (comment === null || withSpan.start !== comment.start || withSpan.end !== comment.end) {
34742 return null;
34743 }
34744 }
34745 if (withExpressionIdentifier !== undefined &&
34746 !hasExpressionIdentifier(sf, node, withExpressionIdentifier)) {
34747 return null;
34748 }
34749 return node;
34750 });
34751 return tcb.forEachChild(visitor) ?? null;
34752 }
34753 /**
34754 * Given a `ts.Node` with source span comments, finds the first node whose source span comment
34755 * matches the given `sourceSpan`. Additionally, the `filter` function allows matching only
34756 * `ts.Nodes` of a given type, which provides the ability to select only matches of a given type
34757 * when there may be more than one.
34758 *
34759 * Returns `null` when no `ts.Node` matches the given conditions.
34760 */
34761 function findAllMatchingNodes(tcb, opts) {
34762 const withSpan = getSpanFromOptions(opts);
34763 const withExpressionIdentifier = opts.withExpressionIdentifier;
34764 const results = [];
34765 const stack = [tcb];
34766 const sf = tcb.getSourceFile();
34767 while (stack.length > 0) {
34768 const node = stack.pop();
34769 if (!opts.filter(node)) {
34770 stack.push(...node.getChildren());
34771 continue;
34772 }
34773 if (withSpan !== null) {
34774 const comment = readSpanComment(node, sf);
34775 if (comment === null || withSpan.start !== comment.start || withSpan.end !== comment.end) {
34776 stack.push(...node.getChildren());
34777 continue;
34778 }
34779 }
34780 if (withExpressionIdentifier !== undefined &&
34781 !hasExpressionIdentifier(sf, node, withExpressionIdentifier)) {
34782 continue;
34783 }
34784 results.push(node);
34785 }
34786 return results;
34787 }
34788 function hasExpressionIdentifier(sourceFile, node, identifier) {
34789 return ts__default["default"].forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => {
34790 if (kind !== ts__default["default"].SyntaxKind.MultiLineCommentTrivia) {
34791 return false;
34792 }
34793 const commentText = sourceFile.text.substring(pos + 2, end - 2);
34794 return commentText === `${CommentTriviaType.EXPRESSION_TYPE_IDENTIFIER}:${identifier}`;
34795 }) || false;
34796 }
34797
34798 /**
34799 * @license
34800 * Copyright Google LLC All Rights Reserved.
34801 *
34802 * Use of this source code is governed by an MIT-style license that can be
34803 * found in the LICENSE file at https://angular.io/license
34804 */
34805 /**
34806 * Powers autocompletion for a specific component.
34807 *
34808 * Internally caches autocompletion results, and must be discarded if the component template or
34809 * surrounding TS program have changed.
34810 */
34811 class CompletionEngine {
34812 constructor(tcb, data, shimPath) {
34813 this.tcb = tcb;
34814 this.data = data;
34815 this.shimPath = shimPath;
34816 /**
34817 * Cache of completions for various levels of the template, including the root template (`null`).
34818 * Memoizes `getTemplateContextCompletions`.
34819 */
34820 this.templateContextCache = new Map();
34821 this.expressionCompletionCache = new Map();
34822 // Find the component completion expression within the TCB. This looks like: `ctx. /* ... */;`
34823 const globalRead = findFirstMatchingNode(this.tcb, {
34824 filter: ts__default["default"].isPropertyAccessExpression,
34825 withExpressionIdentifier: ExpressionIdentifier.COMPONENT_COMPLETION
34826 });
34827 if (globalRead !== null) {
34828 this.componentContext = {
34829 shimPath: this.shimPath,
34830 // `globalRead.name` is an empty `ts.Identifier`, so its start position immediately follows
34831 // the `.` in `ctx.`. TS autocompletion APIs can then be used to access completion results
34832 // for the component context.
34833 positionInShimFile: globalRead.name.getStart(),
34834 };
34835 }
34836 else {
34837 this.componentContext = null;
34838 }
34839 }
34840 /**
34841 * Get global completions within the given template context and AST node.
34842 *
34843 * @param context the given template context - either a `TmplAstTemplate` embedded view, or `null`
34844 * for the root
34845 * template context.
34846 * @param node the given AST node
34847 */
34848 getGlobalCompletions(context, node) {
34849 if (this.componentContext === null) {
34850 return null;
34851 }
34852 const templateContext = this.getTemplateContextCompletions(context);
34853 if (templateContext === null) {
34854 return null;
34855 }
34856 let nodeContext = null;
34857 if (node instanceof EmptyExpr) {
34858 const nodeLocation = findFirstMatchingNode(this.tcb, {
34859 filter: ts__default["default"].isIdentifier,
34860 withSpan: node.sourceSpan,
34861 });
34862 if (nodeLocation !== null) {
34863 nodeContext = {
34864 shimPath: this.shimPath,
34865 positionInShimFile: nodeLocation.getStart(),
34866 };
34867 }
34868 }
34869 if (node instanceof PropertyRead && node.receiver instanceof ImplicitReceiver) {
34870 const nodeLocation = findFirstMatchingNode(this.tcb, {
34871 filter: ts__default["default"].isPropertyAccessExpression,
34872 withSpan: node.sourceSpan,
34873 });
34874 if (nodeLocation) {
34875 nodeContext = {
34876 shimPath: this.shimPath,
34877 positionInShimFile: nodeLocation.getStart(),
34878 };
34879 }
34880 }
34881 return {
34882 componentContext: this.componentContext,
34883 templateContext,
34884 nodeContext,
34885 };
34886 }
34887 getExpressionCompletionLocation(expr) {
34888 if (this.expressionCompletionCache.has(expr)) {
34889 return this.expressionCompletionCache.get(expr);
34890 }
34891 // Completion works inside property reads and method calls.
34892 let tsExpr = null;
34893 if (expr instanceof PropertyRead || expr instanceof PropertyWrite) {
34894 // Non-safe navigation operations are trivial: `foo.bar` or `foo.bar()`
34895 tsExpr = findFirstMatchingNode(this.tcb, {
34896 filter: ts__default["default"].isPropertyAccessExpression,
34897 withSpan: expr.nameSpan,
34898 });
34899 }
34900 else if (expr instanceof SafePropertyRead) {
34901 // Safe navigation operations are a little more complex, and involve a ternary. Completion
34902 // happens in the "true" case of the ternary.
34903 const ternaryExpr = findFirstMatchingNode(this.tcb, {
34904 filter: ts__default["default"].isParenthesizedExpression,
34905 withSpan: expr.sourceSpan,
34906 });
34907 if (ternaryExpr === null || !ts__default["default"].isConditionalExpression(ternaryExpr.expression)) {
34908 return null;
34909 }
34910 const whenTrue = ternaryExpr.expression.whenTrue;
34911 if (ts__default["default"].isPropertyAccessExpression(whenTrue)) {
34912 tsExpr = whenTrue;
34913 }
34914 else if (ts__default["default"].isCallExpression(whenTrue) && ts__default["default"].isPropertyAccessExpression(whenTrue.expression)) {
34915 tsExpr = whenTrue.expression;
34916 }
34917 }
34918 if (tsExpr === null) {
34919 return null;
34920 }
34921 const res = {
34922 shimPath: this.shimPath,
34923 positionInShimFile: tsExpr.name.getEnd(),
34924 };
34925 this.expressionCompletionCache.set(expr, res);
34926 return res;
34927 }
34928 getLiteralCompletionLocation(expr) {
34929 if (this.expressionCompletionCache.has(expr)) {
34930 return this.expressionCompletionCache.get(expr);
34931 }
34932 let tsExpr = null;
34933 if (expr instanceof TextAttribute) {
34934 const strNode = findFirstMatchingNode(this.tcb, {
34935 filter: ts__default["default"].isParenthesizedExpression,
34936 withSpan: expr.sourceSpan,
34937 });
34938 if (strNode !== null && ts__default["default"].isStringLiteral(strNode.expression)) {
34939 tsExpr = strNode.expression;
34940 }
34941 }
34942 else {
34943 tsExpr = findFirstMatchingNode(this.tcb, {
34944 filter: (n) => ts__default["default"].isStringLiteral(n) || ts__default["default"].isNumericLiteral(n),
34945 withSpan: expr.sourceSpan,
34946 });
34947 }
34948 if (tsExpr === null) {
34949 return null;
34950 }
34951 let positionInShimFile = tsExpr.getEnd();
34952 if (ts__default["default"].isStringLiteral(tsExpr)) {
34953 // In the shimFile, if `tsExpr` is a string, the position should be in the quotes.
34954 positionInShimFile -= 1;
34955 }
34956 const res = {
34957 shimPath: this.shimPath,
34958 positionInShimFile,
34959 };
34960 this.expressionCompletionCache.set(expr, res);
34961 return res;
34962 }
34963 /**
34964 * Get global completions within the given template context - either a `TmplAstTemplate` embedded
34965 * view, or `null` for the root context.
34966 */
34967 getTemplateContextCompletions(context) {
34968 if (this.templateContextCache.has(context)) {
34969 return this.templateContextCache.get(context);
34970 }
34971 const templateContext = new Map();
34972 // The bound template already has details about the references and variables in scope in the
34973 // `context` template - they just need to be converted to `Completion`s.
34974 for (const node of this.data.boundTarget.getEntitiesInTemplateScope(context)) {
34975 if (node instanceof Reference$1) {
34976 templateContext.set(node.name, {
34977 kind: CompletionKind.Reference,
34978 node,
34979 });
34980 }
34981 else {
34982 templateContext.set(node.name, {
34983 kind: CompletionKind.Variable,
34984 node,
34985 });
34986 }
34987 }
34988 this.templateContextCache.set(context, templateContext);
34989 return templateContext;
34990 }
34991 }
34992
34993 /**
34994 * @license
34995 * Copyright Google LLC All Rights Reserved.
34996 *
34997 * Use of this source code is governed by an MIT-style license that can be
34998 * found in the LICENSE file at https://angular.io/license
34999 */
35000 const REGISTRY$1 = new DomElementSchemaRegistry();
35001 const REMOVE_XHTML_REGEX = /^:xhtml:/;
35002 /**
35003 * Checks non-Angular elements and properties against the `DomElementSchemaRegistry`, a schema
35004 * maintained by the Angular team via extraction from a browser IDL.
35005 */
35006 class RegistryDomSchemaChecker {
35007 constructor(resolver) {
35008 this.resolver = resolver;
35009 this._diagnostics = [];
35010 }
35011 get diagnostics() {
35012 return this._diagnostics;
35013 }
35014 checkElement(id, element, schemas) {
35015 // HTML elements inside an SVG `foreignObject` are declared in the `xhtml` namespace.
35016 // We need to strip it before handing it over to the registry because all HTML tag names
35017 // in the registry are without a namespace.
35018 const name = element.name.replace(REMOVE_XHTML_REGEX, '');
35019 if (!REGISTRY$1.hasElement(name, schemas)) {
35020 const mapping = this.resolver.getSourceMapping(id);
35021 let errorMsg = `'${name}' is not a known element:\n`;
35022 errorMsg +=
35023 `1. If '${name}' is an Angular component, then verify that it is part of this module.\n`;
35024 if (name.indexOf('-') > -1) {
35025 errorMsg += `2. If '${name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`;
35026 }
35027 else {
35028 errorMsg +=
35029 `2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
35030 }
35031 const diag = makeTemplateDiagnostic(id, mapping, element.startSourceSpan, ts__default["default"].DiagnosticCategory.Error, ngErrorCode(ErrorCode.SCHEMA_INVALID_ELEMENT), errorMsg);
35032 this._diagnostics.push(diag);
35033 }
35034 }
35035 checkProperty(id, element, name, span, schemas) {
35036 if (!REGISTRY$1.hasProperty(element.name, name, schemas)) {
35037 const mapping = this.resolver.getSourceMapping(id);
35038 let errorMsg = `Can't bind to '${name}' since it isn't a known property of '${element.name}'.`;
35039 if (element.name.startsWith('ng-')) {
35040 errorMsg +=
35041 `\n1. If '${name}' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component.` +
35042 `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
35043 }
35044 else if (element.name.indexOf('-') > -1) {
35045 errorMsg +=
35046 `\n1. If '${element.name}' is an Angular component and it has '${name}' input, then verify that it is part of this module.` +
35047 `\n2. If '${element
35048 .name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.` +
35049 `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
35050 }
35051 const diag = makeTemplateDiagnostic(id, mapping, span, ts__default["default"].DiagnosticCategory.Error, ngErrorCode(ErrorCode.SCHEMA_INVALID_ATTRIBUTE), errorMsg);
35052 this._diagnostics.push(diag);
35053 }
35054 }
35055 }
35056
35057 /**
35058 * @license
35059 * Copyright Google LLC All Rights Reserved.
35060 *
35061 * Use of this source code is governed by an MIT-style license that can be
35062 * found in the LICENSE file at https://angular.io/license
35063 */
35064 /**
35065 * A `Set` of `ts.SyntaxKind`s of `ts.Expression` which are safe to wrap in a `ts.AsExpression`
35066 * without needing to be wrapped in parentheses.
35067 *
35068 * For example, `foo.bar()` is a `ts.CallExpression`, and can be safely cast to `any` with
35069 * `foo.bar() as any`. however, `foo !== bar` is a `ts.BinaryExpression`, and attempting to cast
35070 * without the parentheses yields the expression `foo !== bar as any`. This is semantically
35071 * equivalent to `foo !== (bar as any)`, which is not what was intended. Thus,
35072 * `ts.BinaryExpression`s need to be wrapped in parentheses before casting.
35073 */
35074 //
35075 const SAFE_TO_CAST_WITHOUT_PARENS = new Set([
35076 // Expressions which are already parenthesized can be cast without further wrapping.
35077 ts__default["default"].SyntaxKind.ParenthesizedExpression,
35078 // Expressions which form a single lexical unit leave no room for precedence issues with the cast.
35079 ts__default["default"].SyntaxKind.Identifier,
35080 ts__default["default"].SyntaxKind.CallExpression,
35081 ts__default["default"].SyntaxKind.NonNullExpression,
35082 ts__default["default"].SyntaxKind.ElementAccessExpression,
35083 ts__default["default"].SyntaxKind.PropertyAccessExpression,
35084 ts__default["default"].SyntaxKind.ArrayLiteralExpression,
35085 ts__default["default"].SyntaxKind.ObjectLiteralExpression,
35086 // The same goes for various literals.
35087 ts__default["default"].SyntaxKind.StringLiteral,
35088 ts__default["default"].SyntaxKind.NumericLiteral,
35089 ts__default["default"].SyntaxKind.TrueKeyword,
35090 ts__default["default"].SyntaxKind.FalseKeyword,
35091 ts__default["default"].SyntaxKind.NullKeyword,
35092 ts__default["default"].SyntaxKind.UndefinedKeyword,
35093 ]);
35094 function tsCastToAny(expr) {
35095 // Wrap `expr` in parentheses if needed (see `SAFE_TO_CAST_WITHOUT_PARENS` above).
35096 if (!SAFE_TO_CAST_WITHOUT_PARENS.has(expr.kind)) {
35097 expr = ts__default["default"].createParen(expr);
35098 }
35099 // The outer expression is always wrapped in parentheses.
35100 return ts__default["default"].createParen(ts__default["default"].createAsExpression(expr, ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword)));
35101 }
35102 /**
35103 * Create an expression which instantiates an element by its HTML tagName.
35104 *
35105 * Thanks to narrowing of `document.createElement()`, this expression will have its type inferred
35106 * based on the tag name, including for custom elements that have appropriate .d.ts definitions.
35107 */
35108 function tsCreateElement(tagName) {
35109 const createElement = ts__default["default"].createPropertyAccess(
35110 /* expression */ ts__default["default"].createIdentifier('document'), 'createElement');
35111 return ts__default["default"].createCall(
35112 /* expression */ createElement,
35113 /* typeArguments */ undefined,
35114 /* argumentsArray */ [ts__default["default"].createLiteral(tagName)]);
35115 }
35116 /**
35117 * Create a `ts.VariableStatement` which declares a variable without explicit initialization.
35118 *
35119 * The initializer `null!` is used to bypass strict variable initialization checks.
35120 *
35121 * Unlike with `tsCreateVariable`, the type of the variable is explicitly specified.
35122 */
35123 function tsDeclareVariable(id, type) {
35124 const decl = ts__default["default"].createVariableDeclaration(
35125 /* name */ id,
35126 /* type */ type,
35127 /* initializer */ ts__default["default"].createNonNullExpression(ts__default["default"].createNull()));
35128 return ts__default["default"].createVariableStatement(
35129 /* modifiers */ undefined,
35130 /* declarationList */ [decl]);
35131 }
35132 /**
35133 * Creates a `ts.TypeQueryNode` for a coerced input.
35134 *
35135 * For example: `typeof MatInput.ngAcceptInputType_value`, where MatInput is `typeName` and `value`
35136 * is the `coercedInputName`.
35137 *
35138 * @param typeName The `EntityName` of the Directive where the static coerced input is defined.
35139 * @param coercedInputName The field name of the coerced input.
35140 */
35141 function tsCreateTypeQueryForCoercedInput(typeName, coercedInputName) {
35142 return ts__default["default"].createTypeQueryNode(ts__default["default"].createQualifiedName(typeName, `ngAcceptInputType_${coercedInputName}`));
35143 }
35144 /**
35145 * Create a `ts.VariableStatement` that initializes a variable with a given expression.
35146 *
35147 * Unlike with `tsDeclareVariable`, the type of the variable is inferred from the initializer
35148 * expression.
35149 */
35150 function tsCreateVariable(id, initializer) {
35151 const decl = ts__default["default"].createVariableDeclaration(
35152 /* name */ id,
35153 /* type */ undefined,
35154 /* initializer */ initializer);
35155 return ts__default["default"].createVariableStatement(
35156 /* modifiers */ undefined,
35157 /* declarationList */ [decl]);
35158 }
35159 /**
35160 * Construct a `ts.CallExpression` that calls a method on a receiver.
35161 */
35162 function tsCallMethod(receiver, methodName, args = []) {
35163 const methodAccess = ts__default["default"].createPropertyAccess(receiver, methodName);
35164 return ts__default["default"].createCall(
35165 /* expression */ methodAccess,
35166 /* typeArguments */ undefined,
35167 /* argumentsArray */ args);
35168 }
35169 function checkIfClassIsExported(node) {
35170 // A class is exported if one of two conditions is met:
35171 // 1) it has the 'export' modifier.
35172 // 2) it's declared at the top level, and there is an export statement for the class.
35173 if (node.modifiers !== undefined &&
35174 node.modifiers.some(mod => mod.kind === ts__default["default"].SyntaxKind.ExportKeyword)) {
35175 // Condition 1 is true, the class has an 'export' keyword attached.
35176 return true;
35177 }
35178 else if (node.parent !== undefined && ts__default["default"].isSourceFile(node.parent) &&
35179 checkIfFileHasExport(node.parent, node.name.text)) {
35180 // Condition 2 is true, the class is exported via an 'export {}' statement.
35181 return true;
35182 }
35183 return false;
35184 }
35185 function checkIfFileHasExport(sf, name) {
35186 for (const stmt of sf.statements) {
35187 if (ts__default["default"].isExportDeclaration(stmt) && stmt.exportClause !== undefined &&
35188 ts__default["default"].isNamedExports(stmt.exportClause)) {
35189 for (const element of stmt.exportClause.elements) {
35190 if (element.propertyName === undefined && element.name.text === name) {
35191 // The named declaration is directly exported.
35192 return true;
35193 }
35194 else if (element.propertyName !== undefined && element.propertyName.text == name) {
35195 // The named declaration is exported via an alias.
35196 return true;
35197 }
35198 }
35199 }
35200 }
35201 return false;
35202 }
35203 function isAccessExpression(node) {
35204 return ts__default["default"].isPropertyAccessExpression(node) || ts__default["default"].isElementAccessExpression(node);
35205 }
35206
35207 /**
35208 * @license
35209 * Copyright Google LLC All Rights Reserved.
35210 *
35211 * Use of this source code is governed by an MIT-style license that can be
35212 * found in the LICENSE file at https://angular.io/license
35213 */
35214 const INELIGIBLE = {};
35215 /**
35216 * Determines whether the provided type can be emitted, which means that it can be safely emitted
35217 * into a different location.
35218 *
35219 * If this function returns true, a `TypeEmitter` should be able to succeed. Vice versa, if this
35220 * function returns false, then using the `TypeEmitter` should not be attempted as it is known to
35221 * fail.
35222 */
35223 function canEmitType(type, resolver) {
35224 return canEmitTypeWorker(type);
35225 function canEmitTypeWorker(type) {
35226 return visitNode(type) !== INELIGIBLE;
35227 }
35228 // To determine whether a type can be emitted, we have to recursively look through all type nodes.
35229 // If an unsupported type node is found at any position within the type, then the `INELIGIBLE`
35230 // constant is returned to stop the recursive walk as the type as a whole cannot be emitted in
35231 // that case. Otherwise, the result of visiting all child nodes determines the result. If no
35232 // ineligible type reference node is found then the walk returns `undefined`, indicating that
35233 // no type node was visited that could not be emitted.
35234 function visitNode(node) {
35235 // `import('module')` type nodes are not supported, as it may require rewriting the module
35236 // specifier which is currently not done.
35237 if (ts__default["default"].isImportTypeNode(node)) {
35238 return INELIGIBLE;
35239 }
35240 // Emitting a type reference node in a different context requires that an import for the type
35241 // can be created. If a type reference node cannot be emitted, `INELIGIBLE` is returned to stop
35242 // the walk.
35243 if (ts__default["default"].isTypeReferenceNode(node) && !canEmitTypeReference(node)) {
35244 return INELIGIBLE;
35245 }
35246 else {
35247 return ts__default["default"].forEachChild(node, visitNode);
35248 }
35249 }
35250 function canEmitTypeReference(type) {
35251 const reference = resolver(type);
35252 // If the type could not be resolved, it can not be emitted.
35253 if (reference === null) {
35254 return false;
35255 }
35256 // If the type is a reference, consider the type to be eligible for emitting.
35257 if (reference instanceof Reference) {
35258 return true;
35259 }
35260 // The type can be emitted if either it does not have any type arguments, or all of them can be
35261 // emitted.
35262 return type.typeArguments === undefined || type.typeArguments.every(canEmitTypeWorker);
35263 }
35264 }
35265 /**
35266 * Given a `ts.TypeNode`, this class derives an equivalent `ts.TypeNode` that has been emitted into
35267 * a different context.
35268 *
35269 * For example, consider the following code:
35270 *
35271 * ```
35272 * import {NgIterable} from '@angular/core';
35273 *
35274 * class NgForOf<T, U extends NgIterable<T>> {}
35275 * ```
35276 *
35277 * Here, the generic type parameters `T` and `U` can be emitted into a different context, as the
35278 * type reference to `NgIterable` originates from an absolute module import so that it can be
35279 * emitted anywhere, using that same module import. The process of emitting translates the
35280 * `NgIterable` type reference to a type reference that is valid in the context in which it is
35281 * emitted, for example:
35282 *
35283 * ```
35284 * import * as i0 from '@angular/core';
35285 * import * as i1 from '@angular/common';
35286 *
35287 * const _ctor1: <T, U extends i0.NgIterable<T>>(o: Pick<i1.NgForOf<T, U>, 'ngForOf'>):
35288 * i1.NgForOf<T, U>;
35289 * ```
35290 *
35291 * Notice how the type reference for `NgIterable` has been translated into a qualified name,
35292 * referring to the namespace import that was created.
35293 */
35294 class TypeEmitter {
35295 constructor(resolver, emitReference) {
35296 this.resolver = resolver;
35297 this.emitReference = emitReference;
35298 }
35299 emitType(type) {
35300 const typeReferenceTransformer = context => {
35301 const visitNode = (node) => {
35302 if (ts__default["default"].isImportTypeNode(node)) {
35303 throw new Error('Unable to emit import type');
35304 }
35305 if (ts__default["default"].isTypeReferenceNode(node)) {
35306 return this.emitTypeReference(node);
35307 }
35308 else if (ts__default["default"].isLiteralExpression(node)) {
35309 // TypeScript would typically take the emit text for a literal expression from the source
35310 // file itself. As the type node is being emitted into a different file, however,
35311 // TypeScript would extract the literal text from the wrong source file. To mitigate this
35312 // issue the literal is cloned and explicitly marked as synthesized by setting its text
35313 // range to a negative range, forcing TypeScript to determine the node's literal text from
35314 // the synthesized node's text instead of the incorrect source file.
35315 const clone = ts__default["default"].getMutableClone(node);
35316 ts__default["default"].setTextRange(clone, { pos: -1, end: -1 });
35317 return clone;
35318 }
35319 else {
35320 return ts__default["default"].visitEachChild(node, visitNode, context);
35321 }
35322 };
35323 return node => ts__default["default"].visitNode(node, visitNode);
35324 };
35325 return ts__default["default"].transform(type, [typeReferenceTransformer]).transformed[0];
35326 }
35327 emitTypeReference(type) {
35328 // Determine the reference that the type corresponds with.
35329 const reference = this.resolver(type);
35330 if (reference === null) {
35331 throw new Error('Unable to emit an unresolved reference');
35332 }
35333 // Emit the type arguments, if any.
35334 let typeArguments = undefined;
35335 if (type.typeArguments !== undefined) {
35336 typeArguments = ts__default["default"].createNodeArray(type.typeArguments.map(typeArg => this.emitType(typeArg)));
35337 }
35338 // Emit the type name.
35339 let typeName = type.typeName;
35340 if (reference instanceof Reference) {
35341 const emittedType = this.emitReference(reference);
35342 if (!ts__default["default"].isTypeReferenceNode(emittedType)) {
35343 throw new Error(`Expected TypeReferenceNode for emitted reference, got ${ts__default["default"].SyntaxKind[emittedType.kind]}`);
35344 }
35345 typeName = emittedType.typeName;
35346 }
35347 return ts__default["default"].updateTypeReferenceNode(type, typeName, typeArguments);
35348 }
35349 }
35350
35351 /**
35352 * @license
35353 * Copyright Google LLC All Rights Reserved.
35354 *
35355 * Use of this source code is governed by an MIT-style license that can be
35356 * found in the LICENSE file at https://angular.io/license
35357 */
35358 /**
35359 * See `TypeEmitter` for more information on the emitting process.
35360 */
35361 class TypeParameterEmitter {
35362 constructor(typeParameters, reflector) {
35363 this.typeParameters = typeParameters;
35364 this.reflector = reflector;
35365 }
35366 /**
35367 * Determines whether the type parameters can be emitted. If this returns true, then a call to
35368 * `emit` is known to succeed. Vice versa, if false is returned then `emit` should not be
35369 * called, as it would fail.
35370 */
35371 canEmit() {
35372 if (this.typeParameters === undefined) {
35373 return true;
35374 }
35375 return this.typeParameters.every(typeParam => {
35376 return this.canEmitType(typeParam.constraint) && this.canEmitType(typeParam.default);
35377 });
35378 }
35379 canEmitType(type) {
35380 if (type === undefined) {
35381 return true;
35382 }
35383 return canEmitType(type, typeReference => this.resolveTypeReference(typeReference));
35384 }
35385 /**
35386 * Emits the type parameters using the provided emitter function for `Reference`s.
35387 */
35388 emit(emitReference) {
35389 if (this.typeParameters === undefined) {
35390 return undefined;
35391 }
35392 const emitter = new TypeEmitter(type => this.resolveTypeReference(type), emitReference);
35393 return this.typeParameters.map(typeParam => {
35394 const constraint = typeParam.constraint !== undefined ? emitter.emitType(typeParam.constraint) : undefined;
35395 const defaultType = typeParam.default !== undefined ? emitter.emitType(typeParam.default) : undefined;
35396 return ts__default["default"].updateTypeParameterDeclaration(
35397 /* node */ typeParam,
35398 /* name */ typeParam.name,
35399 /* constraint */ constraint,
35400 /* defaultType */ defaultType);
35401 });
35402 }
35403 resolveTypeReference(type) {
35404 const target = ts__default["default"].isIdentifier(type.typeName) ? type.typeName : type.typeName.right;
35405 const declaration = this.reflector.getDeclarationOfIdentifier(target);
35406 // If no declaration could be resolved or does not have a `ts.Declaration`, the type cannot be
35407 // resolved.
35408 if (declaration === null || declaration.node === null) {
35409 return null;
35410 }
35411 // If the declaration corresponds with a local type parameter, the type reference can be used
35412 // as is.
35413 if (this.isLocalTypeParameter(declaration.node)) {
35414 return type;
35415 }
35416 let owningModule = null;
35417 if (declaration.viaModule !== null) {
35418 owningModule = {
35419 specifier: declaration.viaModule,
35420 resolutionContext: type.getSourceFile().fileName,
35421 };
35422 }
35423 // The declaration needs to be exported as a top-level export to be able to emit an import
35424 // statement for it. If the declaration is not exported, null is returned to prevent emit.
35425 if (!this.isTopLevelExport(declaration.node)) {
35426 return null;
35427 }
35428 return new Reference(declaration.node, owningModule);
35429 }
35430 isTopLevelExport(decl) {
35431 if (decl.parent === undefined || !ts__default["default"].isSourceFile(decl.parent)) {
35432 // The declaration has to exist at the top-level, as the reference emitters are not capable of
35433 // generating imports to classes declared in a namespace.
35434 return false;
35435 }
35436 return this.reflector.isStaticallyExported(decl);
35437 }
35438 isLocalTypeParameter(decl) {
35439 // Checking for local type parameters only occurs during resolution of type parameters, so it is
35440 // guaranteed that type parameters are present.
35441 return this.typeParameters.some(param => param === decl);
35442 }
35443 }
35444
35445 /**
35446 * @license
35447 * Copyright Google LLC All Rights Reserved.
35448 *
35449 * Use of this source code is governed by an MIT-style license that can be
35450 * found in the LICENSE file at https://angular.io/license
35451 */
35452 /**
35453 * Indicates whether a particular component requires an inline type check block.
35454 *
35455 * This is not a boolean state as inlining might only be required to get the best possible
35456 * type-checking, but the component could theoretically still be checked without it.
35457 */
35458 var TcbInliningRequirement;
35459 (function (TcbInliningRequirement) {
35460 /**
35461 * There is no way to type check this component without inlining.
35462 */
35463 TcbInliningRequirement[TcbInliningRequirement["MustInline"] = 0] = "MustInline";
35464 /**
35465 * Inlining should be used due to the component's generic bounds, but a non-inlining fallback
35466 * method can be used if that's not possible.
35467 */
35468 TcbInliningRequirement[TcbInliningRequirement["ShouldInlineForGenericBounds"] = 1] = "ShouldInlineForGenericBounds";
35469 /**
35470 * There is no requirement for this component's TCB to be inlined.
35471 */
35472 TcbInliningRequirement[TcbInliningRequirement["None"] = 2] = "None";
35473 })(TcbInliningRequirement || (TcbInliningRequirement = {}));
35474 function requiresInlineTypeCheckBlock(node, usedPipes, reflector) {
35475 // In order to qualify for a declared TCB (not inline) two conditions must be met:
35476 // 1) the class must be exported
35477 // 2) it must not have contextual generic type bounds
35478 if (!checkIfClassIsExported(node)) {
35479 // Condition 1 is false, the class is not exported.
35480 return TcbInliningRequirement.MustInline;
35481 }
35482 else if (!checkIfGenericTypeBoundsAreContextFree(node, reflector)) {
35483 // Condition 2 is false, the class has constrained generic types. It should be checked with an
35484 // inline TCB if possible, but can potentially use fallbacks to avoid inlining if not.
35485 return TcbInliningRequirement.ShouldInlineForGenericBounds;
35486 }
35487 else if (Array.from(usedPipes.values())
35488 .some(pipeRef => !checkIfClassIsExported(pipeRef.node))) {
35489 // If one of the pipes used by the component is not exported, a non-inline TCB will not be able
35490 // to import it, so this requires an inline TCB.
35491 return TcbInliningRequirement.MustInline;
35492 }
35493 else {
35494 return TcbInliningRequirement.None;
35495 }
35496 }
35497 /** Maps a shim position back to a template location. */
35498 function getTemplateMapping(shimSf, position, resolver, isDiagnosticRequest) {
35499 const node = getTokenAtPosition(shimSf, position);
35500 const sourceLocation = findSourceLocation(node, shimSf, isDiagnosticRequest);
35501 if (sourceLocation === null) {
35502 return null;
35503 }
35504 const mapping = resolver.getSourceMapping(sourceLocation.id);
35505 const span = resolver.toParseSourceSpan(sourceLocation.id, sourceLocation.span);
35506 if (span === null) {
35507 return null;
35508 }
35509 // TODO(atscott): Consider adding a context span by walking up from `node` until we get a
35510 // different span.
35511 return { sourceLocation, templateSourceMapping: mapping, span };
35512 }
35513 function findTypeCheckBlock(file, id, isDiagnosticRequest) {
35514 for (const stmt of file.statements) {
35515 if (ts__default["default"].isFunctionDeclaration(stmt) && getTemplateId(stmt, file, isDiagnosticRequest) === id) {
35516 return stmt;
35517 }
35518 }
35519 return null;
35520 }
35521 /**
35522 * Traverses up the AST starting from the given node to extract the source location from comments
35523 * that have been emitted into the TCB. If the node does not exist within a TCB, or if an ignore
35524 * marker comment is found up the tree (and this is part of a diagnostic request), this function
35525 * returns null.
35526 */
35527 function findSourceLocation(node, sourceFile, isDiagnosticsRequest) {
35528 // Search for comments until the TCB's function declaration is encountered.
35529 while (node !== undefined && !ts__default["default"].isFunctionDeclaration(node)) {
35530 if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticsRequest) {
35531 // There's an ignore marker on this node, so the diagnostic should not be reported.
35532 return null;
35533 }
35534 const span = readSpanComment(node, sourceFile);
35535 if (span !== null) {
35536 // Once the positional information has been extracted, search further up the TCB to extract
35537 // the unique id that is attached with the TCB's function declaration.
35538 const id = getTemplateId(node, sourceFile, isDiagnosticsRequest);
35539 if (id === null) {
35540 return null;
35541 }
35542 return { id, span };
35543 }
35544 node = node.parent;
35545 }
35546 return null;
35547 }
35548 function getTemplateId(node, sourceFile, isDiagnosticRequest) {
35549 // Walk up to the function declaration of the TCB, the file information is attached there.
35550 while (!ts__default["default"].isFunctionDeclaration(node)) {
35551 if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticRequest) {
35552 // There's an ignore marker on this node, so the diagnostic should not be reported.
35553 return null;
35554 }
35555 node = node.parent;
35556 // Bail once we have reached the root.
35557 if (node === undefined) {
35558 return null;
35559 }
35560 }
35561 const start = node.getFullStart();
35562 return ts__default["default"].forEachLeadingCommentRange(sourceFile.text, start, (pos, end, kind) => {
35563 if (kind !== ts__default["default"].SyntaxKind.MultiLineCommentTrivia) {
35564 return null;
35565 }
35566 const commentText = sourceFile.text.substring(pos + 2, end - 2);
35567 return commentText;
35568 }) || null;
35569 }
35570 function checkIfGenericTypeBoundsAreContextFree(node, reflector) {
35571 // Generic type parameters are considered context free if they can be emitted into any context.
35572 return new TypeParameterEmitter(node.typeParameters, reflector).canEmit();
35573 }
35574
35575 /**
35576 * @license
35577 * Copyright Google LLC All Rights Reserved.
35578 *
35579 * Use of this source code is governed by an MIT-style license that can be
35580 * found in the LICENSE file at https://angular.io/license
35581 */
35582 function generateTypeCtorDeclarationFn(node, meta, nodeTypeRef, typeParams, reflector) {
35583 if (requiresInlineTypeCtor(node, reflector)) {
35584 throw new Error(`${node.name.text} requires an inline type constructor`);
35585 }
35586 const rawTypeArgs = typeParams !== undefined ? generateGenericArgs(typeParams) : undefined;
35587 const rawType = ts__default["default"].createTypeReferenceNode(nodeTypeRef, rawTypeArgs);
35588 const initParam = constructTypeCtorParameter(node, meta, rawType);
35589 const typeParameters = typeParametersWithDefaultTypes(typeParams);
35590 if (meta.body) {
35591 const fnType = ts__default["default"].createFunctionTypeNode(
35592 /* typeParameters */ typeParameters,
35593 /* parameters */ [initParam],
35594 /* type */ rawType);
35595 const decl = ts__default["default"].createVariableDeclaration(
35596 /* name */ meta.fnName,
35597 /* type */ fnType,
35598 /* body */ ts__default["default"].createNonNullExpression(ts__default["default"].createNull()));
35599 const declList = ts__default["default"].createVariableDeclarationList([decl], ts__default["default"].NodeFlags.Const);
35600 return ts__default["default"].createVariableStatement(
35601 /* modifiers */ undefined,
35602 /* declarationList */ declList);
35603 }
35604 else {
35605 return ts__default["default"].createFunctionDeclaration(
35606 /* decorators */ undefined,
35607 /* modifiers */ [ts__default["default"].createModifier(ts__default["default"].SyntaxKind.DeclareKeyword)],
35608 /* asteriskToken */ undefined,
35609 /* name */ meta.fnName,
35610 /* typeParameters */ typeParameters,
35611 /* parameters */ [initParam],
35612 /* type */ rawType,
35613 /* body */ undefined);
35614 }
35615 }
35616 /**
35617 * Generate an inline type constructor for the given class and metadata.
35618 *
35619 * An inline type constructor is a specially shaped TypeScript static method, intended to be placed
35620 * within a directive class itself, that permits type inference of any generic type parameters of
35621 * the class from the types of expressions bound to inputs or outputs, and the types of elements
35622 * that match queries performed by the directive. It also catches any errors in the types of these
35623 * expressions. This method is never called at runtime, but is used in type-check blocks to
35624 * construct directive types.
35625 *
35626 * An inline type constructor for NgFor looks like:
35627 *
35628 * static ngTypeCtor<T>(init: Pick<NgForOf<T>, 'ngForOf'|'ngForTrackBy'|'ngForTemplate'>):
35629 * NgForOf<T>;
35630 *
35631 * A typical constructor would be:
35632 *
35633 * NgForOf.ngTypeCtor(init: {
35634 * ngForOf: ['foo', 'bar'],
35635 * ngForTrackBy: null as any,
35636 * ngForTemplate: null as any,
35637 * }); // Infers a type of NgForOf<string>.
35638 *
35639 * Any inputs declared on the type for which no property binding is present are assigned a value of
35640 * type `any`, to avoid producing any type errors for unset inputs.
35641 *
35642 * Inline type constructors are used when the type being created has bounded generic types which
35643 * make writing a declared type constructor (via `generateTypeCtorDeclarationFn`) difficult or
35644 * impossible.
35645 *
35646 * @param node the `ClassDeclaration<ts.ClassDeclaration>` for which a type constructor will be
35647 * generated.
35648 * @param meta additional metadata required to generate the type constructor.
35649 * @returns a `ts.MethodDeclaration` for the type constructor.
35650 */
35651 function generateInlineTypeCtor(node, meta) {
35652 // Build rawType, a `ts.TypeNode` of the class with its generic parameters passed through from
35653 // the definition without any type bounds. For example, if the class is
35654 // `FooDirective<T extends Bar>`, its rawType would be `FooDirective<T>`.
35655 const rawTypeArgs = node.typeParameters !== undefined ? generateGenericArgs(node.typeParameters) : undefined;
35656 const rawType = ts__default["default"].createTypeReferenceNode(node.name, rawTypeArgs);
35657 const initParam = constructTypeCtorParameter(node, meta, rawType);
35658 // If this constructor is being generated into a .ts file, then it needs a fake body. The body
35659 // is set to a return of `null!`. If the type constructor is being generated into a .d.ts file,
35660 // it needs no body.
35661 let body = undefined;
35662 if (meta.body) {
35663 body = ts__default["default"].createBlock([
35664 ts__default["default"].createReturn(ts__default["default"].createNonNullExpression(ts__default["default"].createNull())),
35665 ]);
35666 }
35667 // Create the type constructor method declaration.
35668 return ts__default["default"].createMethod(
35669 /* decorators */ undefined,
35670 /* modifiers */ [ts__default["default"].createModifier(ts__default["default"].SyntaxKind.StaticKeyword)],
35671 /* asteriskToken */ undefined,
35672 /* name */ meta.fnName,
35673 /* questionToken */ undefined,
35674 /* typeParameters */ typeParametersWithDefaultTypes(node.typeParameters),
35675 /* parameters */ [initParam],
35676 /* type */ rawType,
35677 /* body */ body);
35678 }
35679 function constructTypeCtorParameter(node, meta, rawType) {
35680 // initType is the type of 'init', the single argument to the type constructor method.
35681 // If the Directive has any inputs, its initType will be:
35682 //
35683 // Pick<rawType, 'inputA'|'inputB'>
35684 //
35685 // Pick here is used to select only those fields from which the generic type parameters of the
35686 // directive will be inferred.
35687 //
35688 // In the special case there are no inputs, initType is set to {}.
35689 let initType = null;
35690 const keys = meta.fields.inputs;
35691 const plainKeys = [];
35692 const coercedKeys = [];
35693 for (const key of keys) {
35694 if (!meta.coercedInputFields.has(key)) {
35695 plainKeys.push(ts__default["default"].createLiteralTypeNode(ts__default["default"].createStringLiteral(key)));
35696 }
35697 else {
35698 coercedKeys.push(ts__default["default"].createPropertySignature(
35699 /* modifiers */ undefined,
35700 /* name */ key,
35701 /* questionToken */ undefined,
35702 /* type */ tsCreateTypeQueryForCoercedInput(rawType.typeName, key),
35703 /* initializer */ undefined));
35704 }
35705 }
35706 if (plainKeys.length > 0) {
35707 // Construct a union of all the field names.
35708 const keyTypeUnion = ts__default["default"].createUnionTypeNode(plainKeys);
35709 // Construct the Pick<rawType, keyTypeUnion>.
35710 initType = ts__default["default"].createTypeReferenceNode('Pick', [rawType, keyTypeUnion]);
35711 }
35712 if (coercedKeys.length > 0) {
35713 const coercedLiteral = ts__default["default"].createTypeLiteralNode(coercedKeys);
35714 initType = initType !== null ? ts__default["default"].createIntersectionTypeNode([initType, coercedLiteral]) :
35715 coercedLiteral;
35716 }
35717 if (initType === null) {
35718 // Special case - no inputs, outputs, or other fields which could influence the result type.
35719 initType = ts__default["default"].createTypeLiteralNode([]);
35720 }
35721 // Create the 'init' parameter itself.
35722 return ts__default["default"].createParameter(
35723 /* decorators */ undefined,
35724 /* modifiers */ undefined,
35725 /* dotDotDotToken */ undefined,
35726 /* name */ 'init',
35727 /* questionToken */ undefined,
35728 /* type */ initType,
35729 /* initializer */ undefined);
35730 }
35731 function generateGenericArgs(params) {
35732 return params.map(param => ts__default["default"].createTypeReferenceNode(param.name, undefined));
35733 }
35734 function requiresInlineTypeCtor(node, host) {
35735 // The class requires an inline type constructor if it has generic type bounds that can not be
35736 // emitted into a different context.
35737 return !checkIfGenericTypeBoundsAreContextFree(node, host);
35738 }
35739 /**
35740 * Add a default `= any` to type parameters that don't have a default value already.
35741 *
35742 * TypeScript uses the default type of a type parameter whenever inference of that parameter fails.
35743 * This can happen when inferring a complex type from 'any'. For example, if `NgFor`'s inference is
35744 * done with the TCB code:
35745 *
35746 * ```
35747 * class NgFor<T> {
35748 * ngForOf: T[];
35749 * }
35750 *
35751 * declare function ctor<T>(o: Pick<NgFor<T>, 'ngForOf'|'ngForTrackBy'|'ngForTemplate'>): NgFor<T>;
35752 * ```
35753 *
35754 * An invocation looks like:
35755 *
35756 * ```
35757 * var _t1 = ctor({ngForOf: [1, 2], ngForTrackBy: null as any, ngForTemplate: null as any});
35758 * ```
35759 *
35760 * This correctly infers the type `NgFor<number>` for `_t1`, since `T` is inferred from the
35761 * assignment of type `number[]` to `ngForOf`'s type `T[]`. However, if `any` is passed instead:
35762 *
35763 * ```
35764 * var _t2 = ctor({ngForOf: [1, 2] as any, ngForTrackBy: null as any, ngForTemplate: null as any});
35765 * ```
35766 *
35767 * then inference for `T` fails (it cannot be inferred from `T[] = any`). In this case, `T` takes
35768 * the type `{}`, and so `_t2` is inferred as `NgFor<{}>`. This is obviously wrong.
35769 *
35770 * Adding a default type to the generic declaration in the constructor solves this problem, as the
35771 * default type will be used in the event that inference fails.
35772 *
35773 * ```
35774 * declare function ctor<T = any>(o: Pick<NgFor<T>, 'ngForOf'>): NgFor<T>;
35775 *
35776 * var _t3 = ctor({ngForOf: [1, 2] as any});
35777 * ```
35778 *
35779 * This correctly infers `T` as `any`, and therefore `_t3` as `NgFor<any>`.
35780 */
35781 function typeParametersWithDefaultTypes(params) {
35782 if (params === undefined) {
35783 return undefined;
35784 }
35785 return params.map(param => {
35786 if (param.default === undefined) {
35787 return ts__default["default"].updateTypeParameterDeclaration(
35788 /* node */ param,
35789 /* name */ param.name,
35790 /* constraint */ param.constraint,
35791 /* defaultType */ ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword));
35792 }
35793 else {
35794 return param;
35795 }
35796 });
35797 }
35798
35799 /**
35800 * @license
35801 * Copyright Google LLC All Rights Reserved.
35802 *
35803 * Use of this source code is governed by an MIT-style license that can be
35804 * found in the LICENSE file at https://angular.io/license
35805 */
35806 /**
35807 * A context which hosts one or more Type Check Blocks (TCBs).
35808 *
35809 * An `Environment` supports the generation of TCBs by tracking necessary imports, declarations of
35810 * type constructors, and other statements beyond the type-checking code within the TCB itself.
35811 * Through method calls on `Environment`, the TCB generator can request `ts.Expression`s which
35812 * reference declarations in the `Environment` for these artifacts`.
35813 *
35814 * `Environment` can be used in a standalone fashion, or can be extended to support more specialized
35815 * usage.
35816 */
35817 class Environment {
35818 constructor(config, importManager, refEmitter, reflector, contextFile) {
35819 this.config = config;
35820 this.importManager = importManager;
35821 this.refEmitter = refEmitter;
35822 this.reflector = reflector;
35823 this.contextFile = contextFile;
35824 this.nextIds = {
35825 pipeInst: 1,
35826 typeCtor: 1,
35827 };
35828 this.typeCtors = new Map();
35829 this.typeCtorStatements = [];
35830 this.pipeInsts = new Map();
35831 this.pipeInstStatements = [];
35832 }
35833 /**
35834 * Get an expression referring to a type constructor for the given directive.
35835 *
35836 * Depending on the shape of the directive itself, this could be either a reference to a declared
35837 * type constructor, or to an inline type constructor.
35838 */
35839 typeCtorFor(dir) {
35840 const dirRef = dir.ref;
35841 const node = dirRef.node;
35842 if (this.typeCtors.has(node)) {
35843 return this.typeCtors.get(node);
35844 }
35845 if (requiresInlineTypeCtor(node, this.reflector)) {
35846 // The constructor has already been created inline, we just need to construct a reference to
35847 // it.
35848 const ref = this.reference(dirRef);
35849 const typeCtorExpr = ts__default["default"].createPropertyAccess(ref, 'ngTypeCtor');
35850 this.typeCtors.set(node, typeCtorExpr);
35851 return typeCtorExpr;
35852 }
35853 else {
35854 const fnName = `_ctor${this.nextIds.typeCtor++}`;
35855 const nodeTypeRef = this.referenceType(dirRef);
35856 if (!ts__default["default"].isTypeReferenceNode(nodeTypeRef)) {
35857 throw new Error(`Expected TypeReferenceNode from reference to ${dirRef.debugName}`);
35858 }
35859 const meta = {
35860 fnName,
35861 body: true,
35862 fields: {
35863 inputs: dir.inputs.classPropertyNames,
35864 outputs: dir.outputs.classPropertyNames,
35865 // TODO: support queries
35866 queries: dir.queries,
35867 },
35868 coercedInputFields: dir.coercedInputFields,
35869 };
35870 const typeParams = this.emitTypeParameters(node);
35871 const typeCtor = generateTypeCtorDeclarationFn(node, meta, nodeTypeRef.typeName, typeParams, this.reflector);
35872 this.typeCtorStatements.push(typeCtor);
35873 const fnId = ts__default["default"].createIdentifier(fnName);
35874 this.typeCtors.set(node, fnId);
35875 return fnId;
35876 }
35877 }
35878 /*
35879 * Get an expression referring to an instance of the given pipe.
35880 */
35881 pipeInst(ref) {
35882 if (this.pipeInsts.has(ref.node)) {
35883 return this.pipeInsts.get(ref.node);
35884 }
35885 const pipeType = this.referenceType(ref);
35886 const pipeInstId = ts__default["default"].createIdentifier(`_pipe${this.nextIds.pipeInst++}`);
35887 this.pipeInstStatements.push(tsDeclareVariable(pipeInstId, pipeType));
35888 this.pipeInsts.set(ref.node, pipeInstId);
35889 return pipeInstId;
35890 }
35891 /**
35892 * Generate a `ts.Expression` that references the given node.
35893 *
35894 * This may involve importing the node into the file if it's not declared there already.
35895 */
35896 reference(ref) {
35897 // Disable aliasing for imports generated in a template type-checking context, as there is no
35898 // guarantee that any alias re-exports exist in the .d.ts files. It's safe to use direct imports
35899 // in these cases as there is no strict dependency checking during the template type-checking
35900 // pass.
35901 const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing);
35902 // Use `translateExpression` to convert the `Expression` into a `ts.Expression`.
35903 return translateExpression(ngExpr.expression, this.importManager);
35904 }
35905 /**
35906 * Generate a `ts.TypeNode` that references the given node as a type.
35907 *
35908 * This may involve importing the node into the file if it's not declared there already.
35909 */
35910 referenceType(ref) {
35911 const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing | ImportFlags.AllowTypeImports);
35912 // Create an `ExpressionType` from the `Expression` and translate it via `translateType`.
35913 // TODO(alxhub): support references to types with generic arguments in a clean way.
35914 return translateType(new ExpressionType(ngExpr.expression), this.importManager);
35915 }
35916 emitTypeParameters(declaration) {
35917 const emitter = new TypeParameterEmitter(declaration.typeParameters, this.reflector);
35918 return emitter.emit(ref => this.referenceType(ref));
35919 }
35920 /**
35921 * Generate a `ts.TypeNode` that references a given type from the provided module.
35922 *
35923 * This will involve importing the type into the file, and will also add type parameters if
35924 * provided.
35925 */
35926 referenceExternalType(moduleName, name, typeParams) {
35927 const external = new ExternalExpr({ moduleName, name });
35928 return translateType(new ExpressionType(external, [ /* modifiers */], typeParams), this.importManager);
35929 }
35930 getPreludeStatements() {
35931 return [
35932 ...this.pipeInstStatements,
35933 ...this.typeCtorStatements,
35934 ];
35935 }
35936 }
35937
35938 /**
35939 * @license
35940 * Copyright Google LLC All Rights Reserved.
35941 *
35942 * Use of this source code is governed by an MIT-style license that can be
35943 * found in the LICENSE file at https://angular.io/license
35944 */
35945 class OutOfBandDiagnosticRecorderImpl {
35946 constructor(resolver) {
35947 this.resolver = resolver;
35948 this._diagnostics = [];
35949 /**
35950 * Tracks which `BindingPipe` nodes have already been recorded as invalid, so only one diagnostic
35951 * is ever produced per node.
35952 */
35953 this.recordedPipes = new Set();
35954 }
35955 get diagnostics() {
35956 return this._diagnostics;
35957 }
35958 missingReferenceTarget(templateId, ref) {
35959 const mapping = this.resolver.getSourceMapping(templateId);
35960 const value = ref.value.trim();
35961 const errorMsg = `No directive found with exportAs '${value}'.`;
35962 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, ref.valueSpan || ref.sourceSpan, ts__default["default"].DiagnosticCategory.Error, ngErrorCode(ErrorCode.MISSING_REFERENCE_TARGET), errorMsg));
35963 }
35964 missingPipe(templateId, ast) {
35965 if (this.recordedPipes.has(ast)) {
35966 return;
35967 }
35968 const mapping = this.resolver.getSourceMapping(templateId);
35969 const errorMsg = `No pipe found with name '${ast.name}'.`;
35970 const sourceSpan = this.resolver.toParseSourceSpan(templateId, ast.nameSpan);
35971 if (sourceSpan === null) {
35972 throw new Error(`Assertion failure: no SourceLocation found for usage of pipe '${ast.name}'.`);
35973 }
35974 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, sourceSpan, ts__default["default"].DiagnosticCategory.Error, ngErrorCode(ErrorCode.MISSING_PIPE), errorMsg));
35975 this.recordedPipes.add(ast);
35976 }
35977 illegalAssignmentToTemplateVar(templateId, assignment, target) {
35978 const mapping = this.resolver.getSourceMapping(templateId);
35979 const errorMsg = `Cannot use variable '${assignment
35980 .name}' as the left-hand side of an assignment expression. Template variables are read-only.`;
35981 const sourceSpan = this.resolver.toParseSourceSpan(templateId, assignment.sourceSpan);
35982 if (sourceSpan === null) {
35983 throw new Error(`Assertion failure: no SourceLocation found for property binding.`);
35984 }
35985 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, sourceSpan, ts__default["default"].DiagnosticCategory.Error, ngErrorCode(ErrorCode.WRITE_TO_READ_ONLY_VARIABLE), errorMsg, [{
35986 text: `The variable ${assignment.name} is declared here.`,
35987 start: target.valueSpan?.start.offset || target.sourceSpan.start.offset,
35988 end: target.valueSpan?.end.offset || target.sourceSpan.end.offset,
35989 sourceFile: mapping.node.getSourceFile(),
35990 }]));
35991 }
35992 duplicateTemplateVar(templateId, variable, firstDecl) {
35993 const mapping = this.resolver.getSourceMapping(templateId);
35994 const errorMsg = `Cannot redeclare variable '${variable.name}' as it was previously declared elsewhere for the same template.`;
35995 // The allocation of the error here is pretty useless for variables declared in microsyntax,
35996 // since the sourceSpan refers to the entire microsyntax property, not a span for the specific
35997 // variable in question.
35998 //
35999 // TODO(alxhub): allocate to a tighter span once one is available.
36000 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, variable.sourceSpan, ts__default["default"].DiagnosticCategory.Error, ngErrorCode(ErrorCode.DUPLICATE_VARIABLE_DECLARATION), errorMsg, [{
36001 text: `The variable '${firstDecl.name}' was first declared here.`,
36002 start: firstDecl.sourceSpan.start.offset,
36003 end: firstDecl.sourceSpan.end.offset,
36004 sourceFile: mapping.node.getSourceFile(),
36005 }]));
36006 }
36007 requiresInlineTcb(templateId, node) {
36008 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.`));
36009 }
36010 requiresInlineTypeConstructors(templateId, node, directives) {
36011 let message;
36012 if (directives.length > 1) {
36013 message =
36014 `This component uses directives which require inline type constructors, which are not supported by the current environment.`;
36015 }
36016 else {
36017 message =
36018 `This component uses a directive which requires an inline type constructor, which is not supported by the current environment.`;
36019 }
36020 this._diagnostics.push(makeInlineDiagnostic(templateId, ErrorCode.INLINE_TYPE_CTOR_REQUIRED, node.name, message, directives.map(dir => makeRelatedInformation(dir.name, `Requires an inline type constructor.`))));
36021 }
36022 suboptimalTypeInference(templateId, variables) {
36023 const mapping = this.resolver.getSourceMapping(templateId);
36024 // Select one of the template variables that's most suitable for reporting the diagnostic. Any
36025 // variable will do, but prefer one bound to the context's $implicit if present.
36026 let diagnosticVar = null;
36027 for (const variable of variables) {
36028 if (diagnosticVar === null || (variable.value === '' || variable.value === '$implicit')) {
36029 diagnosticVar = variable;
36030 }
36031 }
36032 if (diagnosticVar === null) {
36033 // There is no variable on which to report the diagnostic.
36034 return;
36035 }
36036 let varIdentification = `'${diagnosticVar.name}'`;
36037 if (variables.length === 2) {
36038 varIdentification += ` (and 1 other)`;
36039 }
36040 else if (variables.length > 2) {
36041 varIdentification += ` (and ${variables.length - 1} others)`;
36042 }
36043 const message = `This structural directive supports advanced type inference, but the current compiler configuration prevents its usage. The variable ${varIdentification} will have type 'any' as a result.\n\nConsider enabling the 'strictTemplates' option in your tsconfig.json for better type inference within this template.`;
36044 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, diagnosticVar.keySpan, ts__default["default"].DiagnosticCategory.Suggestion, ngErrorCode(ErrorCode.SUGGEST_SUBOPTIMAL_TYPE_INFERENCE), message));
36045 }
36046 splitTwoWayBinding(templateId, input, output, inputConsumer, outputConsumer) {
36047 const mapping = this.resolver.getSourceMapping(templateId);
36048 const errorMsg = `The property and event halves of the two-way binding '${input.name}' are not bound to the same target.
36049 Find more at https://angular.io/guide/two-way-binding#how-two-way-binding-works`;
36050 const relatedMessages = [];
36051 relatedMessages.push({
36052 text: `The property half of the binding is to the '${inputConsumer.name.text}' component.`,
36053 start: inputConsumer.name.getStart(),
36054 end: inputConsumer.name.getEnd(),
36055 sourceFile: inputConsumer.name.getSourceFile(),
36056 });
36057 if (outputConsumer instanceof Element$1) {
36058 let message = `The event half of the binding is to a native event called '${input.name}' on the <${outputConsumer.name}> DOM element.`;
36059 if (!mapping.node.getSourceFile().isDeclarationFile) {
36060 message += `\n \n Are you missing an output declaration called '${output.name}'?`;
36061 }
36062 relatedMessages.push({
36063 text: message,
36064 start: outputConsumer.sourceSpan.start.offset + 1,
36065 end: outputConsumer.sourceSpan.start.offset + outputConsumer.name.length + 1,
36066 sourceFile: mapping.node.getSourceFile(),
36067 });
36068 }
36069 else {
36070 relatedMessages.push({
36071 text: `The event half of the binding is to the '${outputConsumer.name.text}' component.`,
36072 start: outputConsumer.name.getStart(),
36073 end: outputConsumer.name.getEnd(),
36074 sourceFile: outputConsumer.name.getSourceFile(),
36075 });
36076 }
36077 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, input.keySpan, ts__default["default"].DiagnosticCategory.Error, ngErrorCode(ErrorCode.SPLIT_TWO_WAY_BINDING), errorMsg, relatedMessages));
36078 }
36079 }
36080 function makeInlineDiagnostic(templateId, code, node, messageText, relatedInformation) {
36081 return {
36082 ...makeDiagnostic(code, node, messageText, relatedInformation),
36083 componentFile: node.getSourceFile(),
36084 templateId,
36085 };
36086 }
36087
36088 /**
36089 * @license
36090 * Copyright Google LLC All Rights Reserved.
36091 *
36092 * Use of this source code is governed by an MIT-style license that can be
36093 * found in the LICENSE file at https://angular.io/license
36094 */
36095 /**
36096 * A `ShimGenerator` which adds type-checking files to the `ts.Program`.
36097 *
36098 * This is a requirement for performant template type-checking, as TypeScript will only reuse
36099 * information in the main program when creating the type-checking program if the set of files in
36100 * each are exactly the same. Thus, the main program also needs the synthetic type-checking files.
36101 */
36102 class TypeCheckShimGenerator {
36103 constructor() {
36104 this.extensionPrefix = 'ngtypecheck';
36105 this.shouldEmit = false;
36106 }
36107 generateShimForFile(sf, genFilePath, priorShimSf) {
36108 if (priorShimSf !== null) {
36109 // If this shim existed in the previous program, reuse it now. It might not be correct, but
36110 // reusing it in the main program allows the shape of its imports to potentially remain the
36111 // same and TS can then use the fastest path for incremental program creation. Later during
36112 // the type-checking phase it's going to either be reused, or replaced anyways. Thus there's
36113 // no harm in reuse here even if it's out of date.
36114 return priorShimSf;
36115 }
36116 return ts__default["default"].createSourceFile(genFilePath, 'export const USED_FOR_NG_TYPE_CHECKING = true;', ts__default["default"].ScriptTarget.Latest, true, ts__default["default"].ScriptKind.TS);
36117 }
36118 static shimFor(fileName) {
36119 return absoluteFrom(fileName.replace(/\.tsx?$/, '.ngtypecheck.ts'));
36120 }
36121 }
36122
36123 /**
36124 * @license
36125 * Copyright Google LLC All Rights Reserved.
36126 *
36127 * Use of this source code is governed by an MIT-style license that can be
36128 * found in the LICENSE file at https://angular.io/license
36129 */
36130 /**
36131 * Wraps the node in parenthesis such that inserted span comments become attached to the proper
36132 * node. This is an alias for `ts.createParen` with the benefit that it signifies that the
36133 * inserted parenthesis are for diagnostic purposes, not for correctness of the rendered TCB code.
36134 *
36135 * Note that it is important that nodes and its attached comment are not wrapped into parenthesis
36136 * by default, as it prevents correct translation of e.g. diagnostics produced for incorrect method
36137 * arguments. Such diagnostics would then be produced for the parenthesised node whereas the
36138 * positional comment would be located within that node, resulting in a mismatch.
36139 */
36140 function wrapForDiagnostics(expr) {
36141 return ts__default["default"].createParen(expr);
36142 }
36143 /**
36144 * Wraps the node in parenthesis such that inserted span comments become attached to the proper
36145 * node. This is an alias for `ts.createParen` with the benefit that it signifies that the
36146 * inserted parenthesis are for use by the type checker, not for correctness of the rendered TCB
36147 * code.
36148 */
36149 function wrapForTypeChecker(expr) {
36150 return ts__default["default"].createParen(expr);
36151 }
36152 /**
36153 * Adds a synthetic comment to the expression that represents the parse span of the provided node.
36154 * This comment can later be retrieved as trivia of a node to recover original source locations.
36155 */
36156 function addParseSpanInfo(node, span) {
36157 let commentText;
36158 if (span instanceof AbsoluteSourceSpan$1) {
36159 commentText = `${span.start},${span.end}`;
36160 }
36161 else {
36162 commentText = `${span.start.offset},${span.end.offset}`;
36163 }
36164 ts__default["default"].addSyntheticTrailingComment(node, ts__default["default"].SyntaxKind.MultiLineCommentTrivia, commentText, /* hasTrailingNewLine */ false);
36165 }
36166 /**
36167 * Adds a synthetic comment to the function declaration that contains the template id
36168 * of the class declaration.
36169 */
36170 function addTemplateId(tcb, id) {
36171 ts__default["default"].addSyntheticLeadingComment(tcb, ts__default["default"].SyntaxKind.MultiLineCommentTrivia, id, true);
36172 }
36173 /**
36174 * Determines if the diagnostic should be reported. Some diagnostics are produced because of the
36175 * way TCBs are generated; those diagnostics should not be reported as type check errors of the
36176 * template.
36177 */
36178 function shouldReportDiagnostic(diagnostic) {
36179 const { code } = diagnostic;
36180 if (code === 6133 /* $var is declared but its value is never read. */) {
36181 return false;
36182 }
36183 else if (code === 6199 /* All variables are unused. */) {
36184 return false;
36185 }
36186 else if (code === 2695 /* Left side of comma operator is unused and has no side effects. */) {
36187 return false;
36188 }
36189 else if (code === 7006 /* Parameter '$event' implicitly has an 'any' type. */) {
36190 return false;
36191 }
36192 return true;
36193 }
36194 /**
36195 * Attempts to translate a TypeScript diagnostic produced during template type-checking to their
36196 * location of origin, based on the comments that are emitted in the TCB code.
36197 *
36198 * If the diagnostic could not be translated, `null` is returned to indicate that the diagnostic
36199 * should not be reported at all. This prevents diagnostics from non-TCB code in a user's source
36200 * file from being reported as type-check errors.
36201 */
36202 function translateDiagnostic(diagnostic, resolver) {
36203 if (diagnostic.file === undefined || diagnostic.start === undefined) {
36204 return null;
36205 }
36206 const fullMapping = getTemplateMapping(diagnostic.file, diagnostic.start, resolver, /*isDiagnosticsRequest*/ true);
36207 if (fullMapping === null) {
36208 return null;
36209 }
36210 const { sourceLocation, templateSourceMapping, span } = fullMapping;
36211 return makeTemplateDiagnostic(sourceLocation.id, templateSourceMapping, span, diagnostic.category, diagnostic.code, diagnostic.messageText);
36212 }
36213
36214 /**
36215 * @license
36216 * Copyright Google LLC All Rights Reserved.
36217 *
36218 * Use of this source code is governed by an MIT-style license that can be
36219 * found in the LICENSE file at https://angular.io/license
36220 */
36221 const NULL_AS_ANY = ts__default["default"].createAsExpression(ts__default["default"].createNull(), ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword));
36222 const UNDEFINED = ts__default["default"].createIdentifier('undefined');
36223 const UNARY_OPS = new Map([
36224 ['+', ts__default["default"].SyntaxKind.PlusToken],
36225 ['-', ts__default["default"].SyntaxKind.MinusToken],
36226 ]);
36227 const BINARY_OPS = new Map([
36228 ['+', ts__default["default"].SyntaxKind.PlusToken],
36229 ['-', ts__default["default"].SyntaxKind.MinusToken],
36230 ['<', ts__default["default"].SyntaxKind.LessThanToken],
36231 ['>', ts__default["default"].SyntaxKind.GreaterThanToken],
36232 ['<=', ts__default["default"].SyntaxKind.LessThanEqualsToken],
36233 ['>=', ts__default["default"].SyntaxKind.GreaterThanEqualsToken],
36234 ['==', ts__default["default"].SyntaxKind.EqualsEqualsToken],
36235 ['===', ts__default["default"].SyntaxKind.EqualsEqualsEqualsToken],
36236 ['*', ts__default["default"].SyntaxKind.AsteriskToken],
36237 ['/', ts__default["default"].SyntaxKind.SlashToken],
36238 ['%', ts__default["default"].SyntaxKind.PercentToken],
36239 ['!=', ts__default["default"].SyntaxKind.ExclamationEqualsToken],
36240 ['!==', ts__default["default"].SyntaxKind.ExclamationEqualsEqualsToken],
36241 ['||', ts__default["default"].SyntaxKind.BarBarToken],
36242 ['&&', ts__default["default"].SyntaxKind.AmpersandAmpersandToken],
36243 ['&', ts__default["default"].SyntaxKind.AmpersandToken],
36244 ['|', ts__default["default"].SyntaxKind.BarToken],
36245 ['??', ts__default["default"].SyntaxKind.QuestionQuestionToken],
36246 ]);
36247 /**
36248 * Convert an `AST` to TypeScript code directly, without going through an intermediate `Expression`
36249 * AST.
36250 */
36251 function astToTypescript(ast, maybeResolve, config) {
36252 const translator = new AstTranslator(maybeResolve, config);
36253 return translator.translate(ast);
36254 }
36255 class AstTranslator {
36256 constructor(maybeResolve, config) {
36257 this.maybeResolve = maybeResolve;
36258 this.config = config;
36259 }
36260 translate(ast) {
36261 // Skip over an `ASTWithSource` as its `visit` method calls directly into its ast's `visit`,
36262 // which would prevent any custom resolution through `maybeResolve` for that node.
36263 if (ast instanceof ASTWithSource) {
36264 ast = ast.ast;
36265 }
36266 // The `EmptyExpr` doesn't have a dedicated method on `AstVisitor`, so it's special cased here.
36267 if (ast instanceof EmptyExpr) {
36268 const res = ts__default["default"].factory.createIdentifier('undefined');
36269 addParseSpanInfo(res, ast.sourceSpan);
36270 return res;
36271 }
36272 // First attempt to let any custom resolution logic provide a translation for the given node.
36273 const resolved = this.maybeResolve(ast);
36274 if (resolved !== null) {
36275 return resolved;
36276 }
36277 return ast.visit(this);
36278 }
36279 visitUnary(ast) {
36280 const expr = this.translate(ast.expr);
36281 const op = UNARY_OPS.get(ast.operator);
36282 if (op === undefined) {
36283 throw new Error(`Unsupported Unary.operator: ${ast.operator}`);
36284 }
36285 const node = wrapForDiagnostics(ts__default["default"].createPrefix(op, expr));
36286 addParseSpanInfo(node, ast.sourceSpan);
36287 return node;
36288 }
36289 visitBinary(ast) {
36290 const lhs = wrapForDiagnostics(this.translate(ast.left));
36291 const rhs = wrapForDiagnostics(this.translate(ast.right));
36292 const op = BINARY_OPS.get(ast.operation);
36293 if (op === undefined) {
36294 throw new Error(`Unsupported Binary.operation: ${ast.operation}`);
36295 }
36296 const node = ts__default["default"].createBinary(lhs, op, rhs);
36297 addParseSpanInfo(node, ast.sourceSpan);
36298 return node;
36299 }
36300 visitChain(ast) {
36301 const elements = ast.expressions.map(expr => this.translate(expr));
36302 const node = wrapForDiagnostics(ts__default["default"].createCommaList(elements));
36303 addParseSpanInfo(node, ast.sourceSpan);
36304 return node;
36305 }
36306 visitConditional(ast) {
36307 const condExpr = this.translate(ast.condition);
36308 const trueExpr = this.translate(ast.trueExp);
36309 // Wrap `falseExpr` in parens so that the trailing parse span info is not attributed to the
36310 // whole conditional.
36311 // In the following example, the last source span comment (5,6) could be seen as the
36312 // trailing comment for _either_ the whole conditional expression _or_ just the `falseExpr` that
36313 // is immediately before it:
36314 // `conditional /*1,2*/ ? trueExpr /*3,4*/ : falseExpr /*5,6*/`
36315 // This should be instead be `conditional /*1,2*/ ? trueExpr /*3,4*/ : (falseExpr /*5,6*/)`
36316 const falseExpr = wrapForTypeChecker(this.translate(ast.falseExp));
36317 const node = ts__default["default"].createParen(ts__default["default"].createConditional(condExpr, trueExpr, falseExpr));
36318 addParseSpanInfo(node, ast.sourceSpan);
36319 return node;
36320 }
36321 visitImplicitReceiver(ast) {
36322 throw new Error('Method not implemented.');
36323 }
36324 visitThisReceiver(ast) {
36325 throw new Error('Method not implemented.');
36326 }
36327 visitInterpolation(ast) {
36328 // Build up a chain of binary + operations to simulate the string concatenation of the
36329 // interpolation's expressions. The chain is started using an actual string literal to ensure
36330 // the type is inferred as 'string'.
36331 return ast.expressions.reduce((lhs, ast) => ts__default["default"].createBinary(lhs, ts__default["default"].SyntaxKind.PlusToken, wrapForTypeChecker(this.translate(ast))), ts__default["default"].createLiteral(''));
36332 }
36333 visitKeyedRead(ast) {
36334 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
36335 const key = this.translate(ast.key);
36336 const node = ts__default["default"].createElementAccess(receiver, key);
36337 addParseSpanInfo(node, ast.sourceSpan);
36338 return node;
36339 }
36340 visitKeyedWrite(ast) {
36341 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
36342 const left = ts__default["default"].createElementAccess(receiver, this.translate(ast.key));
36343 // TODO(joost): annotate `left` with the span of the element access, which is not currently
36344 // available on `ast`.
36345 const right = wrapForTypeChecker(this.translate(ast.value));
36346 const node = wrapForDiagnostics(ts__default["default"].createBinary(left, ts__default["default"].SyntaxKind.EqualsToken, right));
36347 addParseSpanInfo(node, ast.sourceSpan);
36348 return node;
36349 }
36350 visitLiteralArray(ast) {
36351 const elements = ast.expressions.map(expr => this.translate(expr));
36352 const literal = ts__default["default"].createArrayLiteral(elements);
36353 // If strictLiteralTypes is disabled, array literals are cast to `any`.
36354 const node = this.config.strictLiteralTypes ? literal : tsCastToAny(literal);
36355 addParseSpanInfo(node, ast.sourceSpan);
36356 return node;
36357 }
36358 visitLiteralMap(ast) {
36359 const properties = ast.keys.map(({ key }, idx) => {
36360 const value = this.translate(ast.values[idx]);
36361 return ts__default["default"].createPropertyAssignment(ts__default["default"].createStringLiteral(key), value);
36362 });
36363 const literal = ts__default["default"].createObjectLiteral(properties, true);
36364 // If strictLiteralTypes is disabled, object literals are cast to `any`.
36365 const node = this.config.strictLiteralTypes ? literal : tsCastToAny(literal);
36366 addParseSpanInfo(node, ast.sourceSpan);
36367 return node;
36368 }
36369 visitLiteralPrimitive(ast) {
36370 let node;
36371 if (ast.value === undefined) {
36372 node = ts__default["default"].createIdentifier('undefined');
36373 }
36374 else if (ast.value === null) {
36375 node = ts__default["default"].createNull();
36376 }
36377 else {
36378 node = ts__default["default"].createLiteral(ast.value);
36379 }
36380 addParseSpanInfo(node, ast.sourceSpan);
36381 return node;
36382 }
36383 visitNonNullAssert(ast) {
36384 const expr = wrapForDiagnostics(this.translate(ast.expression));
36385 const node = ts__default["default"].createNonNullExpression(expr);
36386 addParseSpanInfo(node, ast.sourceSpan);
36387 return node;
36388 }
36389 visitPipe(ast) {
36390 throw new Error('Method not implemented.');
36391 }
36392 visitPrefixNot(ast) {
36393 const expression = wrapForDiagnostics(this.translate(ast.expression));
36394 const node = ts__default["default"].createLogicalNot(expression);
36395 addParseSpanInfo(node, ast.sourceSpan);
36396 return node;
36397 }
36398 visitPropertyRead(ast) {
36399 // This is a normal property read - convert the receiver to an expression and emit the correct
36400 // TypeScript expression to read the property.
36401 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
36402 const name = ts__default["default"].createPropertyAccess(receiver, ast.name);
36403 addParseSpanInfo(name, ast.nameSpan);
36404 const node = wrapForDiagnostics(name);
36405 addParseSpanInfo(node, ast.sourceSpan);
36406 return node;
36407 }
36408 visitPropertyWrite(ast) {
36409 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
36410 const left = ts__default["default"].createPropertyAccess(receiver, ast.name);
36411 addParseSpanInfo(left, ast.nameSpan);
36412 // TypeScript reports assignment errors on the entire lvalue expression. Annotate the lvalue of
36413 // the assignment with the sourceSpan, which includes receivers, rather than nameSpan for
36414 // consistency of the diagnostic location.
36415 // a.b.c = 1
36416 // ^^^^^^^^^ sourceSpan
36417 // ^ nameSpan
36418 const leftWithPath = wrapForDiagnostics(left);
36419 addParseSpanInfo(leftWithPath, ast.sourceSpan);
36420 // The right needs to be wrapped in parens as well or we cannot accurately match its
36421 // span to just the RHS. For example, the span in `e = $event /*0,10*/` is ambiguous.
36422 // It could refer to either the whole binary expression or just the RHS.
36423 // We should instead generate `e = ($event /*0,10*/)` so we know the span 0,10 matches RHS.
36424 const right = wrapForTypeChecker(this.translate(ast.value));
36425 const node = wrapForDiagnostics(ts__default["default"].createBinary(leftWithPath, ts__default["default"].SyntaxKind.EqualsToken, right));
36426 addParseSpanInfo(node, ast.sourceSpan);
36427 return node;
36428 }
36429 visitQuote(ast) {
36430 return NULL_AS_ANY;
36431 }
36432 visitSafePropertyRead(ast) {
36433 let node;
36434 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
36435 // The form of safe property reads depends on whether strictness is in use.
36436 if (this.config.strictSafeNavigationTypes) {
36437 // Basically, the return here is either the type of the complete expression with a null-safe
36438 // property read, or `undefined`. So a ternary is used to create an "or" type:
36439 // "a?.b" becomes (null as any ? a!.b : undefined)
36440 // The type of this expression is (typeof a!.b) | undefined, which is exactly as desired.
36441 const expr = ts__default["default"].createPropertyAccess(ts__default["default"].createNonNullExpression(receiver), ast.name);
36442 addParseSpanInfo(expr, ast.nameSpan);
36443 node = ts__default["default"].createParen(ts__default["default"].createConditional(NULL_AS_ANY, expr, UNDEFINED));
36444 }
36445 else if (VeSafeLhsInferenceBugDetector.veWillInferAnyFor(ast)) {
36446 // Emulate a View Engine bug where 'any' is inferred for the left-hand side of the safe
36447 // navigation operation. With this bug, the type of the left-hand side is regarded as any.
36448 // Therefore, the left-hand side only needs repeating in the output (to validate it), and then
36449 // 'any' is used for the rest of the expression. This is done using a comma operator:
36450 // "a?.b" becomes (a as any).b, which will of course have type 'any'.
36451 node = ts__default["default"].createPropertyAccess(tsCastToAny(receiver), ast.name);
36452 }
36453 else {
36454 // The View Engine bug isn't active, so check the entire type of the expression, but the final
36455 // result is still inferred as `any`.
36456 // "a?.b" becomes (a!.b as any)
36457 const expr = ts__default["default"].createPropertyAccess(ts__default["default"].createNonNullExpression(receiver), ast.name);
36458 addParseSpanInfo(expr, ast.nameSpan);
36459 node = tsCastToAny(expr);
36460 }
36461 addParseSpanInfo(node, ast.sourceSpan);
36462 return node;
36463 }
36464 visitSafeKeyedRead(ast) {
36465 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
36466 const key = this.translate(ast.key);
36467 let node;
36468 // The form of safe property reads depends on whether strictness is in use.
36469 if (this.config.strictSafeNavigationTypes) {
36470 // "a?.[...]" becomes (null as any ? a![...] : undefined)
36471 const expr = ts__default["default"].createElementAccess(ts__default["default"].createNonNullExpression(receiver), key);
36472 addParseSpanInfo(expr, ast.sourceSpan);
36473 node = ts__default["default"].createParen(ts__default["default"].createConditional(NULL_AS_ANY, expr, UNDEFINED));
36474 }
36475 else if (VeSafeLhsInferenceBugDetector.veWillInferAnyFor(ast)) {
36476 // "a?.[...]" becomes (a as any)[...]
36477 node = ts__default["default"].createElementAccess(tsCastToAny(receiver), key);
36478 }
36479 else {
36480 // "a?.[...]" becomes (a!.[...] as any)
36481 const expr = ts__default["default"].createElementAccess(ts__default["default"].createNonNullExpression(receiver), key);
36482 addParseSpanInfo(expr, ast.sourceSpan);
36483 node = tsCastToAny(expr);
36484 }
36485 addParseSpanInfo(node, ast.sourceSpan);
36486 return node;
36487 }
36488 visitCall(ast) {
36489 const args = ast.args.map(expr => this.translate(expr));
36490 const expr = wrapForDiagnostics(this.translate(ast.receiver));
36491 let node;
36492 // Safe property/keyed reads will produce a ternary whose value is nullable.
36493 // We have to generate a similar ternary around the call.
36494 if (ast.receiver instanceof SafePropertyRead || ast.receiver instanceof SafeKeyedRead) {
36495 if (this.config.strictSafeNavigationTypes) {
36496 // "a?.method(...)" becomes (null as any ? a!.method(...) : undefined)
36497 const call = ts__default["default"].createCall(ts__default["default"].createNonNullExpression(expr), undefined, args);
36498 node = ts__default["default"].createParen(ts__default["default"].createConditional(NULL_AS_ANY, call, UNDEFINED));
36499 }
36500 else if (VeSafeLhsInferenceBugDetector.veWillInferAnyFor(ast)) {
36501 // "a?.method(...)" becomes (a as any).method(...)
36502 node = ts__default["default"].createCall(tsCastToAny(expr), undefined, args);
36503 }
36504 else {
36505 // "a?.method(...)" becomes (a!.method(...) as any)
36506 node = tsCastToAny(ts__default["default"].createCall(ts__default["default"].createNonNullExpression(expr), undefined, args));
36507 }
36508 }
36509 else {
36510 node = ts__default["default"].createCall(expr, undefined, args);
36511 }
36512 addParseSpanInfo(node, ast.sourceSpan);
36513 return node;
36514 }
36515 }
36516 /**
36517 * Checks whether View Engine will infer a type of 'any' for the left-hand side of a safe navigation
36518 * operation.
36519 *
36520 * In View Engine's template type-checker, certain receivers of safe navigation operations will
36521 * cause a temporary variable to be allocated as part of the checking expression, to save the value
36522 * of the receiver and use it more than once in the expression. This temporary variable has type
36523 * 'any'. In practice, this means certain receivers cause View Engine to not check the full
36524 * expression, and other receivers will receive more complete checking.
36525 *
36526 * For compatibility, this logic is adapted from View Engine's expression_converter.ts so that the
36527 * Ivy checker can emulate this bug when needed.
36528 */
36529 class VeSafeLhsInferenceBugDetector {
36530 static veWillInferAnyFor(ast) {
36531 const visitor = VeSafeLhsInferenceBugDetector.SINGLETON;
36532 return ast instanceof Call ? ast.visit(visitor) : ast.receiver.visit(visitor);
36533 }
36534 visitUnary(ast) {
36535 return ast.expr.visit(this);
36536 }
36537 visitBinary(ast) {
36538 return ast.left.visit(this) || ast.right.visit(this);
36539 }
36540 visitChain(ast) {
36541 return false;
36542 }
36543 visitConditional(ast) {
36544 return ast.condition.visit(this) || ast.trueExp.visit(this) || ast.falseExp.visit(this);
36545 }
36546 visitCall(ast) {
36547 return true;
36548 }
36549 visitImplicitReceiver(ast) {
36550 return false;
36551 }
36552 visitThisReceiver(ast) {
36553 return false;
36554 }
36555 visitInterpolation(ast) {
36556 return ast.expressions.some(exp => exp.visit(this));
36557 }
36558 visitKeyedRead(ast) {
36559 return false;
36560 }
36561 visitKeyedWrite(ast) {
36562 return false;
36563 }
36564 visitLiteralArray(ast) {
36565 return true;
36566 }
36567 visitLiteralMap(ast) {
36568 return true;
36569 }
36570 visitLiteralPrimitive(ast) {
36571 return false;
36572 }
36573 visitPipe(ast) {
36574 return true;
36575 }
36576 visitPrefixNot(ast) {
36577 return ast.expression.visit(this);
36578 }
36579 visitNonNullAssert(ast) {
36580 return ast.expression.visit(this);
36581 }
36582 visitPropertyRead(ast) {
36583 return false;
36584 }
36585 visitPropertyWrite(ast) {
36586 return false;
36587 }
36588 visitQuote(ast) {
36589 return false;
36590 }
36591 visitSafePropertyRead(ast) {
36592 return false;
36593 }
36594 visitSafeKeyedRead(ast) {
36595 return false;
36596 }
36597 }
36598 VeSafeLhsInferenceBugDetector.SINGLETON = new VeSafeLhsInferenceBugDetector();
36599
36600 /**
36601 * @license
36602 * Copyright Google LLC All Rights Reserved.
36603 *
36604 * Use of this source code is governed by an MIT-style license that can be
36605 * found in the LICENSE file at https://angular.io/license
36606 */
36607 /**
36608 * Visits a template and records any semantic errors within its expressions.
36609 */
36610 class ExpressionSemanticVisitor extends RecursiveAstVisitor {
36611 constructor(templateId, boundTarget, oob) {
36612 super();
36613 this.templateId = templateId;
36614 this.boundTarget = boundTarget;
36615 this.oob = oob;
36616 }
36617 visitPropertyWrite(ast, context) {
36618 super.visitPropertyWrite(ast, context);
36619 if (!(ast.receiver instanceof ImplicitReceiver)) {
36620 return;
36621 }
36622 const target = this.boundTarget.getExpressionTarget(ast);
36623 if (target instanceof Variable) {
36624 // Template variables are read-only.
36625 this.oob.illegalAssignmentToTemplateVar(this.templateId, ast, target);
36626 }
36627 }
36628 static visit(ast, id, boundTarget, oob) {
36629 ast.visit(new ExpressionSemanticVisitor(id, boundTarget, oob));
36630 }
36631 }
36632
36633 /**
36634 * @license
36635 * Copyright Google LLC All Rights Reserved.
36636 *
36637 * Use of this source code is governed by an MIT-style license that can be
36638 * found in the LICENSE file at https://angular.io/license
36639 */
36640 /**
36641 * Controls how generics for the component context class will be handled during TCB generation.
36642 */
36643 var TcbGenericContextBehavior;
36644 (function (TcbGenericContextBehavior) {
36645 /**
36646 * References to generic parameter bounds will be emitted via the `TypeParameterEmitter`.
36647 *
36648 * The caller must verify that all parameter bounds are emittable in order to use this mode.
36649 */
36650 TcbGenericContextBehavior[TcbGenericContextBehavior["UseEmitter"] = 0] = "UseEmitter";
36651 /**
36652 * Generic parameter declarations will be copied directly from the `ts.ClassDeclaration` of the
36653 * component class.
36654 *
36655 * The caller must only use the generated TCB code in a context where such copies will still be
36656 * valid, such as an inline type check block.
36657 */
36658 TcbGenericContextBehavior[TcbGenericContextBehavior["CopyClassNodes"] = 1] = "CopyClassNodes";
36659 /**
36660 * Any generic parameters for the component context class will be set to `any`.
36661 *
36662 * Produces a less useful type, but is always safe to use.
36663 */
36664 TcbGenericContextBehavior[TcbGenericContextBehavior["FallbackToAny"] = 2] = "FallbackToAny";
36665 })(TcbGenericContextBehavior || (TcbGenericContextBehavior = {}));
36666 /**
36667 * Given a `ts.ClassDeclaration` for a component, and metadata regarding that component, compose a
36668 * "type check block" function.
36669 *
36670 * When passed through TypeScript's TypeChecker, type errors that arise within the type check block
36671 * function indicate issues in the template itself.
36672 *
36673 * As a side effect of generating a TCB for the component, `ts.Diagnostic`s may also be produced
36674 * directly for issues within the template which are identified during generation. These issues are
36675 * recorded in either the `domSchemaChecker` (which checks usage of DOM elements and bindings) as
36676 * well as the `oobRecorder` (which records errors when the type-checking code generator is unable
36677 * to sufficiently understand a template).
36678 *
36679 * @param env an `Environment` into which type-checking code will be generated.
36680 * @param ref a `Reference` to the component class which should be type-checked.
36681 * @param name a `ts.Identifier` to use for the generated `ts.FunctionDeclaration`.
36682 * @param meta metadata about the component's template and the function being generated.
36683 * @param domSchemaChecker used to check and record errors regarding improper usage of DOM elements
36684 * and bindings.
36685 * @param oobRecorder used to record errors regarding template elements which could not be correctly
36686 * translated into types during TCB generation.
36687 * @param genericContextBehavior controls how generic parameters (especially parameters with generic
36688 * bounds) will be referenced from the generated TCB code.
36689 */
36690 function generateTypeCheckBlock(env, ref, name, meta, domSchemaChecker, oobRecorder, genericContextBehavior) {
36691 const tcb = new Context(env, domSchemaChecker, oobRecorder, meta.id, meta.boundTarget, meta.pipes, meta.schemas);
36692 const scope = Scope.forNodes(tcb, null, tcb.boundTarget.target.template, /* guard */ null);
36693 const ctxRawType = env.referenceType(ref);
36694 if (!ts__default["default"].isTypeReferenceNode(ctxRawType)) {
36695 throw new Error(`Expected TypeReferenceNode when referencing the ctx param for ${ref.debugName}`);
36696 }
36697 let typeParameters = undefined;
36698 let typeArguments = undefined;
36699 if (ref.node.typeParameters !== undefined) {
36700 if (!env.config.useContextGenericType) {
36701 genericContextBehavior = TcbGenericContextBehavior.FallbackToAny;
36702 }
36703 switch (genericContextBehavior) {
36704 case TcbGenericContextBehavior.UseEmitter:
36705 // Guaranteed to emit type parameters since we checked that the class has them above.
36706 typeParameters = new TypeParameterEmitter(ref.node.typeParameters, env.reflector)
36707 .emit(typeRef => env.referenceType(typeRef));
36708 typeArguments = typeParameters.map(param => ts__default["default"].factory.createTypeReferenceNode(param.name));
36709 break;
36710 case TcbGenericContextBehavior.CopyClassNodes:
36711 typeParameters = [...ref.node.typeParameters];
36712 typeArguments = typeParameters.map(param => ts__default["default"].factory.createTypeReferenceNode(param.name));
36713 break;
36714 case TcbGenericContextBehavior.FallbackToAny:
36715 typeArguments = ref.node.typeParameters.map(() => ts__default["default"].factory.createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword));
36716 break;
36717 }
36718 }
36719 const paramList = [tcbCtxParam(ref.node, ctxRawType.typeName, typeArguments)];
36720 const scopeStatements = scope.render();
36721 const innerBody = ts__default["default"].createBlock([
36722 ...env.getPreludeStatements(),
36723 ...scopeStatements,
36724 ]);
36725 // Wrap the body in an "if (true)" expression. This is unnecessary but has the effect of causing
36726 // the `ts.Printer` to format the type-check block nicely.
36727 const body = ts__default["default"].createBlock([ts__default["default"].createIf(ts__default["default"].createTrue(), innerBody, undefined)]);
36728 const fnDecl = ts__default["default"].createFunctionDeclaration(
36729 /* decorators */ undefined,
36730 /* modifiers */ undefined,
36731 /* asteriskToken */ undefined,
36732 /* name */ name,
36733 /* typeParameters */ env.config.useContextGenericType ? typeParameters : undefined,
36734 /* parameters */ paramList,
36735 /* type */ undefined,
36736 /* body */ body);
36737 addTemplateId(fnDecl, meta.id);
36738 return fnDecl;
36739 }
36740 /**
36741 * A code generation operation that's involved in the construction of a Type Check Block.
36742 *
36743 * The generation of a TCB is non-linear. Bindings within a template may result in the need to
36744 * construct certain types earlier than they otherwise would be constructed. That is, if the
36745 * generation of a TCB for a template is broken down into specific operations (constructing a
36746 * directive, extracting a variable from a let- operation, etc), then it's possible for operations
36747 * earlier in the sequence to depend on operations which occur later in the sequence.
36748 *
36749 * `TcbOp` abstracts the different types of operations which are required to convert a template into
36750 * a TCB. This allows for two phases of processing for the template, where 1) a linear sequence of
36751 * `TcbOp`s is generated, and then 2) these operations are executed, not necessarily in linear
36752 * order.
36753 *
36754 * Each `TcbOp` may insert statements into the body of the TCB, and also optionally return a
36755 * `ts.Expression` which can be used to reference the operation's result.
36756 */
36757 class TcbOp {
36758 /**
36759 * Replacement value or operation used while this `TcbOp` is executing (i.e. to resolve circular
36760 * references during its execution).
36761 *
36762 * This is usually a `null!` expression (which asks TS to infer an appropriate type), but another
36763 * `TcbOp` can be returned in cases where additional code generation is necessary to deal with
36764 * circular references.
36765 */
36766 circularFallback() {
36767 return INFER_TYPE_FOR_CIRCULAR_OP_EXPR;
36768 }
36769 }
36770 /**
36771 * A `TcbOp` which creates an expression for a native DOM element (or web component) from a
36772 * `TmplAstElement`.
36773 *
36774 * Executing this operation returns a reference to the element variable.
36775 */
36776 class TcbElementOp extends TcbOp {
36777 constructor(tcb, scope, element) {
36778 super();
36779 this.tcb = tcb;
36780 this.scope = scope;
36781 this.element = element;
36782 }
36783 get optional() {
36784 // The statement generated by this operation is only used for type-inference of the DOM
36785 // element's type and won't report diagnostics by itself, so the operation is marked as optional
36786 // to avoid generating statements for DOM elements that are never referenced.
36787 return true;
36788 }
36789 execute() {
36790 const id = this.tcb.allocateId();
36791 // Add the declaration of the element using document.createElement.
36792 const initializer = tsCreateElement(this.element.name);
36793 addParseSpanInfo(initializer, this.element.startSourceSpan || this.element.sourceSpan);
36794 this.scope.addStatement(tsCreateVariable(id, initializer));
36795 return id;
36796 }
36797 }
36798 /**
36799 * A `TcbOp` which creates an expression for particular let- `TmplAstVariable` on a
36800 * `TmplAstTemplate`'s context.
36801 *
36802 * Executing this operation returns a reference to the variable variable (lol).
36803 */
36804 class TcbVariableOp extends TcbOp {
36805 constructor(tcb, scope, template, variable) {
36806 super();
36807 this.tcb = tcb;
36808 this.scope = scope;
36809 this.template = template;
36810 this.variable = variable;
36811 }
36812 get optional() {
36813 return false;
36814 }
36815 execute() {
36816 // Look for a context variable for the template.
36817 const ctx = this.scope.resolve(this.template);
36818 // Allocate an identifier for the TmplAstVariable, and initialize it to a read of the variable
36819 // on the template context.
36820 const id = this.tcb.allocateId();
36821 const initializer = ts__default["default"].createPropertyAccess(
36822 /* expression */ ctx,
36823 /* name */ this.variable.value || '$implicit');
36824 addParseSpanInfo(id, this.variable.keySpan);
36825 // Declare the variable, and return its identifier.
36826 let variable;
36827 if (this.variable.valueSpan !== undefined) {
36828 addParseSpanInfo(initializer, this.variable.valueSpan);
36829 variable = tsCreateVariable(id, wrapForTypeChecker(initializer));
36830 }
36831 else {
36832 variable = tsCreateVariable(id, initializer);
36833 }
36834 addParseSpanInfo(variable.declarationList.declarations[0], this.variable.sourceSpan);
36835 this.scope.addStatement(variable);
36836 return id;
36837 }
36838 }
36839 /**
36840 * A `TcbOp` which generates a variable for a `TmplAstTemplate`'s context.
36841 *
36842 * Executing this operation returns a reference to the template's context variable.
36843 */
36844 class TcbTemplateContextOp extends TcbOp {
36845 constructor(tcb, scope) {
36846 super();
36847 this.tcb = tcb;
36848 this.scope = scope;
36849 // The declaration of the context variable is only needed when the context is actually referenced.
36850 this.optional = true;
36851 }
36852 execute() {
36853 // Allocate a template ctx variable and declare it with an 'any' type. The type of this variable
36854 // may be narrowed as a result of template guard conditions.
36855 const ctx = this.tcb.allocateId();
36856 const type = ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword);
36857 this.scope.addStatement(tsDeclareVariable(ctx, type));
36858 return ctx;
36859 }
36860 }
36861 /**
36862 * A `TcbOp` which descends into a `TmplAstTemplate`'s children and generates type-checking code for
36863 * them.
36864 *
36865 * This operation wraps the children's type-checking code in an `if` block, which may include one
36866 * or more type guard conditions that narrow types within the template body.
36867 */
36868 class TcbTemplateBodyOp extends TcbOp {
36869 constructor(tcb, scope, template) {
36870 super();
36871 this.tcb = tcb;
36872 this.scope = scope;
36873 this.template = template;
36874 }
36875 get optional() {
36876 return false;
36877 }
36878 execute() {
36879 // An `if` will be constructed, within which the template's children will be type checked. The
36880 // `if` is used for two reasons: it creates a new syntactic scope, isolating variables declared
36881 // in the template's TCB from the outer context, and it allows any directives on the templates
36882 // to perform type narrowing of either expressions or the template's context.
36883 //
36884 // The guard is the `if` block's condition. It's usually set to `true` but directives that exist
36885 // on the template can trigger extra guard expressions that serve to narrow types within the
36886 // `if`. `guard` is calculated by starting with `true` and adding other conditions as needed.
36887 // Collect these into `guards` by processing the directives.
36888 const directiveGuards = [];
36889 const directives = this.tcb.boundTarget.getDirectivesOfNode(this.template);
36890 if (directives !== null) {
36891 for (const dir of directives) {
36892 const dirInstId = this.scope.resolve(this.template, dir);
36893 const dirId = this.tcb.env.reference(dir.ref);
36894 // There are two kinds of guards. Template guards (ngTemplateGuards) allow type narrowing of
36895 // the expression passed to an @Input of the directive. Scan the directive to see if it has
36896 // any template guards, and generate them if needed.
36897 dir.ngTemplateGuards.forEach(guard => {
36898 // For each template guard function on the directive, look for a binding to that input.
36899 const boundInput = this.template.inputs.find(i => i.name === guard.inputName) ||
36900 this.template.templateAttrs.find((i) => i instanceof BoundAttribute && i.name === guard.inputName);
36901 if (boundInput !== undefined) {
36902 // If there is such a binding, generate an expression for it.
36903 const expr = tcbExpression(boundInput.value, this.tcb, this.scope);
36904 // The expression has already been checked in the type constructor invocation, so
36905 // it should be ignored when used within a template guard.
36906 markIgnoreDiagnostics(expr);
36907 if (guard.type === 'binding') {
36908 // Use the binding expression itself as guard.
36909 directiveGuards.push(expr);
36910 }
36911 else {
36912 // Call the guard function on the directive with the directive instance and that
36913 // expression.
36914 const guardInvoke = tsCallMethod(dirId, `ngTemplateGuard_${guard.inputName}`, [
36915 dirInstId,
36916 expr,
36917 ]);
36918 addParseSpanInfo(guardInvoke, boundInput.value.sourceSpan);
36919 directiveGuards.push(guardInvoke);
36920 }
36921 }
36922 });
36923 // The second kind of guard is a template context guard. This guard narrows the template
36924 // rendering context variable `ctx`.
36925 if (dir.hasNgTemplateContextGuard) {
36926 if (this.tcb.env.config.applyTemplateContextGuards) {
36927 const ctx = this.scope.resolve(this.template);
36928 const guardInvoke = tsCallMethod(dirId, 'ngTemplateContextGuard', [dirInstId, ctx]);
36929 addParseSpanInfo(guardInvoke, this.template.sourceSpan);
36930 directiveGuards.push(guardInvoke);
36931 }
36932 else if (this.template.variables.length > 0 &&
36933 this.tcb.env.config.suggestionsForSuboptimalTypeInference) {
36934 // The compiler could have inferred a better type for the variables in this template,
36935 // but was prevented from doing so by the type-checking configuration. Issue a warning
36936 // diagnostic.
36937 this.tcb.oobRecorder.suboptimalTypeInference(this.tcb.id, this.template.variables);
36938 }
36939 }
36940 }
36941 }
36942 // By default the guard is simply `true`.
36943 let guard = null;
36944 // If there are any guards from directives, use them instead.
36945 if (directiveGuards.length > 0) {
36946 // Pop the first value and use it as the initializer to reduce(). This way, a single guard
36947 // will be used on its own, but two or more will be combined into binary AND expressions.
36948 guard = directiveGuards.reduce((expr, dirGuard) => ts__default["default"].createBinary(expr, ts__default["default"].SyntaxKind.AmpersandAmpersandToken, dirGuard), directiveGuards.pop());
36949 }
36950 // Create a new Scope for the template. This constructs the list of operations for the template
36951 // children, as well as tracks bindings within the template.
36952 const tmplScope = Scope.forNodes(this.tcb, this.scope, this.template, guard);
36953 // Render the template's `Scope` into its statements.
36954 const statements = tmplScope.render();
36955 if (statements.length === 0) {
36956 // As an optimization, don't generate the scope's block if it has no statements. This is
36957 // beneficial for templates that contain for example `<span *ngIf="first"></span>`, in which
36958 // case there's no need to render the `NgIf` guard expression. This seems like a minor
36959 // improvement, however it reduces the number of flow-node antecedents that TypeScript needs
36960 // to keep into account for such cases, resulting in an overall reduction of
36961 // type-checking time.
36962 return null;
36963 }
36964 let tmplBlock = ts__default["default"].createBlock(statements);
36965 if (guard !== null) {
36966 // The scope has a guard that needs to be applied, so wrap the template block into an `if`
36967 // statement containing the guard expression.
36968 tmplBlock = ts__default["default"].createIf(/* expression */ guard, /* thenStatement */ tmplBlock);
36969 }
36970 this.scope.addStatement(tmplBlock);
36971 return null;
36972 }
36973 }
36974 /**
36975 * A `TcbOp` which renders a text binding (interpolation) into the TCB.
36976 *
36977 * Executing this operation returns nothing.
36978 */
36979 class TcbTextInterpolationOp extends TcbOp {
36980 constructor(tcb, scope, binding) {
36981 super();
36982 this.tcb = tcb;
36983 this.scope = scope;
36984 this.binding = binding;
36985 }
36986 get optional() {
36987 return false;
36988 }
36989 execute() {
36990 const expr = tcbExpression(this.binding.value, this.tcb, this.scope);
36991 this.scope.addStatement(ts__default["default"].createExpressionStatement(expr));
36992 return null;
36993 }
36994 }
36995 /**
36996 * A `TcbOp` which constructs an instance of a directive. For generic directives, generic
36997 * parameters are set to `any` type.
36998 */
36999 class TcbDirectiveTypeOpBase extends TcbOp {
37000 constructor(tcb, scope, node, dir) {
37001 super();
37002 this.tcb = tcb;
37003 this.scope = scope;
37004 this.node = node;
37005 this.dir = dir;
37006 }
37007 get optional() {
37008 // The statement generated by this operation is only used to declare the directive's type and
37009 // won't report diagnostics by itself, so the operation is marked as optional to avoid
37010 // generating declarations for directives that don't have any inputs/outputs.
37011 return true;
37012 }
37013 execute() {
37014 const dirRef = this.dir.ref;
37015 const rawType = this.tcb.env.referenceType(this.dir.ref);
37016 let type;
37017 if (this.dir.isGeneric === false || dirRef.node.typeParameters === undefined) {
37018 type = rawType;
37019 }
37020 else {
37021 if (!ts__default["default"].isTypeReferenceNode(rawType)) {
37022 throw new Error(`Expected TypeReferenceNode when referencing the type for ${this.dir.ref.debugName}`);
37023 }
37024 const typeArguments = dirRef.node.typeParameters.map(() => ts__default["default"].factory.createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword));
37025 type = ts__default["default"].factory.createTypeReferenceNode(rawType.typeName, typeArguments);
37026 }
37027 const id = this.tcb.allocateId();
37028 addExpressionIdentifier(type, ExpressionIdentifier.DIRECTIVE);
37029 addParseSpanInfo(type, this.node.startSourceSpan || this.node.sourceSpan);
37030 this.scope.addStatement(tsDeclareVariable(id, type));
37031 return id;
37032 }
37033 }
37034 /**
37035 * A `TcbOp` which constructs an instance of a non-generic directive _without_ setting any of its
37036 * inputs. Inputs are later set in the `TcbDirectiveInputsOp`. Type checking was found to be
37037 * faster when done in this way as opposed to `TcbDirectiveCtorOp` which is only necessary when the
37038 * directive is generic.
37039 *
37040 * Executing this operation returns a reference to the directive instance variable with its inferred
37041 * type.
37042 */
37043 class TcbNonGenericDirectiveTypeOp extends TcbDirectiveTypeOpBase {
37044 /**
37045 * Creates a variable declaration for this op's directive of the argument type. Returns the id of
37046 * the newly created variable.
37047 */
37048 execute() {
37049 const dirRef = this.dir.ref;
37050 if (this.dir.isGeneric) {
37051 throw new Error(`Assertion Error: expected ${dirRef.debugName} not to be generic.`);
37052 }
37053 return super.execute();
37054 }
37055 }
37056 /**
37057 * A `TcbOp` which constructs an instance of a generic directive with its generic parameters set
37058 * to `any` type. This op is like `TcbDirectiveTypeOp`, except that generic parameters are set to
37059 * `any` type. This is used for situations where we want to avoid inlining.
37060 *
37061 * Executing this operation returns a reference to the directive instance variable with its generic
37062 * type parameters set to `any`.
37063 */
37064 class TcbGenericDirectiveTypeWithAnyParamsOp extends TcbDirectiveTypeOpBase {
37065 execute() {
37066 const dirRef = this.dir.ref;
37067 if (dirRef.node.typeParameters === undefined) {
37068 throw new Error(`Assertion Error: expected typeParameters when creating a declaration for ${dirRef.debugName}`);
37069 }
37070 return super.execute();
37071 }
37072 }
37073 /**
37074 * A `TcbOp` which creates a variable for a local ref in a template.
37075 * The initializer for the variable is the variable expression for the directive, template, or
37076 * element the ref refers to. When the reference is used in the template, those TCB statements will
37077 * access this variable as well. For example:
37078 * ```
37079 * var _t1 = document.createElement('div');
37080 * var _t2 = _t1;
37081 * _t2.value
37082 * ```
37083 * This operation supports more fluent lookups for the `TemplateTypeChecker` when getting a symbol
37084 * for a reference. In most cases, this isn't essential; that is, the information for the symbol
37085 * could be gathered without this operation using the `BoundTarget`. However, for the case of
37086 * ng-template references, we will need this reference variable to not only provide a location in
37087 * the shim file, but also to narrow the variable to the correct `TemplateRef<T>` type rather than
37088 * `TemplateRef<any>` (this work is still TODO).
37089 *
37090 * Executing this operation returns a reference to the directive instance variable with its inferred
37091 * type.
37092 */
37093 class TcbReferenceOp extends TcbOp {
37094 constructor(tcb, scope, node, host, target) {
37095 super();
37096 this.tcb = tcb;
37097 this.scope = scope;
37098 this.node = node;
37099 this.host = host;
37100 this.target = target;
37101 // The statement generated by this operation is only used to for the Type Checker
37102 // so it can map a reference variable in the template directly to a node in the TCB.
37103 this.optional = true;
37104 }
37105 execute() {
37106 const id = this.tcb.allocateId();
37107 let initializer = this.target instanceof Template || this.target instanceof Element$1 ?
37108 this.scope.resolve(this.target) :
37109 this.scope.resolve(this.host, this.target);
37110 // The reference is either to an element, an <ng-template> node, or to a directive on an
37111 // element or template.
37112 if ((this.target instanceof Element$1 && !this.tcb.env.config.checkTypeOfDomReferences) ||
37113 !this.tcb.env.config.checkTypeOfNonDomReferences) {
37114 // References to DOM nodes are pinned to 'any' when `checkTypeOfDomReferences` is `false`.
37115 // References to `TemplateRef`s and directives are pinned to 'any' when
37116 // `checkTypeOfNonDomReferences` is `false`.
37117 initializer =
37118 ts__default["default"].createAsExpression(initializer, ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword));
37119 }
37120 else if (this.target instanceof Template) {
37121 // Direct references to an <ng-template> node simply require a value of type
37122 // `TemplateRef<any>`. To get this, an expression of the form
37123 // `(_t1 as any as TemplateRef<any>)` is constructed.
37124 initializer =
37125 ts__default["default"].createAsExpression(initializer, ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword));
37126 initializer = ts__default["default"].createAsExpression(initializer, this.tcb.env.referenceExternalType('@angular/core', 'TemplateRef', [DYNAMIC_TYPE]));
37127 initializer = ts__default["default"].createParen(initializer);
37128 }
37129 addParseSpanInfo(initializer, this.node.sourceSpan);
37130 addParseSpanInfo(id, this.node.keySpan);
37131 this.scope.addStatement(tsCreateVariable(id, initializer));
37132 return id;
37133 }
37134 }
37135 /**
37136 * A `TcbOp` which is used when the target of a reference is missing. This operation generates a
37137 * variable of type any for usages of the invalid reference to resolve to. The invalid reference
37138 * itself is recorded out-of-band.
37139 */
37140 class TcbInvalidReferenceOp extends TcbOp {
37141 constructor(tcb, scope) {
37142 super();
37143 this.tcb = tcb;
37144 this.scope = scope;
37145 // The declaration of a missing reference is only needed when the reference is resolved.
37146 this.optional = true;
37147 }
37148 execute() {
37149 const id = this.tcb.allocateId();
37150 this.scope.addStatement(tsCreateVariable(id, NULL_AS_ANY));
37151 return id;
37152 }
37153 }
37154 /**
37155 * A `TcbOp` which constructs an instance of a directive with types inferred from its inputs. The
37156 * inputs themselves are not checked here; checking of inputs is achieved in `TcbDirectiveInputsOp`.
37157 * Any errors reported in this statement are ignored, as the type constructor call is only present
37158 * for type-inference.
37159 *
37160 * When a Directive is generic, it is required that the TCB generates the instance using this method
37161 * in order to infer the type information correctly.
37162 *
37163 * Executing this operation returns a reference to the directive instance variable with its inferred
37164 * type.
37165 */
37166 class TcbDirectiveCtorOp extends TcbOp {
37167 constructor(tcb, scope, node, dir) {
37168 super();
37169 this.tcb = tcb;
37170 this.scope = scope;
37171 this.node = node;
37172 this.dir = dir;
37173 }
37174 get optional() {
37175 // The statement generated by this operation is only used to infer the directive's type and
37176 // won't report diagnostics by itself, so the operation is marked as optional.
37177 return true;
37178 }
37179 execute() {
37180 const id = this.tcb.allocateId();
37181 addExpressionIdentifier(id, ExpressionIdentifier.DIRECTIVE);
37182 addParseSpanInfo(id, this.node.startSourceSpan || this.node.sourceSpan);
37183 const genericInputs = new Map();
37184 const inputs = getBoundInputs(this.dir, this.node, this.tcb);
37185 for (const input of inputs) {
37186 // Skip text attributes if configured to do so.
37187 if (!this.tcb.env.config.checkTypeOfAttributes &&
37188 input.attribute instanceof TextAttribute) {
37189 continue;
37190 }
37191 for (const fieldName of input.fieldNames) {
37192 // Skip the field if an attribute has already been bound to it; we can't have a duplicate
37193 // key in the type constructor call.
37194 if (genericInputs.has(fieldName)) {
37195 continue;
37196 }
37197 const expression = translateInput(input.attribute, this.tcb, this.scope);
37198 genericInputs.set(fieldName, {
37199 type: 'binding',
37200 field: fieldName,
37201 expression,
37202 sourceSpan: input.attribute.sourceSpan
37203 });
37204 }
37205 }
37206 // Add unset directive inputs for each of the remaining unset fields.
37207 for (const [fieldName] of this.dir.inputs) {
37208 if (!genericInputs.has(fieldName)) {
37209 genericInputs.set(fieldName, { type: 'unset', field: fieldName });
37210 }
37211 }
37212 // Call the type constructor of the directive to infer a type, and assign the directive
37213 // instance.
37214 const typeCtor = tcbCallTypeCtor(this.dir, this.tcb, Array.from(genericInputs.values()));
37215 markIgnoreDiagnostics(typeCtor);
37216 this.scope.addStatement(tsCreateVariable(id, typeCtor));
37217 return id;
37218 }
37219 circularFallback() {
37220 return new TcbDirectiveCtorCircularFallbackOp(this.tcb, this.scope, this.node, this.dir);
37221 }
37222 }
37223 /**
37224 * A `TcbOp` which generates code to check input bindings on an element that correspond with the
37225 * members of a directive.
37226 *
37227 * Executing this operation returns nothing.
37228 */
37229 class TcbDirectiveInputsOp extends TcbOp {
37230 constructor(tcb, scope, node, dir) {
37231 super();
37232 this.tcb = tcb;
37233 this.scope = scope;
37234 this.node = node;
37235 this.dir = dir;
37236 }
37237 get optional() {
37238 return false;
37239 }
37240 execute() {
37241 let dirId = null;
37242 // TODO(joost): report duplicate properties
37243 const inputs = getBoundInputs(this.dir, this.node, this.tcb);
37244 for (const input of inputs) {
37245 // For bound inputs, the property is assigned the binding expression.
37246 const expr = widenBinding(translateInput(input.attribute, this.tcb, this.scope), this.tcb);
37247 let assignment = wrapForDiagnostics(expr);
37248 for (const fieldName of input.fieldNames) {
37249 let target;
37250 if (this.dir.coercedInputFields.has(fieldName)) {
37251 // The input has a coercion declaration which should be used instead of assigning the
37252 // expression into the input field directly. To achieve this, a variable is declared
37253 // with a type of `typeof Directive.ngAcceptInputType_fieldName` which is then used as
37254 // target of the assignment.
37255 const dirTypeRef = this.tcb.env.referenceType(this.dir.ref);
37256 if (!ts__default["default"].isTypeReferenceNode(dirTypeRef)) {
37257 throw new Error(`Expected TypeReferenceNode from reference to ${this.dir.ref.debugName}`);
37258 }
37259 const id = this.tcb.allocateId();
37260 const type = tsCreateTypeQueryForCoercedInput(dirTypeRef.typeName, fieldName);
37261 this.scope.addStatement(tsDeclareVariable(id, type));
37262 target = id;
37263 }
37264 else if (this.dir.undeclaredInputFields.has(fieldName)) {
37265 // If no coercion declaration is present nor is the field declared (i.e. the input is
37266 // declared in a `@Directive` or `@Component` decorator's `inputs` property) there is no
37267 // assignment target available, so this field is skipped.
37268 continue;
37269 }
37270 else if (!this.tcb.env.config.honorAccessModifiersForInputBindings &&
37271 this.dir.restrictedInputFields.has(fieldName)) {
37272 // If strict checking of access modifiers is disabled and the field is restricted
37273 // (i.e. private/protected/readonly), generate an assignment into a temporary variable
37274 // that has the type of the field. This achieves type-checking but circumvents the access
37275 // modifiers.
37276 if (dirId === null) {
37277 dirId = this.scope.resolve(this.node, this.dir);
37278 }
37279 const id = this.tcb.allocateId();
37280 const dirTypeRef = this.tcb.env.referenceType(this.dir.ref);
37281 if (!ts__default["default"].isTypeReferenceNode(dirTypeRef)) {
37282 throw new Error(`Expected TypeReferenceNode from reference to ${this.dir.ref.debugName}`);
37283 }
37284 const type = ts__default["default"].createIndexedAccessTypeNode(ts__default["default"].createTypeQueryNode(dirId), ts__default["default"].createLiteralTypeNode(ts__default["default"].createStringLiteral(fieldName)));
37285 const temp = tsDeclareVariable(id, type);
37286 this.scope.addStatement(temp);
37287 target = id;
37288 }
37289 else {
37290 if (dirId === null) {
37291 dirId = this.scope.resolve(this.node, this.dir);
37292 }
37293 // To get errors assign directly to the fields on the instance, using property access
37294 // when possible. String literal fields may not be valid JS identifiers so we use
37295 // literal element access instead for those cases.
37296 target = this.dir.stringLiteralInputFields.has(fieldName) ?
37297 ts__default["default"].createElementAccess(dirId, ts__default["default"].createStringLiteral(fieldName)) :
37298 ts__default["default"].createPropertyAccess(dirId, ts__default["default"].createIdentifier(fieldName));
37299 }
37300 if (input.attribute.keySpan !== undefined) {
37301 addParseSpanInfo(target, input.attribute.keySpan);
37302 }
37303 // Finally the assignment is extended by assigning it into the target expression.
37304 assignment = ts__default["default"].createBinary(target, ts__default["default"].SyntaxKind.EqualsToken, assignment);
37305 }
37306 addParseSpanInfo(assignment, input.attribute.sourceSpan);
37307 // Ignore diagnostics for text attributes if configured to do so.
37308 if (!this.tcb.env.config.checkTypeOfAttributes &&
37309 input.attribute instanceof TextAttribute) {
37310 markIgnoreDiagnostics(assignment);
37311 }
37312 this.scope.addStatement(ts__default["default"].createExpressionStatement(assignment));
37313 }
37314 return null;
37315 }
37316 }
37317 /**
37318 * A `TcbOp` which is used to generate a fallback expression if the inference of a directive type
37319 * via `TcbDirectiveCtorOp` requires a reference to its own type. This can happen using a template
37320 * reference:
37321 *
37322 * ```html
37323 * <some-cmp #ref [prop]="ref.foo"></some-cmp>
37324 * ```
37325 *
37326 * In this case, `TcbDirectiveCtorCircularFallbackOp` will add a second inference of the directive
37327 * type to the type-check block, this time calling the directive's type constructor without any
37328 * input expressions. This infers the widest possible supertype for the directive, which is used to
37329 * resolve any recursive references required to infer the real type.
37330 */
37331 class TcbDirectiveCtorCircularFallbackOp extends TcbOp {
37332 constructor(tcb, scope, node, dir) {
37333 super();
37334 this.tcb = tcb;
37335 this.scope = scope;
37336 this.node = node;
37337 this.dir = dir;
37338 }
37339 get optional() {
37340 return false;
37341 }
37342 execute() {
37343 const id = this.tcb.allocateId();
37344 const typeCtor = this.tcb.env.typeCtorFor(this.dir);
37345 const circularPlaceholder = ts__default["default"].createCall(typeCtor, /* typeArguments */ undefined, [ts__default["default"].createNonNullExpression(ts__default["default"].createNull())]);
37346 this.scope.addStatement(tsCreateVariable(id, circularPlaceholder));
37347 return id;
37348 }
37349 }
37350 /**
37351 * A `TcbOp` which feeds elements and unclaimed properties to the `DomSchemaChecker`.
37352 *
37353 * The DOM schema is not checked via TCB code generation. Instead, the `DomSchemaChecker` ingests
37354 * elements and property bindings and accumulates synthetic `ts.Diagnostic`s out-of-band. These are
37355 * later merged with the diagnostics generated from the TCB.
37356 *
37357 * For convenience, the TCB iteration of the template is used to drive the `DomSchemaChecker` via
37358 * the `TcbDomSchemaCheckerOp`.
37359 */
37360 class TcbDomSchemaCheckerOp extends TcbOp {
37361 constructor(tcb, element, checkElement, claimedInputs) {
37362 super();
37363 this.tcb = tcb;
37364 this.element = element;
37365 this.checkElement = checkElement;
37366 this.claimedInputs = claimedInputs;
37367 }
37368 get optional() {
37369 return false;
37370 }
37371 execute() {
37372 if (this.checkElement) {
37373 this.tcb.domSchemaChecker.checkElement(this.tcb.id, this.element, this.tcb.schemas);
37374 }
37375 // TODO(alxhub): this could be more efficient.
37376 for (const binding of this.element.inputs) {
37377 if (binding.type === 0 /* Property */ && this.claimedInputs.has(binding.name)) {
37378 // Skip this binding as it was claimed by a directive.
37379 continue;
37380 }
37381 if (binding.type === 0 /* Property */) {
37382 if (binding.name !== 'style' && binding.name !== 'class') {
37383 // A direct binding to a property.
37384 const propertyName = ATTR_TO_PROP[binding.name] || binding.name;
37385 this.tcb.domSchemaChecker.checkProperty(this.tcb.id, this.element, propertyName, binding.sourceSpan, this.tcb.schemas);
37386 }
37387 }
37388 }
37389 return null;
37390 }
37391 }
37392 /**
37393 * Mapping between attributes names that don't correspond to their element property names.
37394 * Note: this mapping has to be kept in sync with the equally named mapping in the runtime.
37395 */
37396 const ATTR_TO_PROP = {
37397 'class': 'className',
37398 'for': 'htmlFor',
37399 'formaction': 'formAction',
37400 'innerHtml': 'innerHTML',
37401 'readonly': 'readOnly',
37402 'tabindex': 'tabIndex',
37403 };
37404 /**
37405 * A `TcbOp` which generates code to check "unclaimed inputs" - bindings on an element which were
37406 * not attributed to any directive or component, and are instead processed against the HTML element
37407 * itself.
37408 *
37409 * Currently, only the expressions of these bindings are checked. The targets of the bindings are
37410 * checked against the DOM schema via a `TcbDomSchemaCheckerOp`.
37411 *
37412 * Executing this operation returns nothing.
37413 */
37414 class TcbUnclaimedInputsOp extends TcbOp {
37415 constructor(tcb, scope, element, claimedInputs) {
37416 super();
37417 this.tcb = tcb;
37418 this.scope = scope;
37419 this.element = element;
37420 this.claimedInputs = claimedInputs;
37421 }
37422 get optional() {
37423 return false;
37424 }
37425 execute() {
37426 // `this.inputs` contains only those bindings not matched by any directive. These bindings go to
37427 // the element itself.
37428 let elId = null;
37429 // TODO(alxhub): this could be more efficient.
37430 for (const binding of this.element.inputs) {
37431 if (binding.type === 0 /* Property */ && this.claimedInputs.has(binding.name)) {
37432 // Skip this binding as it was claimed by a directive.
37433 continue;
37434 }
37435 const expr = widenBinding(tcbExpression(binding.value, this.tcb, this.scope), this.tcb);
37436 if (this.tcb.env.config.checkTypeOfDomBindings && binding.type === 0 /* Property */) {
37437 if (binding.name !== 'style' && binding.name !== 'class') {
37438 if (elId === null) {
37439 elId = this.scope.resolve(this.element);
37440 }
37441 // A direct binding to a property.
37442 const propertyName = ATTR_TO_PROP[binding.name] || binding.name;
37443 const prop = ts__default["default"].createElementAccess(elId, ts__default["default"].createStringLiteral(propertyName));
37444 const stmt = ts__default["default"].createBinary(prop, ts__default["default"].SyntaxKind.EqualsToken, wrapForDiagnostics(expr));
37445 addParseSpanInfo(stmt, binding.sourceSpan);
37446 this.scope.addStatement(ts__default["default"].createExpressionStatement(stmt));
37447 }
37448 else {
37449 this.scope.addStatement(ts__default["default"].createExpressionStatement(expr));
37450 }
37451 }
37452 else {
37453 // A binding to an animation, attribute, class or style. For now, only validate the right-
37454 // hand side of the expression.
37455 // TODO: properly check class and style bindings.
37456 this.scope.addStatement(ts__default["default"].createExpressionStatement(expr));
37457 }
37458 }
37459 return null;
37460 }
37461 }
37462 /**
37463 * A `TcbOp` which generates code to check event bindings on an element that correspond with the
37464 * outputs of a directive.
37465 *
37466 * Executing this operation returns nothing.
37467 */
37468 class TcbDirectiveOutputsOp extends TcbOp {
37469 constructor(tcb, scope, node, dir) {
37470 super();
37471 this.tcb = tcb;
37472 this.scope = scope;
37473 this.node = node;
37474 this.dir = dir;
37475 }
37476 get optional() {
37477 return false;
37478 }
37479 execute() {
37480 let dirId = null;
37481 const outputs = this.dir.outputs;
37482 for (const output of this.node.outputs) {
37483 if (output.type !== 0 /* Regular */ || !outputs.hasBindingPropertyName(output.name)) {
37484 continue;
37485 }
37486 if (this.tcb.env.config.checkTypeOfOutputEvents && output.name.endsWith('Change')) {
37487 const inputName = output.name.slice(0, -6);
37488 isSplitTwoWayBinding(inputName, output, this.node.inputs, this.tcb);
37489 }
37490 // TODO(alxhub): consider supporting multiple fields with the same property name for outputs.
37491 const field = outputs.getByBindingPropertyName(output.name)[0].classPropertyName;
37492 if (dirId === null) {
37493 dirId = this.scope.resolve(this.node, this.dir);
37494 }
37495 const outputField = ts__default["default"].createElementAccess(dirId, ts__default["default"].createStringLiteral(field));
37496 addParseSpanInfo(outputField, output.keySpan);
37497 if (this.tcb.env.config.checkTypeOfOutputEvents) {
37498 // For strict checking of directive events, generate a call to the `subscribe` method
37499 // on the directive's output field to let type information flow into the handler function's
37500 // `$event` parameter.
37501 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 0 /* Infer */);
37502 const subscribeFn = ts__default["default"].createPropertyAccess(outputField, 'subscribe');
37503 const call = ts__default["default"].createCall(subscribeFn, /* typeArguments */ undefined, [handler]);
37504 addParseSpanInfo(call, output.sourceSpan);
37505 this.scope.addStatement(ts__default["default"].createExpressionStatement(call));
37506 }
37507 else {
37508 // If strict checking of directive events is disabled:
37509 //
37510 // * We still generate the access to the output field as a statement in the TCB so consumers
37511 // of the `TemplateTypeChecker` can still find the node for the class member for the
37512 // output.
37513 // * Emit a handler function where the `$event` parameter has an explicit `any` type.
37514 this.scope.addStatement(ts__default["default"].createExpressionStatement(outputField));
37515 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 1 /* Any */);
37516 this.scope.addStatement(ts__default["default"].createExpressionStatement(handler));
37517 }
37518 ExpressionSemanticVisitor.visit(output.handler, this.tcb.id, this.tcb.boundTarget, this.tcb.oobRecorder);
37519 }
37520 return null;
37521 }
37522 }
37523 /**
37524 * A `TcbOp` which generates code to check "unclaimed outputs" - event bindings on an element which
37525 * were not attributed to any directive or component, and are instead processed against the HTML
37526 * element itself.
37527 *
37528 * Executing this operation returns nothing.
37529 */
37530 class TcbUnclaimedOutputsOp extends TcbOp {
37531 constructor(tcb, scope, element, claimedOutputs) {
37532 super();
37533 this.tcb = tcb;
37534 this.scope = scope;
37535 this.element = element;
37536 this.claimedOutputs = claimedOutputs;
37537 }
37538 get optional() {
37539 return false;
37540 }
37541 execute() {
37542 let elId = null;
37543 // TODO(alxhub): this could be more efficient.
37544 for (const output of this.element.outputs) {
37545 if (this.claimedOutputs.has(output.name)) {
37546 // Skip this event handler as it was claimed by a directive.
37547 continue;
37548 }
37549 if (this.tcb.env.config.checkTypeOfOutputEvents && output.name.endsWith('Change')) {
37550 const inputName = output.name.slice(0, -6);
37551 if (isSplitTwoWayBinding(inputName, output, this.element.inputs, this.tcb)) {
37552 // Skip this event handler as the error was already handled.
37553 continue;
37554 }
37555 }
37556 if (output.type === 1 /* Animation */) {
37557 // Animation output bindings always have an `$event` parameter of type `AnimationEvent`.
37558 const eventType = this.tcb.env.config.checkTypeOfAnimationEvents ?
37559 this.tcb.env.referenceExternalType('@angular/animations', 'AnimationEvent') :
37560 1 /* Any */;
37561 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, eventType);
37562 this.scope.addStatement(ts__default["default"].createExpressionStatement(handler));
37563 }
37564 else if (this.tcb.env.config.checkTypeOfDomEvents) {
37565 // If strict checking of DOM events is enabled, generate a call to `addEventListener` on
37566 // the element instance so that TypeScript's type inference for
37567 // `HTMLElement.addEventListener` using `HTMLElementEventMap` to infer an accurate type for
37568 // `$event` depending on the event name. For unknown event names, TypeScript resorts to the
37569 // base `Event` type.
37570 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 0 /* Infer */);
37571 if (elId === null) {
37572 elId = this.scope.resolve(this.element);
37573 }
37574 const propertyAccess = ts__default["default"].createPropertyAccess(elId, 'addEventListener');
37575 addParseSpanInfo(propertyAccess, output.keySpan);
37576 const call = ts__default["default"].createCall(
37577 /* expression */ propertyAccess,
37578 /* typeArguments */ undefined,
37579 /* arguments */ [ts__default["default"].createStringLiteral(output.name), handler]);
37580 addParseSpanInfo(call, output.sourceSpan);
37581 this.scope.addStatement(ts__default["default"].createExpressionStatement(call));
37582 }
37583 else {
37584 // If strict checking of DOM inputs is disabled, emit a handler function where the `$event`
37585 // parameter has an explicit `any` type.
37586 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 1 /* Any */);
37587 this.scope.addStatement(ts__default["default"].createExpressionStatement(handler));
37588 }
37589 ExpressionSemanticVisitor.visit(output.handler, this.tcb.id, this.tcb.boundTarget, this.tcb.oobRecorder);
37590 }
37591 return null;
37592 }
37593 }
37594 /**
37595 * A `TcbOp` which generates a completion point for the component context.
37596 *
37597 * This completion point looks like `ctx. ;` in the TCB output, and does not produce diagnostics.
37598 * TypeScript autocompletion APIs can be used at this completion point (after the '.') to produce
37599 * autocompletion results of properties and methods from the template's component context.
37600 */
37601 class TcbComponentContextCompletionOp extends TcbOp {
37602 constructor(scope) {
37603 super();
37604 this.scope = scope;
37605 this.optional = false;
37606 }
37607 execute() {
37608 const ctx = ts__default["default"].createIdentifier('ctx');
37609 const ctxDot = ts__default["default"].createPropertyAccess(ctx, '');
37610 markIgnoreDiagnostics(ctxDot);
37611 addExpressionIdentifier(ctxDot, ExpressionIdentifier.COMPONENT_COMPLETION);
37612 this.scope.addStatement(ts__default["default"].createExpressionStatement(ctxDot));
37613 return null;
37614 }
37615 }
37616 /**
37617 * Value used to break a circular reference between `TcbOp`s.
37618 *
37619 * This value is returned whenever `TcbOp`s have a circular dependency. The expression is a non-null
37620 * assertion of the null value (in TypeScript, the expression `null!`). This construction will infer
37621 * the least narrow type for whatever it's assigned to.
37622 */
37623 const INFER_TYPE_FOR_CIRCULAR_OP_EXPR = ts__default["default"].createNonNullExpression(ts__default["default"].createNull());
37624 /**
37625 * Overall generation context for the type check block.
37626 *
37627 * `Context` handles operations during code generation which are global with respect to the whole
37628 * block. It's responsible for variable name allocation and management of any imports needed. It
37629 * also contains the template metadata itself.
37630 */
37631 class Context {
37632 constructor(env, domSchemaChecker, oobRecorder, id, boundTarget, pipes, schemas) {
37633 this.env = env;
37634 this.domSchemaChecker = domSchemaChecker;
37635 this.oobRecorder = oobRecorder;
37636 this.id = id;
37637 this.boundTarget = boundTarget;
37638 this.pipes = pipes;
37639 this.schemas = schemas;
37640 this.nextId = 1;
37641 }
37642 /**
37643 * Allocate a new variable name for use within the `Context`.
37644 *
37645 * Currently this uses a monotonically increasing counter, but in the future the variable name
37646 * might change depending on the type of data being stored.
37647 */
37648 allocateId() {
37649 return ts__default["default"].createIdentifier(`_t${this.nextId++}`);
37650 }
37651 getPipeByName(name) {
37652 if (!this.pipes.has(name)) {
37653 return null;
37654 }
37655 return this.pipes.get(name);
37656 }
37657 }
37658 /**
37659 * Local scope within the type check block for a particular template.
37660 *
37661 * The top-level template and each nested `<ng-template>` have their own `Scope`, which exist in a
37662 * hierarchy. The structure of this hierarchy mirrors the syntactic scopes in the generated type
37663 * check block, where each nested template is encased in an `if` structure.
37664 *
37665 * As a template's `TcbOp`s are executed in a given `Scope`, statements are added via
37666 * `addStatement()`. When this processing is complete, the `Scope` can be turned into a `ts.Block`
37667 * via `renderToBlock()`.
37668 *
37669 * If a `TcbOp` requires the output of another, it can call `resolve()`.
37670 */
37671 class Scope {
37672 constructor(tcb, parent = null, guard = null) {
37673 this.tcb = tcb;
37674 this.parent = parent;
37675 this.guard = guard;
37676 /**
37677 * A queue of operations which need to be performed to generate the TCB code for this scope.
37678 *
37679 * This array can contain either a `TcbOp` which has yet to be executed, or a `ts.Expression|null`
37680 * representing the memoized result of executing the operation. As operations are executed, their
37681 * results are written into the `opQueue`, overwriting the original operation.
37682 *
37683 * If an operation is in the process of being executed, it is temporarily overwritten here with
37684 * `INFER_TYPE_FOR_CIRCULAR_OP_EXPR`. This way, if a cycle is encountered where an operation
37685 * depends transitively on its own result, the inner operation will infer the least narrow type
37686 * that fits instead. This has the same semantics as TypeScript itself when types are referenced
37687 * circularly.
37688 */
37689 this.opQueue = [];
37690 /**
37691 * A map of `TmplAstElement`s to the index of their `TcbElementOp` in the `opQueue`
37692 */
37693 this.elementOpMap = new Map();
37694 /**
37695 * A map of maps which tracks the index of `TcbDirectiveCtorOp`s in the `opQueue` for each
37696 * directive on a `TmplAstElement` or `TmplAstTemplate` node.
37697 */
37698 this.directiveOpMap = new Map();
37699 /**
37700 * A map of `TmplAstReference`s to the index of their `TcbReferenceOp` in the `opQueue`
37701 */
37702 this.referenceOpMap = new Map();
37703 /**
37704 * Map of immediately nested <ng-template>s (within this `Scope`) represented by `TmplAstTemplate`
37705 * nodes to the index of their `TcbTemplateContextOp`s in the `opQueue`.
37706 */
37707 this.templateCtxOpMap = new Map();
37708 /**
37709 * Map of variables declared on the template that created this `Scope` (represented by
37710 * `TmplAstVariable` nodes) to the index of their `TcbVariableOp`s in the `opQueue`.
37711 */
37712 this.varMap = new Map();
37713 /**
37714 * Statements for this template.
37715 *
37716 * Executing the `TcbOp`s in the `opQueue` populates this array.
37717 */
37718 this.statements = [];
37719 }
37720 /**
37721 * Constructs a `Scope` given either a `TmplAstTemplate` or a list of `TmplAstNode`s.
37722 *
37723 * @param tcb the overall context of TCB generation.
37724 * @param parent the `Scope` of the parent template (if any) or `null` if this is the root
37725 * `Scope`.
37726 * @param templateOrNodes either a `TmplAstTemplate` representing the template for which to
37727 * calculate the `Scope`, or a list of nodes if no outer template object is available.
37728 * @param guard an expression that is applied to this scope for type narrowing purposes.
37729 */
37730 static forNodes(tcb, parent, templateOrNodes, guard) {
37731 const scope = new Scope(tcb, parent, guard);
37732 if (parent === null && tcb.env.config.enableTemplateTypeChecker) {
37733 // Add an autocompletion point for the component context.
37734 scope.opQueue.push(new TcbComponentContextCompletionOp(scope));
37735 }
37736 let children;
37737 // If given an actual `TmplAstTemplate` instance, then process any additional information it
37738 // has.
37739 if (templateOrNodes instanceof Template) {
37740 // The template's variable declarations need to be added as `TcbVariableOp`s.
37741 const varMap = new Map();
37742 for (const v of templateOrNodes.variables) {
37743 // Validate that variables on the `TmplAstTemplate` are only declared once.
37744 if (!varMap.has(v.name)) {
37745 varMap.set(v.name, v);
37746 }
37747 else {
37748 const firstDecl = varMap.get(v.name);
37749 tcb.oobRecorder.duplicateTemplateVar(tcb.id, v, firstDecl);
37750 }
37751 const opIndex = scope.opQueue.push(new TcbVariableOp(tcb, scope, templateOrNodes, v)) - 1;
37752 scope.varMap.set(v, opIndex);
37753 }
37754 children = templateOrNodes.children;
37755 }
37756 else {
37757 children = templateOrNodes;
37758 }
37759 for (const node of children) {
37760 scope.appendNode(node);
37761 }
37762 return scope;
37763 }
37764 /**
37765 * Look up a `ts.Expression` representing the value of some operation in the current `Scope`,
37766 * including any parent scope(s). This method always returns a mutable clone of the
37767 * `ts.Expression` with the comments cleared.
37768 *
37769 * @param node a `TmplAstNode` of the operation in question. The lookup performed will depend on
37770 * the type of this node:
37771 *
37772 * Assuming `directive` is not present, then `resolve` will return:
37773 *
37774 * * `TmplAstElement` - retrieve the expression for the element DOM node
37775 * * `TmplAstTemplate` - retrieve the template context variable
37776 * * `TmplAstVariable` - retrieve a template let- variable
37777 * * `TmplAstReference` - retrieve variable created for the local ref
37778 *
37779 * @param directive if present, a directive type on a `TmplAstElement` or `TmplAstTemplate` to
37780 * look up instead of the default for an element or template node.
37781 */
37782 resolve(node, directive) {
37783 // Attempt to resolve the operation locally.
37784 const res = this.resolveLocal(node, directive);
37785 if (res !== null) {
37786 // We want to get a clone of the resolved expression and clear the trailing comments
37787 // so they don't continue to appear in every place the expression is used.
37788 // As an example, this would otherwise produce:
37789 // var _t1 /**T:DIR*/ /*1,2*/ = _ctor1();
37790 // _t1 /**T:DIR*/ /*1,2*/.input = 'value';
37791 //
37792 // In addition, returning a clone prevents the consumer of `Scope#resolve` from
37793 // attaching comments at the declaration site.
37794 const clone = ts__default["default"].getMutableClone(res);
37795 ts__default["default"].setSyntheticTrailingComments(clone, []);
37796 return clone;
37797 }
37798 else if (this.parent !== null) {
37799 // Check with the parent.
37800 return this.parent.resolve(node, directive);
37801 }
37802 else {
37803 throw new Error(`Could not resolve ${node} / ${directive}`);
37804 }
37805 }
37806 /**
37807 * Add a statement to this scope.
37808 */
37809 addStatement(stmt) {
37810 this.statements.push(stmt);
37811 }
37812 /**
37813 * Get the statements.
37814 */
37815 render() {
37816 for (let i = 0; i < this.opQueue.length; i++) {
37817 // Optional statements cannot be skipped when we are generating the TCB for use
37818 // by the TemplateTypeChecker.
37819 const skipOptional = !this.tcb.env.config.enableTemplateTypeChecker;
37820 this.executeOp(i, skipOptional);
37821 }
37822 return this.statements;
37823 }
37824 /**
37825 * Returns an expression of all template guards that apply to this scope, including those of
37826 * parent scopes. If no guards have been applied, null is returned.
37827 */
37828 guards() {
37829 let parentGuards = null;
37830 if (this.parent !== null) {
37831 // Start with the guards from the parent scope, if present.
37832 parentGuards = this.parent.guards();
37833 }
37834 if (this.guard === null) {
37835 // This scope does not have a guard, so return the parent's guards as is.
37836 return parentGuards;
37837 }
37838 else if (parentGuards === null) {
37839 // There's no guards from the parent scope, so this scope's guard represents all available
37840 // guards.
37841 return this.guard;
37842 }
37843 else {
37844 // Both the parent scope and this scope provide a guard, so create a combination of the two.
37845 // It is important that the parent guard is used as left operand, given that it may provide
37846 // narrowing that is required for this scope's guard to be valid.
37847 return ts__default["default"].createBinary(parentGuards, ts__default["default"].SyntaxKind.AmpersandAmpersandToken, this.guard);
37848 }
37849 }
37850 resolveLocal(ref, directive) {
37851 if (ref instanceof Reference$1 && this.referenceOpMap.has(ref)) {
37852 return this.resolveOp(this.referenceOpMap.get(ref));
37853 }
37854 else if (ref instanceof Variable && this.varMap.has(ref)) {
37855 // Resolving a context variable for this template.
37856 // Execute the `TcbVariableOp` associated with the `TmplAstVariable`.
37857 return this.resolveOp(this.varMap.get(ref));
37858 }
37859 else if (ref instanceof Template && directive === undefined &&
37860 this.templateCtxOpMap.has(ref)) {
37861 // Resolving the context of the given sub-template.
37862 // Execute the `TcbTemplateContextOp` for the template.
37863 return this.resolveOp(this.templateCtxOpMap.get(ref));
37864 }
37865 else if ((ref instanceof Element$1 || ref instanceof Template) &&
37866 directive !== undefined && this.directiveOpMap.has(ref)) {
37867 // Resolving a directive on an element or sub-template.
37868 const dirMap = this.directiveOpMap.get(ref);
37869 if (dirMap.has(directive)) {
37870 return this.resolveOp(dirMap.get(directive));
37871 }
37872 else {
37873 return null;
37874 }
37875 }
37876 else if (ref instanceof Element$1 && this.elementOpMap.has(ref)) {
37877 // Resolving the DOM node of an element in this template.
37878 return this.resolveOp(this.elementOpMap.get(ref));
37879 }
37880 else {
37881 return null;
37882 }
37883 }
37884 /**
37885 * Like `executeOp`, but assert that the operation actually returned `ts.Expression`.
37886 */
37887 resolveOp(opIndex) {
37888 const res = this.executeOp(opIndex, /* skipOptional */ false);
37889 if (res === null) {
37890 throw new Error(`Error resolving operation, got null`);
37891 }
37892 return res;
37893 }
37894 /**
37895 * Execute a particular `TcbOp` in the `opQueue`.
37896 *
37897 * This method replaces the operation in the `opQueue` with the result of execution (once done)
37898 * and also protects against a circular dependency from the operation to itself by temporarily
37899 * setting the operation's result to a special expression.
37900 */
37901 executeOp(opIndex, skipOptional) {
37902 const op = this.opQueue[opIndex];
37903 if (!(op instanceof TcbOp)) {
37904 return op;
37905 }
37906 if (skipOptional && op.optional) {
37907 return null;
37908 }
37909 // Set the result of the operation in the queue to its circular fallback. If executing this
37910 // operation results in a circular dependency, this will prevent an infinite loop and allow for
37911 // the resolution of such cycles.
37912 this.opQueue[opIndex] = op.circularFallback();
37913 const res = op.execute();
37914 // Once the operation has finished executing, it's safe to cache the real result.
37915 this.opQueue[opIndex] = res;
37916 return res;
37917 }
37918 appendNode(node) {
37919 if (node instanceof Element$1) {
37920 const opIndex = this.opQueue.push(new TcbElementOp(this.tcb, this, node)) - 1;
37921 this.elementOpMap.set(node, opIndex);
37922 this.appendDirectivesAndInputsOfNode(node);
37923 this.appendOutputsOfNode(node);
37924 for (const child of node.children) {
37925 this.appendNode(child);
37926 }
37927 this.checkAndAppendReferencesOfNode(node);
37928 }
37929 else if (node instanceof Template) {
37930 // Template children are rendered in a child scope.
37931 this.appendDirectivesAndInputsOfNode(node);
37932 this.appendOutputsOfNode(node);
37933 const ctxIndex = this.opQueue.push(new TcbTemplateContextOp(this.tcb, this)) - 1;
37934 this.templateCtxOpMap.set(node, ctxIndex);
37935 if (this.tcb.env.config.checkTemplateBodies) {
37936 this.opQueue.push(new TcbTemplateBodyOp(this.tcb, this, node));
37937 }
37938 else if (this.tcb.env.config.alwaysCheckSchemaInTemplateBodies) {
37939 this.appendDeepSchemaChecks(node.children);
37940 }
37941 this.checkAndAppendReferencesOfNode(node);
37942 }
37943 else if (node instanceof BoundText) {
37944 this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, node));
37945 }
37946 else if (node instanceof Icu$1) {
37947 this.appendIcuExpressions(node);
37948 }
37949 }
37950 checkAndAppendReferencesOfNode(node) {
37951 for (const ref of node.references) {
37952 const target = this.tcb.boundTarget.getReferenceTarget(ref);
37953 let ctxIndex;
37954 if (target === null) {
37955 // The reference is invalid if it doesn't have a target, so report it as an error.
37956 this.tcb.oobRecorder.missingReferenceTarget(this.tcb.id, ref);
37957 // Any usages of the invalid reference will be resolved to a variable of type any.
37958 ctxIndex = this.opQueue.push(new TcbInvalidReferenceOp(this.tcb, this)) - 1;
37959 }
37960 else if (target instanceof Template || target instanceof Element$1) {
37961 ctxIndex = this.opQueue.push(new TcbReferenceOp(this.tcb, this, ref, node, target)) - 1;
37962 }
37963 else {
37964 ctxIndex =
37965 this.opQueue.push(new TcbReferenceOp(this.tcb, this, ref, node, target.directive)) - 1;
37966 }
37967 this.referenceOpMap.set(ref, ctxIndex);
37968 }
37969 }
37970 appendDirectivesAndInputsOfNode(node) {
37971 // Collect all the inputs on the element.
37972 const claimedInputs = new Set();
37973 const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
37974 if (directives === null || directives.length === 0) {
37975 // If there are no directives, then all inputs are unclaimed inputs, so queue an operation
37976 // to add them if needed.
37977 if (node instanceof Element$1) {
37978 this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node, claimedInputs));
37979 this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, /* checkElement */ true, claimedInputs));
37980 }
37981 return;
37982 }
37983 const dirMap = new Map();
37984 for (const dir of directives) {
37985 let directiveOp;
37986 const host = this.tcb.env.reflector;
37987 const dirRef = dir.ref;
37988 if (!dir.isGeneric) {
37989 // The most common case is that when a directive is not generic, we use the normal
37990 // `TcbNonDirectiveTypeOp`.
37991 directiveOp = new TcbNonGenericDirectiveTypeOp(this.tcb, this, node, dir);
37992 }
37993 else if (!requiresInlineTypeCtor(dirRef.node, host) ||
37994 this.tcb.env.config.useInlineTypeConstructors) {
37995 // For generic directives, we use a type constructor to infer types. If a directive requires
37996 // an inline type constructor, then inlining must be available to use the
37997 // `TcbDirectiveCtorOp`. If not we, we fallback to using `any` – see below.
37998 directiveOp = new TcbDirectiveCtorOp(this.tcb, this, node, dir);
37999 }
38000 else {
38001 // If inlining is not available, then we give up on infering the generic params, and use
38002 // `any` type for the directive's generic parameters.
38003 directiveOp = new TcbGenericDirectiveTypeWithAnyParamsOp(this.tcb, this, node, dir);
38004 }
38005 const dirIndex = this.opQueue.push(directiveOp) - 1;
38006 dirMap.set(dir, dirIndex);
38007 this.opQueue.push(new TcbDirectiveInputsOp(this.tcb, this, node, dir));
38008 }
38009 this.directiveOpMap.set(node, dirMap);
38010 // After expanding the directives, we might need to queue an operation to check any unclaimed
38011 // inputs.
38012 if (node instanceof Element$1) {
38013 // Go through the directives and remove any inputs that it claims from `elementInputs`.
38014 for (const dir of directives) {
38015 for (const propertyName of dir.inputs.propertyNames) {
38016 claimedInputs.add(propertyName);
38017 }
38018 }
38019 this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node, claimedInputs));
38020 // If there are no directives which match this element, then it's a "plain" DOM element (or a
38021 // web component), and should be checked against the DOM schema. If any directives match,
38022 // we must assume that the element could be custom (either a component, or a directive like
38023 // <router-outlet>) and shouldn't validate the element name itself.
38024 const checkElement = directives.length === 0;
38025 this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, checkElement, claimedInputs));
38026 }
38027 }
38028 appendOutputsOfNode(node) {
38029 // Collect all the outputs on the element.
38030 const claimedOutputs = new Set();
38031 const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
38032 if (directives === null || directives.length === 0) {
38033 // If there are no directives, then all outputs are unclaimed outputs, so queue an operation
38034 // to add them if needed.
38035 if (node instanceof Element$1) {
38036 this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, claimedOutputs));
38037 }
38038 return;
38039 }
38040 // Queue operations for all directives to check the relevant outputs for a directive.
38041 for (const dir of directives) {
38042 this.opQueue.push(new TcbDirectiveOutputsOp(this.tcb, this, node, dir));
38043 }
38044 // After expanding the directives, we might need to queue an operation to check any unclaimed
38045 // outputs.
38046 if (node instanceof Element$1) {
38047 // Go through the directives and register any outputs that it claims in `claimedOutputs`.
38048 for (const dir of directives) {
38049 for (const outputProperty of dir.outputs.propertyNames) {
38050 claimedOutputs.add(outputProperty);
38051 }
38052 }
38053 this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, claimedOutputs));
38054 }
38055 }
38056 appendDeepSchemaChecks(nodes) {
38057 for (const node of nodes) {
38058 if (!(node instanceof Element$1 || node instanceof Template)) {
38059 continue;
38060 }
38061 if (node instanceof Element$1) {
38062 const claimedInputs = new Set();
38063 const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
38064 let hasDirectives;
38065 if (directives === null || directives.length === 0) {
38066 hasDirectives = false;
38067 }
38068 else {
38069 hasDirectives = true;
38070 for (const dir of directives) {
38071 for (const propertyName of dir.inputs.propertyNames) {
38072 claimedInputs.add(propertyName);
38073 }
38074 }
38075 }
38076 this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, !hasDirectives, claimedInputs));
38077 }
38078 this.appendDeepSchemaChecks(node.children);
38079 }
38080 }
38081 appendIcuExpressions(node) {
38082 for (const variable of Object.values(node.vars)) {
38083 this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, variable));
38084 }
38085 for (const placeholder of Object.values(node.placeholders)) {
38086 if (placeholder instanceof BoundText) {
38087 this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, placeholder));
38088 }
38089 }
38090 }
38091 }
38092 /**
38093 * Create the `ctx` parameter to the top-level TCB function, with the given generic type arguments.
38094 */
38095 function tcbCtxParam(node, name, typeArguments) {
38096 const type = ts__default["default"].factory.createTypeReferenceNode(name, typeArguments);
38097 return ts__default["default"].factory.createParameterDeclaration(
38098 /* decorators */ undefined,
38099 /* modifiers */ undefined,
38100 /* dotDotDotToken */ undefined,
38101 /* name */ 'ctx',
38102 /* questionToken */ undefined,
38103 /* type */ type,
38104 /* initializer */ undefined);
38105 }
38106 /**
38107 * Process an `AST` expression and convert it into a `ts.Expression`, generating references to the
38108 * correct identifiers in the current scope.
38109 */
38110 function tcbExpression(ast, tcb, scope) {
38111 const translator = new TcbExpressionTranslator(tcb, scope);
38112 return translator.translate(ast);
38113 }
38114 class TcbExpressionTranslator {
38115 constructor(tcb, scope) {
38116 this.tcb = tcb;
38117 this.scope = scope;
38118 }
38119 translate(ast) {
38120 // `astToTypescript` actually does the conversion. A special resolver `tcbResolve` is passed
38121 // which interprets specific expression nodes that interact with the `ImplicitReceiver`. These
38122 // nodes actually refer to identifiers within the current scope.
38123 return astToTypescript(ast, ast => this.resolve(ast), this.tcb.env.config);
38124 }
38125 /**
38126 * Resolve an `AST` expression within the given scope.
38127 *
38128 * Some `AST` expressions refer to top-level concepts (references, variables, the component
38129 * context). This method assists in resolving those.
38130 */
38131 resolve(ast) {
38132 if (ast instanceof PropertyRead && ast.receiver instanceof ImplicitReceiver) {
38133 // Try to resolve a bound target for this expression. If no such target is available, then
38134 // the expression is referencing the top-level component context. In that case, `null` is
38135 // returned here to let it fall through resolution so it will be caught when the
38136 // `ImplicitReceiver` is resolved in the branch below.
38137 return this.resolveTarget(ast);
38138 }
38139 else if (ast instanceof PropertyWrite && ast.receiver instanceof ImplicitReceiver) {
38140 const target = this.resolveTarget(ast);
38141 if (target === null) {
38142 return null;
38143 }
38144 const expr = this.translate(ast.value);
38145 const result = ts__default["default"].createParen(ts__default["default"].createBinary(target, ts__default["default"].SyntaxKind.EqualsToken, expr));
38146 addParseSpanInfo(result, ast.sourceSpan);
38147 return result;
38148 }
38149 else if (ast instanceof ImplicitReceiver) {
38150 // AST instances representing variables and references look very similar to property reads
38151 // or method calls from the component context: both have the shape
38152 // PropertyRead(ImplicitReceiver, 'propName') or Call(ImplicitReceiver, 'methodName').
38153 //
38154 // `translate` will first try to `resolve` the outer PropertyRead/Call. If this works,
38155 // it's because the `BoundTarget` found an expression target for the whole expression, and
38156 // therefore `translate` will never attempt to `resolve` the ImplicitReceiver of that
38157 // PropertyRead/Call.
38158 //
38159 // Therefore if `resolve` is called on an `ImplicitReceiver`, it's because no outer
38160 // PropertyRead/Call resolved to a variable or reference, and therefore this is a
38161 // property read or method call on the component context itself.
38162 return ts__default["default"].createIdentifier('ctx');
38163 }
38164 else if (ast instanceof BindingPipe) {
38165 const expr = this.translate(ast.exp);
38166 const pipeRef = this.tcb.getPipeByName(ast.name);
38167 let pipe;
38168 if (pipeRef === null) {
38169 // No pipe by that name exists in scope. Record this as an error.
38170 this.tcb.oobRecorder.missingPipe(this.tcb.id, ast);
38171 // Use an 'any' value to at least allow the rest of the expression to be checked.
38172 pipe = NULL_AS_ANY;
38173 }
38174 else {
38175 // Use a variable declared as the pipe's type.
38176 pipe = this.tcb.env.pipeInst(pipeRef);
38177 }
38178 const args = ast.args.map(arg => this.translate(arg));
38179 let methodAccess = ts__default["default"].factory.createPropertyAccessExpression(pipe, 'transform');
38180 addParseSpanInfo(methodAccess, ast.nameSpan);
38181 if (!this.tcb.env.config.checkTypeOfPipes) {
38182 methodAccess = ts__default["default"].factory.createAsExpression(methodAccess, ts__default["default"].factory.createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword));
38183 }
38184 const result = ts__default["default"].createCall(
38185 /* expression */ methodAccess,
38186 /* typeArguments */ undefined,
38187 /* argumentsArray */ [expr, ...args]);
38188 addParseSpanInfo(result, ast.sourceSpan);
38189 return result;
38190 }
38191 else if (ast instanceof Call &&
38192 (ast.receiver instanceof PropertyRead || ast.receiver instanceof SafePropertyRead) &&
38193 !(ast.receiver.receiver instanceof ThisReceiver)) {
38194 // Resolve the special `$any(expr)` syntax to insert a cast of the argument to type `any`.
38195 // `$any(expr)` -> `expr as any`
38196 if (ast.receiver.name === '$any' && ast.args.length === 1) {
38197 const expr = this.translate(ast.args[0]);
38198 const exprAsAny = ts__default["default"].createAsExpression(expr, ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword));
38199 const result = ts__default["default"].createParen(exprAsAny);
38200 addParseSpanInfo(result, ast.sourceSpan);
38201 return result;
38202 }
38203 // Attempt to resolve a bound target for the method, and generate the method call if a target
38204 // could be resolved. If no target is available, then the method is referencing the top-level
38205 // component context, in which case `null` is returned to let the `ImplicitReceiver` being
38206 // resolved to the component context.
38207 const receiver = this.resolveTarget(ast);
38208 if (receiver === null) {
38209 return null;
38210 }
38211 const method = wrapForDiagnostics(receiver);
38212 addParseSpanInfo(method, ast.receiver.nameSpan);
38213 const args = ast.args.map(arg => this.translate(arg));
38214 const node = ts__default["default"].createCall(method, undefined, args);
38215 addParseSpanInfo(node, ast.sourceSpan);
38216 return node;
38217 }
38218 else {
38219 // This AST isn't special after all.
38220 return null;
38221 }
38222 }
38223 /**
38224 * Attempts to resolve a bound target for a given expression, and translates it into the
38225 * appropriate `ts.Expression` that represents the bound target. If no target is available,
38226 * `null` is returned.
38227 */
38228 resolveTarget(ast) {
38229 const binding = this.tcb.boundTarget.getExpressionTarget(ast);
38230 if (binding === null) {
38231 return null;
38232 }
38233 const expr = this.scope.resolve(binding);
38234 addParseSpanInfo(expr, ast.sourceSpan);
38235 return expr;
38236 }
38237 }
38238 /**
38239 * Call the type constructor of a directive instance on a given template node, inferring a type for
38240 * the directive instance from any bound inputs.
38241 */
38242 function tcbCallTypeCtor(dir, tcb, inputs) {
38243 const typeCtor = tcb.env.typeCtorFor(dir);
38244 // Construct an array of `ts.PropertyAssignment`s for each of the directive's inputs.
38245 const members = inputs.map(input => {
38246 const propertyName = ts__default["default"].createStringLiteral(input.field);
38247 if (input.type === 'binding') {
38248 // For bound inputs, the property is assigned the binding expression.
38249 const expr = widenBinding(input.expression, tcb);
38250 const assignment = ts__default["default"].createPropertyAssignment(propertyName, wrapForDiagnostics(expr));
38251 addParseSpanInfo(assignment, input.sourceSpan);
38252 return assignment;
38253 }
38254 else {
38255 // A type constructor is required to be called with all input properties, so any unset
38256 // inputs are simply assigned a value of type `any` to ignore them.
38257 return ts__default["default"].createPropertyAssignment(propertyName, NULL_AS_ANY);
38258 }
38259 });
38260 // Call the `ngTypeCtor` method on the directive class, with an object literal argument created
38261 // from the matched inputs.
38262 return ts__default["default"].createCall(
38263 /* expression */ typeCtor,
38264 /* typeArguments */ undefined,
38265 /* argumentsArray */ [ts__default["default"].createObjectLiteral(members)]);
38266 }
38267 function getBoundInputs(directive, node, tcb) {
38268 const boundInputs = [];
38269 const processAttribute = (attr) => {
38270 // Skip non-property bindings.
38271 if (attr instanceof BoundAttribute && attr.type !== 0 /* Property */) {
38272 return;
38273 }
38274 // Skip the attribute if the directive does not have an input for it.
38275 const inputs = directive.inputs.getByBindingPropertyName(attr.name);
38276 if (inputs === null) {
38277 return;
38278 }
38279 const fieldNames = inputs.map(input => input.classPropertyName);
38280 boundInputs.push({ attribute: attr, fieldNames });
38281 };
38282 node.inputs.forEach(processAttribute);
38283 node.attributes.forEach(processAttribute);
38284 if (node instanceof Template) {
38285 node.templateAttrs.forEach(processAttribute);
38286 }
38287 return boundInputs;
38288 }
38289 /**
38290 * Translates the given attribute binding to a `ts.Expression`.
38291 */
38292 function translateInput(attr, tcb, scope) {
38293 if (attr instanceof BoundAttribute) {
38294 // Produce an expression representing the value of the binding.
38295 return tcbExpression(attr.value, tcb, scope);
38296 }
38297 else {
38298 // For regular attributes with a static string value, use the represented string literal.
38299 return ts__default["default"].createStringLiteral(attr.value);
38300 }
38301 }
38302 /**
38303 * Potentially widens the type of `expr` according to the type-checking configuration.
38304 */
38305 function widenBinding(expr, tcb) {
38306 if (!tcb.env.config.checkTypeOfInputBindings) {
38307 // If checking the type of bindings is disabled, cast the resulting expression to 'any'
38308 // before the assignment.
38309 return tsCastToAny(expr);
38310 }
38311 else if (!tcb.env.config.strictNullInputBindings) {
38312 if (ts__default["default"].isObjectLiteralExpression(expr) || ts__default["default"].isArrayLiteralExpression(expr)) {
38313 // Object literals and array literals should not be wrapped in non-null assertions as that
38314 // would cause literals to be prematurely widened, resulting in type errors when assigning
38315 // into a literal type.
38316 return expr;
38317 }
38318 else {
38319 // If strict null checks are disabled, erase `null` and `undefined` from the type by
38320 // wrapping the expression in a non-null assertion.
38321 return ts__default["default"].createNonNullExpression(expr);
38322 }
38323 }
38324 else {
38325 // No widening is requested, use the expression as is.
38326 return expr;
38327 }
38328 }
38329 const EVENT_PARAMETER = '$event';
38330 /**
38331 * Creates an arrow function to be used as handler function for event bindings. The handler
38332 * function has a single parameter `$event` and the bound event's handler `AST` represented as a
38333 * TypeScript expression as its body.
38334 *
38335 * When `eventType` is set to `Infer`, the `$event` parameter will not have an explicit type. This
38336 * allows for the created handler function to have its `$event` parameter's type inferred based on
38337 * how it's used, to enable strict type checking of event bindings. When set to `Any`, the `$event`
38338 * parameter will have an explicit `any` type, effectively disabling strict type checking of event
38339 * bindings. Alternatively, an explicit type can be passed for the `$event` parameter.
38340 */
38341 function tcbCreateEventHandler(event, tcb, scope, eventType) {
38342 const handler = tcbEventHandlerExpression(event.handler, tcb, scope);
38343 let eventParamType;
38344 if (eventType === 0 /* Infer */) {
38345 eventParamType = undefined;
38346 }
38347 else if (eventType === 1 /* Any */) {
38348 eventParamType = ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword);
38349 }
38350 else {
38351 eventParamType = eventType;
38352 }
38353 // Obtain all guards that have been applied to the scope and its parents, as they have to be
38354 // repeated within the handler function for their narrowing to be in effect within the handler.
38355 const guards = scope.guards();
38356 let body = ts__default["default"].createExpressionStatement(handler);
38357 if (guards !== null) {
38358 // Wrap the body in an `if` statement containing all guards that have to be applied.
38359 body = ts__default["default"].createIf(guards, body);
38360 }
38361 const eventParam = ts__default["default"].createParameter(
38362 /* decorators */ undefined,
38363 /* modifiers */ undefined,
38364 /* dotDotDotToken */ undefined,
38365 /* name */ EVENT_PARAMETER,
38366 /* questionToken */ undefined,
38367 /* type */ eventParamType);
38368 addExpressionIdentifier(eventParam, ExpressionIdentifier.EVENT_PARAMETER);
38369 return ts__default["default"].createFunctionExpression(
38370 /* modifier */ undefined,
38371 /* asteriskToken */ undefined,
38372 /* name */ undefined,
38373 /* typeParameters */ undefined,
38374 /* parameters */ [eventParam],
38375 /* type */ ts__default["default"].createKeywordTypeNode(ts__default["default"].SyntaxKind.AnyKeyword),
38376 /* body */ ts__default["default"].createBlock([body]));
38377 }
38378 /**
38379 * Similar to `tcbExpression`, this function converts the provided `AST` expression into a
38380 * `ts.Expression`, with special handling of the `$event` variable that can be used within event
38381 * bindings.
38382 */
38383 function tcbEventHandlerExpression(ast, tcb, scope) {
38384 const translator = new TcbEventHandlerTranslator(tcb, scope);
38385 return translator.translate(ast);
38386 }
38387 function isSplitTwoWayBinding(inputName, output, inputs, tcb) {
38388 const input = inputs.find(input => input.name === inputName);
38389 if (input === undefined || input.sourceSpan !== output.sourceSpan) {
38390 return false;
38391 }
38392 // Input consumer should be a directive because it's claimed
38393 const inputConsumer = tcb.boundTarget.getConsumerOfBinding(input);
38394 const outputConsumer = tcb.boundTarget.getConsumerOfBinding(output);
38395 if (outputConsumer === null || inputConsumer.ref === undefined ||
38396 outputConsumer instanceof Template) {
38397 return false;
38398 }
38399 if (outputConsumer instanceof Element$1) {
38400 tcb.oobRecorder.splitTwoWayBinding(tcb.id, input, output, inputConsumer.ref.node, outputConsumer);
38401 return true;
38402 }
38403 else if (outputConsumer.ref !== inputConsumer.ref) {
38404 tcb.oobRecorder.splitTwoWayBinding(tcb.id, input, output, inputConsumer.ref.node, outputConsumer.ref.node);
38405 return true;
38406 }
38407 return false;
38408 }
38409 class TcbEventHandlerTranslator extends TcbExpressionTranslator {
38410 resolve(ast) {
38411 // Recognize a property read on the implicit receiver corresponding with the event parameter
38412 // that is available in event bindings. Since this variable is a parameter of the handler
38413 // function that the converted expression becomes a child of, just create a reference to the
38414 // parameter by its name.
38415 if (ast instanceof PropertyRead && ast.receiver instanceof ImplicitReceiver &&
38416 !(ast.receiver instanceof ThisReceiver) && ast.name === EVENT_PARAMETER) {
38417 const event = ts__default["default"].createIdentifier(EVENT_PARAMETER);
38418 addParseSpanInfo(event, ast.nameSpan);
38419 return event;
38420 }
38421 return super.resolve(ast);
38422 }
38423 }
38424
38425 /**
38426 * @license
38427 * Copyright Google LLC All Rights Reserved.
38428 *
38429 * Use of this source code is governed by an MIT-style license that can be
38430 * found in the LICENSE file at https://angular.io/license
38431 */
38432 /**
38433 * An `Environment` representing the single type-checking file into which most (if not all) Type
38434 * Check Blocks (TCBs) will be generated.
38435 *
38436 * The `TypeCheckFile` hosts multiple TCBs and allows the sharing of declarations (e.g. type
38437 * constructors) between them. Rather than return such declarations via `getPreludeStatements()`, it
38438 * hoists them to the top of the generated `ts.SourceFile`.
38439 */
38440 class TypeCheckFile extends Environment {
38441 constructor(fileName, config, refEmitter, reflector, compilerHost) {
38442 super(config, new ImportManager(new NoopImportRewriter(), 'i'), refEmitter, reflector, ts__default["default"].createSourceFile(compilerHost.getCanonicalFileName(fileName), '', ts__default["default"].ScriptTarget.Latest, true));
38443 this.fileName = fileName;
38444 this.nextTcbId = 1;
38445 this.tcbStatements = [];
38446 }
38447 addTypeCheckBlock(ref, meta, domSchemaChecker, oobRecorder, genericContextBehavior) {
38448 const fnId = ts__default["default"].createIdentifier(`_tcb${this.nextTcbId++}`);
38449 const fn = generateTypeCheckBlock(this, ref, fnId, meta, domSchemaChecker, oobRecorder, genericContextBehavior);
38450 this.tcbStatements.push(fn);
38451 }
38452 render(removeComments) {
38453 let source = this.importManager.getAllImports(this.contextFile.fileName)
38454 .map(i => `import * as ${i.qualifier.text} from '${i.specifier}';`)
38455 .join('\n') +
38456 '\n\n';
38457 const printer = ts__default["default"].createPrinter({ removeComments });
38458 source += '\n';
38459 for (const stmt of this.pipeInstStatements) {
38460 source += printer.printNode(ts__default["default"].EmitHint.Unspecified, stmt, this.contextFile) + '\n';
38461 }
38462 for (const stmt of this.typeCtorStatements) {
38463 source += printer.printNode(ts__default["default"].EmitHint.Unspecified, stmt, this.contextFile) + '\n';
38464 }
38465 source += '\n';
38466 for (const stmt of this.tcbStatements) {
38467 source += printer.printNode(ts__default["default"].EmitHint.Unspecified, stmt, this.contextFile) + '\n';
38468 }
38469 // Ensure the template type-checking file is an ES module. Otherwise, it's interpreted as some
38470 // kind of global namespace in TS, which forces a full re-typecheck of the user's program that
38471 // is somehow more expensive than the initial parse.
38472 source += '\nexport const IS_A_MODULE = true;\n';
38473 return source;
38474 }
38475 getPreludeStatements() {
38476 return [];
38477 }
38478 }
38479
38480 /**
38481 * @license
38482 * Copyright Google LLC All Rights Reserved.
38483 *
38484 * Use of this source code is governed by an MIT-style license that can be
38485 * found in the LICENSE file at https://angular.io/license
38486 */
38487 /**
38488 * How a type-checking context should handle operations which would require inlining.
38489 */
38490 var InliningMode;
38491 (function (InliningMode) {
38492 /**
38493 * Use inlining operations when required.
38494 */
38495 InliningMode[InliningMode["InlineOps"] = 0] = "InlineOps";
38496 /**
38497 * Produce diagnostics if an operation would require inlining.
38498 */
38499 InliningMode[InliningMode["Error"] = 1] = "Error";
38500 })(InliningMode || (InliningMode = {}));
38501 /**
38502 * A template type checking context for a program.
38503 *
38504 * The `TypeCheckContext` allows registration of components and their templates which need to be
38505 * type checked.
38506 */
38507 class TypeCheckContextImpl {
38508 constructor(config, compilerHost, refEmitter, reflector, host, inlining, perf) {
38509 this.config = config;
38510 this.compilerHost = compilerHost;
38511 this.refEmitter = refEmitter;
38512 this.reflector = reflector;
38513 this.host = host;
38514 this.inlining = inlining;
38515 this.perf = perf;
38516 this.fileMap = new Map();
38517 /**
38518 * A `Map` of `ts.SourceFile`s that the context has seen to the operations (additions of methods
38519 * or type-check blocks) that need to be eventually performed on that file.
38520 */
38521 this.opMap = new Map();
38522 /**
38523 * Tracks when an a particular class has a pending type constructor patching operation already
38524 * queued.
38525 */
38526 this.typeCtorPending = new Set();
38527 if (inlining === InliningMode.Error && config.useInlineTypeConstructors) {
38528 // We cannot use inlining for type checking since this environment does not support it.
38529 throw new Error(`AssertionError: invalid inlining configuration.`);
38530 }
38531 }
38532 /**
38533 * Register a template to potentially be type-checked.
38534 *
38535 * Implements `TypeCheckContext.addTemplate`.
38536 */
38537 addTemplate(ref, binder, template, pipes, schemas, sourceMapping, file, parseErrors) {
38538 if (!this.host.shouldCheckComponent(ref.node)) {
38539 return;
38540 }
38541 const fileData = this.dataForFile(ref.node.getSourceFile());
38542 const shimData = this.pendingShimForComponent(ref.node);
38543 const templateId = fileData.sourceManager.getTemplateId(ref.node);
38544 const templateDiagnostics = [];
38545 if (parseErrors !== null) {
38546 templateDiagnostics.push(...this.getTemplateDiagnostics(parseErrors, templateId, sourceMapping));
38547 }
38548 const boundTarget = binder.bind({ template });
38549 if (this.inlining === InliningMode.InlineOps) {
38550 // Get all of the directives used in the template and record inline type constructors when
38551 // required.
38552 for (const dir of boundTarget.getUsedDirectives()) {
38553 const dirRef = dir.ref;
38554 const dirNode = dirRef.node;
38555 if (!dir.isGeneric || !requiresInlineTypeCtor(dirNode, this.reflector)) {
38556 // inlining not required
38557 continue;
38558 }
38559 // Add an inline type constructor operation for the directive.
38560 this.addInlineTypeCtor(fileData, dirNode.getSourceFile(), dirRef, {
38561 fnName: 'ngTypeCtor',
38562 // The constructor should have a body if the directive comes from a .ts file, but not if
38563 // it comes from a .d.ts file. .d.ts declarations don't have bodies.
38564 body: !dirNode.getSourceFile().isDeclarationFile,
38565 fields: {
38566 inputs: dir.inputs.classPropertyNames,
38567 outputs: dir.outputs.classPropertyNames,
38568 // TODO(alxhub): support queries
38569 queries: dir.queries,
38570 },
38571 coercedInputFields: dir.coercedInputFields,
38572 });
38573 }
38574 }
38575 shimData.templates.set(templateId, {
38576 template,
38577 boundTarget,
38578 templateDiagnostics,
38579 });
38580 const inliningRequirement = requiresInlineTypeCheckBlock(ref.node, pipes, this.reflector);
38581 // If inlining is not supported, but is required for either the TCB or one of its directive
38582 // dependencies, then exit here with an error.
38583 if (this.inlining === InliningMode.Error &&
38584 inliningRequirement === TcbInliningRequirement.MustInline) {
38585 // This template cannot be supported because the underlying strategy does not support inlining
38586 // and inlining would be required.
38587 // Record diagnostics to indicate the issues with this template.
38588 shimData.oobRecorder.requiresInlineTcb(templateId, ref.node);
38589 // Checking this template would be unsupported, so don't try.
38590 this.perf.eventCount(PerfEvent.SkipGenerateTcbNoInline);
38591 return;
38592 }
38593 const meta = {
38594 id: fileData.sourceManager.captureSource(ref.node, sourceMapping, file),
38595 boundTarget,
38596 pipes,
38597 schemas,
38598 };
38599 this.perf.eventCount(PerfEvent.GenerateTcb);
38600 if (inliningRequirement !== TcbInliningRequirement.None &&
38601 this.inlining === InliningMode.InlineOps) {
38602 // This class didn't meet the requirements for external type checking, so generate an inline
38603 // TCB for the class.
38604 this.addInlineTypeCheckBlock(fileData, shimData, ref, meta);
38605 }
38606 else if (inliningRequirement === TcbInliningRequirement.ShouldInlineForGenericBounds &&
38607 this.inlining === InliningMode.Error) {
38608 // It's suggested that this TCB should be generated inline due to the component's generic
38609 // bounds, but inlining is not supported by the current environment. Use a non-inline type
38610 // check block, but fall back to `any` generic parameters since the generic bounds can't be
38611 // referenced in that context. This will infer a less useful type for the component, but allow
38612 // for type-checking it in an environment where that would not be possible otherwise.
38613 shimData.file.addTypeCheckBlock(ref, meta, shimData.domSchemaChecker, shimData.oobRecorder, TcbGenericContextBehavior.FallbackToAny);
38614 }
38615 else {
38616 shimData.file.addTypeCheckBlock(ref, meta, shimData.domSchemaChecker, shimData.oobRecorder, TcbGenericContextBehavior.UseEmitter);
38617 }
38618 }
38619 /**
38620 * Record a type constructor for the given `node` with the given `ctorMetadata`.
38621 */
38622 addInlineTypeCtor(fileData, sf, ref, ctorMeta) {
38623 if (this.typeCtorPending.has(ref.node)) {
38624 return;
38625 }
38626 this.typeCtorPending.add(ref.node);
38627 // Lazily construct the operation map.
38628 if (!this.opMap.has(sf)) {
38629 this.opMap.set(sf, []);
38630 }
38631 const ops = this.opMap.get(sf);
38632 // Push a `TypeCtorOp` into the operation queue for the source file.
38633 ops.push(new TypeCtorOp(ref, ctorMeta));
38634 fileData.hasInlines = true;
38635 }
38636 /**
38637 * Transform a `ts.SourceFile` into a version that includes type checking code.
38638 *
38639 * If this particular `ts.SourceFile` requires changes, the text representing its new contents
38640 * will be returned. Otherwise, a `null` return indicates no changes were necessary.
38641 */
38642 transform(sf) {
38643 // If there are no operations pending for this particular file, return `null` to indicate no
38644 // changes.
38645 if (!this.opMap.has(sf)) {
38646 return null;
38647 }
38648 // Imports may need to be added to the file to support type-checking of directives used in the
38649 // template within it.
38650 const importManager = new ImportManager(new NoopImportRewriter(), '_i');
38651 // Each Op has a splitPoint index into the text where it needs to be inserted. Split the
38652 // original source text into chunks at these split points, where code will be inserted between
38653 // the chunks.
38654 const ops = this.opMap.get(sf).sort(orderOps);
38655 const textParts = splitStringAtPoints(sf.text, ops.map(op => op.splitPoint));
38656 // Use a `ts.Printer` to generate source code.
38657 const printer = ts__default["default"].createPrinter({ omitTrailingSemicolon: true });
38658 // Begin with the intial section of the code text.
38659 let code = textParts[0];
38660 // Process each operation and use the printer to generate source code for it, inserting it into
38661 // the source code in between the original chunks.
38662 ops.forEach((op, idx) => {
38663 const text = op.execute(importManager, sf, this.refEmitter, printer);
38664 code += '\n\n' + text + textParts[idx + 1];
38665 });
38666 // Write out the imports that need to be added to the beginning of the file.
38667 let imports = importManager.getAllImports(sf.fileName)
38668 .map(i => `import * as ${i.qualifier.text} from '${i.specifier}';`)
38669 .join('\n');
38670 code = imports + '\n' + code;
38671 return code;
38672 }
38673 finalize() {
38674 // First, build the map of updates to source files.
38675 const updates = new Map();
38676 for (const originalSf of this.opMap.keys()) {
38677 const newText = this.transform(originalSf);
38678 if (newText !== null) {
38679 updates.set(absoluteFromSourceFile(originalSf), {
38680 newText,
38681 originalFile: originalSf,
38682 });
38683 }
38684 }
38685 // Then go through each input file that has pending code generation operations.
38686 for (const [sfPath, pendingFileData] of this.fileMap) {
38687 // For each input file, consider generation operations for each of its shims.
38688 for (const pendingShimData of pendingFileData.shimData.values()) {
38689 this.host.recordShimData(sfPath, {
38690 genesisDiagnostics: [
38691 ...pendingShimData.domSchemaChecker.diagnostics,
38692 ...pendingShimData.oobRecorder.diagnostics,
38693 ],
38694 hasInlines: pendingFileData.hasInlines,
38695 path: pendingShimData.file.fileName,
38696 templates: pendingShimData.templates,
38697 });
38698 const sfText = pendingShimData.file.render(false /* removeComments */);
38699 updates.set(pendingShimData.file.fileName, {
38700 newText: sfText,
38701 // Shim files do not have an associated original file.
38702 originalFile: null,
38703 });
38704 }
38705 }
38706 return updates;
38707 }
38708 addInlineTypeCheckBlock(fileData, shimData, ref, tcbMeta) {
38709 const sf = ref.node.getSourceFile();
38710 if (!this.opMap.has(sf)) {
38711 this.opMap.set(sf, []);
38712 }
38713 const ops = this.opMap.get(sf);
38714 ops.push(new InlineTcbOp(ref, tcbMeta, this.config, this.reflector, shimData.domSchemaChecker, shimData.oobRecorder));
38715 fileData.hasInlines = true;
38716 }
38717 pendingShimForComponent(node) {
38718 const fileData = this.dataForFile(node.getSourceFile());
38719 const shimPath = TypeCheckShimGenerator.shimFor(absoluteFromSourceFile(node.getSourceFile()));
38720 if (!fileData.shimData.has(shimPath)) {
38721 fileData.shimData.set(shimPath, {
38722 domSchemaChecker: new RegistryDomSchemaChecker(fileData.sourceManager),
38723 oobRecorder: new OutOfBandDiagnosticRecorderImpl(fileData.sourceManager),
38724 file: new TypeCheckFile(shimPath, this.config, this.refEmitter, this.reflector, this.compilerHost),
38725 templates: new Map(),
38726 });
38727 }
38728 return fileData.shimData.get(shimPath);
38729 }
38730 dataForFile(sf) {
38731 const sfPath = absoluteFromSourceFile(sf);
38732 if (!this.fileMap.has(sfPath)) {
38733 const data = {
38734 hasInlines: false,
38735 sourceManager: this.host.getSourceManager(sfPath),
38736 shimData: new Map(),
38737 };
38738 this.fileMap.set(sfPath, data);
38739 }
38740 return this.fileMap.get(sfPath);
38741 }
38742 getTemplateDiagnostics(parseErrors, templateId, sourceMapping) {
38743 return parseErrors.map(error => {
38744 const span = error.span;
38745 if (span.start.offset === span.end.offset) {
38746 // Template errors can contain zero-length spans, if the error occurs at a single point.
38747 // However, TypeScript does not handle displaying a zero-length diagnostic very well, so
38748 // increase the ending offset by 1 for such errors, to ensure the position is shown in the
38749 // diagnostic.
38750 span.end.offset++;
38751 }
38752 return makeTemplateDiagnostic(templateId, sourceMapping, span, ts__default["default"].DiagnosticCategory.Error, ngErrorCode(ErrorCode.TEMPLATE_PARSE_ERROR), error.msg);
38753 });
38754 }
38755 }
38756 /**
38757 * A type check block operation which produces inline type check code for a particular component.
38758 */
38759 class InlineTcbOp {
38760 constructor(ref, meta, config, reflector, domSchemaChecker, oobRecorder) {
38761 this.ref = ref;
38762 this.meta = meta;
38763 this.config = config;
38764 this.reflector = reflector;
38765 this.domSchemaChecker = domSchemaChecker;
38766 this.oobRecorder = oobRecorder;
38767 }
38768 /**
38769 * Type check blocks are inserted immediately after the end of the component class.
38770 */
38771 get splitPoint() {
38772 return this.ref.node.end + 1;
38773 }
38774 execute(im, sf, refEmitter, printer) {
38775 const env = new Environment(this.config, im, refEmitter, this.reflector, sf);
38776 const fnName = ts__default["default"].createIdentifier(`_tcb_${this.ref.node.pos}`);
38777 // Inline TCBs should copy any generic type parameter nodes directly, as the TCB code is inlined
38778 // into the class in a context where that will always be legal.
38779 const fn = generateTypeCheckBlock(env, this.ref, fnName, this.meta, this.domSchemaChecker, this.oobRecorder, TcbGenericContextBehavior.CopyClassNodes);
38780 return printer.printNode(ts__default["default"].EmitHint.Unspecified, fn, sf);
38781 }
38782 }
38783 /**
38784 * A type constructor operation which produces type constructor code for a particular directive.
38785 */
38786 class TypeCtorOp {
38787 constructor(ref, meta) {
38788 this.ref = ref;
38789 this.meta = meta;
38790 }
38791 /**
38792 * Type constructor operations are inserted immediately before the end of the directive class.
38793 */
38794 get splitPoint() {
38795 return this.ref.node.end - 1;
38796 }
38797 execute(im, sf, refEmitter, printer) {
38798 const tcb = generateInlineTypeCtor(this.ref.node, this.meta);
38799 return printer.printNode(ts__default["default"].EmitHint.Unspecified, tcb, sf);
38800 }
38801 }
38802 /**
38803 * Compare two operations and return their split point ordering.
38804 */
38805 function orderOps(op1, op2) {
38806 return op1.splitPoint - op2.splitPoint;
38807 }
38808 /**
38809 * Split a string into chunks at any number of split points.
38810 */
38811 function splitStringAtPoints(str, points) {
38812 const splits = [];
38813 let start = 0;
38814 for (let i = 0; i < points.length; i++) {
38815 const point = points[i];
38816 splits.push(str.substring(start, point));
38817 start = point;
38818 }
38819 splits.push(str.substring(start));
38820 return splits;
38821 }
38822
38823 /**
38824 * @license
38825 * Copyright Google LLC All Rights Reserved.
38826 *
38827 * Use of this source code is governed by an MIT-style license that can be
38828 * found in the LICENSE file at https://angular.io/license
38829 */
38830 const LF_CHAR = 10;
38831 const CR_CHAR = 13;
38832 const LINE_SEP_CHAR = 8232;
38833 const PARAGRAPH_CHAR = 8233;
38834 /** Gets the line and character for the given position from the line starts map. */
38835 function getLineAndCharacterFromPosition(lineStartsMap, position) {
38836 const lineIndex = findClosestLineStartPosition(lineStartsMap, position);
38837 return { character: position - lineStartsMap[lineIndex], line: lineIndex };
38838 }
38839 /**
38840 * Computes the line start map of the given text. This can be used in order to
38841 * retrieve the line and character of a given text position index.
38842 */
38843 function computeLineStartsMap(text) {
38844 const result = [0];
38845 let pos = 0;
38846 while (pos < text.length) {
38847 const char = text.charCodeAt(pos++);
38848 // Handles the "CRLF" line break. In that case we peek the character
38849 // after the "CR" and check if it is a line feed.
38850 if (char === CR_CHAR) {
38851 if (text.charCodeAt(pos) === LF_CHAR) {
38852 pos++;
38853 }
38854 result.push(pos);
38855 }
38856 else if (char === LF_CHAR || char === LINE_SEP_CHAR || char === PARAGRAPH_CHAR) {
38857 result.push(pos);
38858 }
38859 }
38860 result.push(pos);
38861 return result;
38862 }
38863 /** Finds the closest line start for the given position. */
38864 function findClosestLineStartPosition(linesMap, position, low = 0, high = linesMap.length - 1) {
38865 while (low <= high) {
38866 const pivotIdx = Math.floor((low + high) / 2);
38867 const pivotEl = linesMap[pivotIdx];
38868 if (pivotEl === position) {
38869 return pivotIdx;
38870 }
38871 else if (position > pivotEl) {
38872 low = pivotIdx + 1;
38873 }
38874 else {
38875 high = pivotIdx - 1;
38876 }
38877 }
38878 // In case there was no exact match, return the closest "lower" line index. We also
38879 // subtract the index by one because want the index of the previous line start.
38880 return low - 1;
38881 }
38882
38883 /**
38884 * @license
38885 * Copyright Google LLC All Rights Reserved.
38886 *
38887 * Use of this source code is governed by an MIT-style license that can be
38888 * found in the LICENSE file at https://angular.io/license
38889 */
38890 /**
38891 * Represents the source of a template that was processed during type-checking. This information is
38892 * used when translating parse offsets in diagnostics back to their original line/column location.
38893 */
38894 class TemplateSource {
38895 constructor(mapping, file) {
38896 this.mapping = mapping;
38897 this.file = file;
38898 this.lineStarts = null;
38899 }
38900 toParseSourceSpan(start, end) {
38901 const startLoc = this.toParseLocation(start);
38902 const endLoc = this.toParseLocation(end);
38903 return new ParseSourceSpan(startLoc, endLoc);
38904 }
38905 toParseLocation(position) {
38906 const lineStarts = this.acquireLineStarts();
38907 const { line, character } = getLineAndCharacterFromPosition(lineStarts, position);
38908 return new ParseLocation(this.file, position, line, character);
38909 }
38910 acquireLineStarts() {
38911 if (this.lineStarts === null) {
38912 this.lineStarts = computeLineStartsMap(this.file.content);
38913 }
38914 return this.lineStarts;
38915 }
38916 }
38917 /**
38918 * Assigns IDs to templates and keeps track of their origins.
38919 *
38920 * Implements `TemplateSourceResolver` to resolve the source of a template based on these IDs.
38921 */
38922 class TemplateSourceManager {
38923 constructor() {
38924 /**
38925 * This map keeps track of all template sources that have been type-checked by the id that is
38926 * attached to a TCB's function declaration as leading trivia. This enables translation of
38927 * diagnostics produced for TCB code to their source location in the template.
38928 */
38929 this.templateSources = new Map();
38930 }
38931 getTemplateId(node) {
38932 return getTemplateId$1(node);
38933 }
38934 captureSource(node, mapping, file) {
38935 const id = getTemplateId$1(node);
38936 this.templateSources.set(id, new TemplateSource(mapping, file));
38937 return id;
38938 }
38939 getSourceMapping(id) {
38940 if (!this.templateSources.has(id)) {
38941 throw new Error(`Unexpected unknown template ID: ${id}`);
38942 }
38943 return this.templateSources.get(id).mapping;
38944 }
38945 toParseSourceSpan(id, span) {
38946 if (!this.templateSources.has(id)) {
38947 return null;
38948 }
38949 const templateSource = this.templateSources.get(id);
38950 return templateSource.toParseSourceSpan(span.start, span.end);
38951 }
38952 }
38953
38954 /**
38955 * @license
38956 * Copyright Google LLC All Rights Reserved.
38957 *
38958 * Use of this source code is governed by an MIT-style license that can be
38959 * found in the LICENSE file at https://angular.io/license
38960 */
38961 /**
38962 * Generates and caches `Symbol`s for various template structures for a given component.
38963 *
38964 * The `SymbolBuilder` internally caches the `Symbol`s it creates, and must be destroyed and
38965 * replaced if the component's template changes.
38966 */
38967 class SymbolBuilder {
38968 constructor(shimPath, typeCheckBlock, templateData, componentScopeReader,
38969 // The `ts.TypeChecker` depends on the current type-checking program, and so must be requested
38970 // on-demand instead of cached.
38971 getTypeChecker) {
38972 this.shimPath = shimPath;
38973 this.typeCheckBlock = typeCheckBlock;
38974 this.templateData = templateData;
38975 this.componentScopeReader = componentScopeReader;
38976 this.getTypeChecker = getTypeChecker;
38977 this.symbolCache = new Map();
38978 }
38979 getSymbol(node) {
38980 if (this.symbolCache.has(node)) {
38981 return this.symbolCache.get(node);
38982 }
38983 let symbol = null;
38984 if (node instanceof BoundAttribute || node instanceof TextAttribute) {
38985 // TODO(atscott): input and output bindings only return the first directive match but should
38986 // return a list of bindings for all of them.
38987 symbol = this.getSymbolOfInputBinding(node);
38988 }
38989 else if (node instanceof BoundEvent) {
38990 symbol = this.getSymbolOfBoundEvent(node);
38991 }
38992 else if (node instanceof Element$1) {
38993 symbol = this.getSymbolOfElement(node);
38994 }
38995 else if (node instanceof Template) {
38996 symbol = this.getSymbolOfAstTemplate(node);
38997 }
38998 else if (node instanceof Variable) {
38999 symbol = this.getSymbolOfVariable(node);
39000 }
39001 else if (node instanceof Reference$1) {
39002 symbol = this.getSymbolOfReference(node);
39003 }
39004 else if (node instanceof BindingPipe) {
39005 symbol = this.getSymbolOfPipe(node);
39006 }
39007 else if (node instanceof AST) {
39008 symbol = this.getSymbolOfTemplateExpression(node);
39009 }
39010 else ;
39011 this.symbolCache.set(node, symbol);
39012 return symbol;
39013 }
39014 getSymbolOfAstTemplate(template) {
39015 const directives = this.getDirectivesOfNode(template);
39016 return { kind: SymbolKind.Template, directives, templateNode: template };
39017 }
39018 getSymbolOfElement(element) {
39019 const elementSourceSpan = element.startSourceSpan ?? element.sourceSpan;
39020 const node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: elementSourceSpan, filter: ts__default["default"].isVariableDeclaration });
39021 if (node === null) {
39022 return null;
39023 }
39024 const symbolFromDeclaration = this.getSymbolOfTsNode(node);
39025 if (symbolFromDeclaration === null || symbolFromDeclaration.tsSymbol === null) {
39026 return null;
39027 }
39028 const directives = this.getDirectivesOfNode(element);
39029 // All statements in the TCB are `Expression`s that optionally include more information.
39030 // An `ElementSymbol` uses the information returned for the variable declaration expression,
39031 // adds the directives for the element, and updates the `kind` to be `SymbolKind.Element`.
39032 return {
39033 ...symbolFromDeclaration,
39034 kind: SymbolKind.Element,
39035 directives,
39036 templateNode: element,
39037 };
39038 }
39039 getDirectivesOfNode(element) {
39040 const elementSourceSpan = element.startSourceSpan ?? element.sourceSpan;
39041 const tcbSourceFile = this.typeCheckBlock.getSourceFile();
39042 // directives could be either:
39043 // - var _t1: TestDir /*T:D*/ = (null!);
39044 // - var _t1 /*T:D*/ = _ctor1({});
39045 const isDirectiveDeclaration = (node) => (ts__default["default"].isTypeNode(node) || ts__default["default"].isIdentifier(node)) && ts__default["default"].isVariableDeclaration(node.parent) &&
39046 hasExpressionIdentifier(tcbSourceFile, node, ExpressionIdentifier.DIRECTIVE);
39047 const nodes = findAllMatchingNodes(this.typeCheckBlock, { withSpan: elementSourceSpan, filter: isDirectiveDeclaration });
39048 return nodes
39049 .map(node => {
39050 const symbol = this.getSymbolOfTsNode(node.parent);
39051 if (symbol === null || !isSymbolWithValueDeclaration(symbol.tsSymbol) ||
39052 !ts__default["default"].isClassDeclaration(symbol.tsSymbol.valueDeclaration)) {
39053 return null;
39054 }
39055 const meta = this.getDirectiveMeta(element, symbol.tsSymbol.valueDeclaration);
39056 if (meta === null) {
39057 return null;
39058 }
39059 const ngModule = this.getDirectiveModule(symbol.tsSymbol.valueDeclaration);
39060 if (meta.selector === null) {
39061 return null;
39062 }
39063 const isComponent = meta.isComponent ?? null;
39064 const directiveSymbol = {
39065 ...symbol,
39066 tsSymbol: symbol.tsSymbol,
39067 selector: meta.selector,
39068 isComponent,
39069 ngModule,
39070 kind: SymbolKind.Directive,
39071 isStructural: meta.isStructural,
39072 };
39073 return directiveSymbol;
39074 })
39075 .filter((d) => d !== null);
39076 }
39077 getDirectiveMeta(host, directiveDeclaration) {
39078 let directives = this.templateData.boundTarget.getDirectivesOfNode(host);
39079 // `getDirectivesOfNode` will not return the directives intended for an element
39080 // on a microsyntax template, for example `<div *ngFor="let user of users;" dir>`,
39081 // the `dir` will be skipped, but it's needed in language service.
39082 const firstChild = host.children[0];
39083 if (firstChild instanceof Element$1) {
39084 const isMicrosyntaxTemplate = host instanceof Template &&
39085 sourceSpanEqual(firstChild.sourceSpan, host.sourceSpan);
39086 if (isMicrosyntaxTemplate) {
39087 const firstChildDirectives = this.templateData.boundTarget.getDirectivesOfNode(firstChild);
39088 if (firstChildDirectives !== null && directives !== null) {
39089 directives = directives.concat(firstChildDirectives);
39090 }
39091 else {
39092 directives = directives ?? firstChildDirectives;
39093 }
39094 }
39095 }
39096 if (directives === null) {
39097 return null;
39098 }
39099 return directives.find(m => m.ref.node === directiveDeclaration) ?? null;
39100 }
39101 getDirectiveModule(declaration) {
39102 const scope = this.componentScopeReader.getScopeForComponent(declaration);
39103 if (scope === null) {
39104 return null;
39105 }
39106 return scope.ngModule;
39107 }
39108 getSymbolOfBoundEvent(eventBinding) {
39109 const consumer = this.templateData.boundTarget.getConsumerOfBinding(eventBinding);
39110 if (consumer === null) {
39111 return null;
39112 }
39113 // Outputs in the TCB look like one of the two:
39114 // * _t1["outputField"].subscribe(handler);
39115 // * _t1.addEventListener(handler);
39116 // Even with strict null checks disabled, we still produce the access as a separate statement
39117 // so that it can be found here.
39118 let expectedAccess;
39119 if (consumer instanceof Template || consumer instanceof Element$1) {
39120 expectedAccess = 'addEventListener';
39121 }
39122 else {
39123 const bindingPropertyNames = consumer.outputs.getByBindingPropertyName(eventBinding.name);
39124 if (bindingPropertyNames === null || bindingPropertyNames.length === 0) {
39125 return null;
39126 }
39127 // Note that we only get the expectedAccess text from a single consumer of the binding. If
39128 // there are multiple consumers (not supported in the `boundTarget` API) and one of them has
39129 // an alias, it will not get matched here.
39130 expectedAccess = bindingPropertyNames[0].classPropertyName;
39131 }
39132 function filter(n) {
39133 if (!isAccessExpression(n)) {
39134 return false;
39135 }
39136 if (ts__default["default"].isPropertyAccessExpression(n)) {
39137 return n.name.getText() === expectedAccess;
39138 }
39139 else {
39140 return ts__default["default"].isStringLiteral(n.argumentExpression) &&
39141 n.argumentExpression.text === expectedAccess;
39142 }
39143 }
39144 const outputFieldAccesses = findAllMatchingNodes(this.typeCheckBlock, { withSpan: eventBinding.keySpan, filter });
39145 const bindings = [];
39146 for (const outputFieldAccess of outputFieldAccesses) {
39147 if (consumer instanceof Template || consumer instanceof Element$1) {
39148 if (!ts__default["default"].isPropertyAccessExpression(outputFieldAccess)) {
39149 continue;
39150 }
39151 const addEventListener = outputFieldAccess.name;
39152 const tsSymbol = this.getTypeChecker().getSymbolAtLocation(addEventListener);
39153 const tsType = this.getTypeChecker().getTypeAtLocation(addEventListener);
39154 const positionInShimFile = this.getShimPositionForNode(addEventListener);
39155 const target = this.getSymbol(consumer);
39156 if (target === null || tsSymbol === undefined) {
39157 continue;
39158 }
39159 bindings.push({
39160 kind: SymbolKind.Binding,
39161 tsSymbol,
39162 tsType,
39163 target,
39164 shimLocation: { shimPath: this.shimPath, positionInShimFile },
39165 });
39166 }
39167 else {
39168 if (!ts__default["default"].isElementAccessExpression(outputFieldAccess)) {
39169 continue;
39170 }
39171 const tsSymbol = this.getTypeChecker().getSymbolAtLocation(outputFieldAccess.argumentExpression);
39172 if (tsSymbol === undefined) {
39173 continue;
39174 }
39175 const target = this.getDirectiveSymbolForAccessExpression(outputFieldAccess, consumer);
39176 if (target === null) {
39177 continue;
39178 }
39179 const positionInShimFile = this.getShimPositionForNode(outputFieldAccess);
39180 const tsType = this.getTypeChecker().getTypeAtLocation(outputFieldAccess);
39181 bindings.push({
39182 kind: SymbolKind.Binding,
39183 tsSymbol,
39184 tsType,
39185 target,
39186 shimLocation: { shimPath: this.shimPath, positionInShimFile },
39187 });
39188 }
39189 }
39190 if (bindings.length === 0) {
39191 return null;
39192 }
39193 return { kind: SymbolKind.Output, bindings };
39194 }
39195 getSymbolOfInputBinding(binding) {
39196 const consumer = this.templateData.boundTarget.getConsumerOfBinding(binding);
39197 if (consumer === null) {
39198 return null;
39199 }
39200 if (consumer instanceof Element$1 || consumer instanceof Template) {
39201 const host = this.getSymbol(consumer);
39202 return host !== null ? { kind: SymbolKind.DomBinding, host } : null;
39203 }
39204 const nodes = findAllMatchingNodes(this.typeCheckBlock, { withSpan: binding.sourceSpan, filter: isAssignment });
39205 const bindings = [];
39206 for (const node of nodes) {
39207 if (!isAccessExpression(node.left)) {
39208 continue;
39209 }
39210 const symbolInfo = this.getSymbolOfTsNode(node.left);
39211 if (symbolInfo === null || symbolInfo.tsSymbol === null) {
39212 continue;
39213 }
39214 const target = this.getDirectiveSymbolForAccessExpression(node.left, consumer);
39215 if (target === null) {
39216 continue;
39217 }
39218 bindings.push({
39219 ...symbolInfo,
39220 tsSymbol: symbolInfo.tsSymbol,
39221 kind: SymbolKind.Binding,
39222 target,
39223 });
39224 }
39225 if (bindings.length === 0) {
39226 return null;
39227 }
39228 return { kind: SymbolKind.Input, bindings };
39229 }
39230 getDirectiveSymbolForAccessExpression(node, { isComponent, selector, isStructural }) {
39231 // In either case, `_t1["index"]` or `_t1.index`, `node.expression` is _t1.
39232 // The retrieved symbol for _t1 will be the variable declaration.
39233 const tsSymbol = this.getTypeChecker().getSymbolAtLocation(node.expression);
39234 if (tsSymbol?.declarations === undefined || tsSymbol.declarations.length === 0 ||
39235 selector === null) {
39236 return null;
39237 }
39238 const [declaration] = tsSymbol.declarations;
39239 if (!ts__default["default"].isVariableDeclaration(declaration) ||
39240 !hasExpressionIdentifier(
39241 // The expression identifier could be on the type (for regular directives) or the name
39242 // (for generic directives and the ctor op).
39243 declaration.getSourceFile(), declaration.type ?? declaration.name, ExpressionIdentifier.DIRECTIVE)) {
39244 return null;
39245 }
39246 const symbol = this.getSymbolOfTsNode(declaration);
39247 if (symbol === null || !isSymbolWithValueDeclaration(symbol.tsSymbol) ||
39248 !ts__default["default"].isClassDeclaration(symbol.tsSymbol.valueDeclaration)) {
39249 return null;
39250 }
39251 const ngModule = this.getDirectiveModule(symbol.tsSymbol.valueDeclaration);
39252 return {
39253 kind: SymbolKind.Directive,
39254 tsSymbol: symbol.tsSymbol,
39255 tsType: symbol.tsType,
39256 shimLocation: symbol.shimLocation,
39257 isComponent,
39258 isStructural,
39259 selector,
39260 ngModule,
39261 };
39262 }
39263 getSymbolOfVariable(variable) {
39264 const node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: variable.sourceSpan, filter: ts__default["default"].isVariableDeclaration });
39265 if (node === null || node.initializer === undefined) {
39266 return null;
39267 }
39268 const expressionSymbol = this.getSymbolOfTsNode(node.initializer);
39269 if (expressionSymbol === null) {
39270 return null;
39271 }
39272 return {
39273 tsType: expressionSymbol.tsType,
39274 tsSymbol: expressionSymbol.tsSymbol,
39275 initializerLocation: expressionSymbol.shimLocation,
39276 kind: SymbolKind.Variable,
39277 declaration: variable,
39278 localVarLocation: {
39279 shimPath: this.shimPath,
39280 positionInShimFile: this.getShimPositionForNode(node.name),
39281 }
39282 };
39283 }
39284 getSymbolOfReference(ref) {
39285 const target = this.templateData.boundTarget.getReferenceTarget(ref);
39286 // Find the node for the reference declaration, i.e. `var _t2 = _t1;`
39287 let node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: ref.sourceSpan, filter: ts__default["default"].isVariableDeclaration });
39288 if (node === null || target === null || node.initializer === undefined) {
39289 return null;
39290 }
39291 // Get the original declaration for the references variable, with the exception of template refs
39292 // which are of the form var _t3 = (_t2 as any as i2.TemplateRef<any>)
39293 // TODO(atscott): Consider adding an `ExpressionIdentifier` to tag variable declaration
39294 // initializers as invalid for symbol retrieval.
39295 const originalDeclaration = ts__default["default"].isParenthesizedExpression(node.initializer) &&
39296 ts__default["default"].isAsExpression(node.initializer.expression) ?
39297 this.getTypeChecker().getSymbolAtLocation(node.name) :
39298 this.getTypeChecker().getSymbolAtLocation(node.initializer);
39299 if (originalDeclaration === undefined || originalDeclaration.valueDeclaration === undefined) {
39300 return null;
39301 }
39302 const symbol = this.getSymbolOfTsNode(originalDeclaration.valueDeclaration);
39303 if (symbol === null || symbol.tsSymbol === null) {
39304 return null;
39305 }
39306 const referenceVarShimLocation = {
39307 shimPath: this.shimPath,
39308 positionInShimFile: this.getShimPositionForNode(node),
39309 };
39310 if (target instanceof Template || target instanceof Element$1) {
39311 return {
39312 kind: SymbolKind.Reference,
39313 tsSymbol: symbol.tsSymbol,
39314 tsType: symbol.tsType,
39315 target,
39316 declaration: ref,
39317 targetLocation: symbol.shimLocation,
39318 referenceVarLocation: referenceVarShimLocation,
39319 };
39320 }
39321 else {
39322 if (!ts__default["default"].isClassDeclaration(target.directive.ref.node)) {
39323 return null;
39324 }
39325 return {
39326 kind: SymbolKind.Reference,
39327 tsSymbol: symbol.tsSymbol,
39328 tsType: symbol.tsType,
39329 declaration: ref,
39330 target: target.directive.ref.node,
39331 targetLocation: symbol.shimLocation,
39332 referenceVarLocation: referenceVarShimLocation,
39333 };
39334 }
39335 }
39336 getSymbolOfPipe(expression) {
39337 const methodAccess = findFirstMatchingNode(this.typeCheckBlock, { withSpan: expression.nameSpan, filter: ts__default["default"].isPropertyAccessExpression });
39338 if (methodAccess === null) {
39339 return null;
39340 }
39341 const pipeVariableNode = methodAccess.expression;
39342 const pipeDeclaration = this.getTypeChecker().getSymbolAtLocation(pipeVariableNode);
39343 if (pipeDeclaration === undefined || pipeDeclaration.valueDeclaration === undefined) {
39344 return null;
39345 }
39346 const pipeInstance = this.getSymbolOfTsNode(pipeDeclaration.valueDeclaration);
39347 // The instance should never be null, nor should the symbol lack a value declaration. This
39348 // is because the node used to look for the `pipeInstance` symbol info is a value
39349 // declaration of another symbol (i.e. the `pipeDeclaration` symbol).
39350 if (pipeInstance === null || !isSymbolWithValueDeclaration(pipeInstance.tsSymbol)) {
39351 return null;
39352 }
39353 const symbolInfo = this.getSymbolOfTsNode(methodAccess);
39354 if (symbolInfo === null) {
39355 return null;
39356 }
39357 return {
39358 kind: SymbolKind.Pipe,
39359 ...symbolInfo,
39360 classSymbol: {
39361 ...pipeInstance,
39362 tsSymbol: pipeInstance.tsSymbol,
39363 },
39364 };
39365 }
39366 getSymbolOfTemplateExpression(expression) {
39367 if (expression instanceof ASTWithSource) {
39368 expression = expression.ast;
39369 }
39370 const expressionTarget = this.templateData.boundTarget.getExpressionTarget(expression);
39371 if (expressionTarget !== null) {
39372 return this.getSymbol(expressionTarget);
39373 }
39374 let withSpan = expression.sourceSpan;
39375 // The `name` part of a `PropertyWrite` and a non-safe `Call` does not have its own
39376 // AST so there is no way to retrieve a `Symbol` for just the `name` via a specific node.
39377 if (expression instanceof PropertyWrite) {
39378 withSpan = expression.nameSpan;
39379 }
39380 let node = null;
39381 // Property reads in templates usually map to a `PropertyAccessExpression`
39382 // (e.g. `ctx.foo`) so try looking for one first.
39383 if (expression instanceof PropertyRead) {
39384 node = findFirstMatchingNode(this.typeCheckBlock, { withSpan, filter: ts__default["default"].isPropertyAccessExpression });
39385 }
39386 // Otherwise fall back to searching for any AST node.
39387 if (node === null) {
39388 node = findFirstMatchingNode(this.typeCheckBlock, { withSpan, filter: anyNodeFilter });
39389 }
39390 if (node === null) {
39391 return null;
39392 }
39393 while (ts__default["default"].isParenthesizedExpression(node)) {
39394 node = node.expression;
39395 }
39396 // - If we have safe property read ("a?.b") we want to get the Symbol for b, the `whenTrue`
39397 // expression.
39398 // - If our expression is a pipe binding ("a | test:b:c"), we want the Symbol for the
39399 // `transform` on the pipe.
39400 // - Otherwise, we retrieve the symbol for the node itself with no special considerations
39401 if (expression instanceof SafePropertyRead && ts__default["default"].isConditionalExpression(node)) {
39402 const whenTrueSymbol = this.getSymbolOfTsNode(node.whenTrue);
39403 if (whenTrueSymbol === null) {
39404 return null;
39405 }
39406 return {
39407 ...whenTrueSymbol,
39408 kind: SymbolKind.Expression,
39409 // Rather than using the type of only the `whenTrue` part of the expression, we should
39410 // still get the type of the whole conditional expression to include `|undefined`.
39411 tsType: this.getTypeChecker().getTypeAtLocation(node)
39412 };
39413 }
39414 else {
39415 const symbolInfo = this.getSymbolOfTsNode(node);
39416 return symbolInfo === null ? null : { ...symbolInfo, kind: SymbolKind.Expression };
39417 }
39418 }
39419 getSymbolOfTsNode(node) {
39420 while (ts__default["default"].isParenthesizedExpression(node)) {
39421 node = node.expression;
39422 }
39423 let tsSymbol;
39424 if (ts__default["default"].isPropertyAccessExpression(node)) {
39425 tsSymbol = this.getTypeChecker().getSymbolAtLocation(node.name);
39426 }
39427 else if (ts__default["default"].isElementAccessExpression(node)) {
39428 tsSymbol = this.getTypeChecker().getSymbolAtLocation(node.argumentExpression);
39429 }
39430 else {
39431 tsSymbol = this.getTypeChecker().getSymbolAtLocation(node);
39432 }
39433 const positionInShimFile = this.getShimPositionForNode(node);
39434 const type = this.getTypeChecker().getTypeAtLocation(node);
39435 return {
39436 // If we could not find a symbol, fall back to the symbol on the type for the node.
39437 // Some nodes won't have a "symbol at location" but will have a symbol for the type.
39438 // Examples of this would be literals and `document.createElement('div')`.
39439 tsSymbol: tsSymbol ?? type.symbol ?? null,
39440 tsType: type,
39441 shimLocation: { shimPath: this.shimPath, positionInShimFile },
39442 };
39443 }
39444 getShimPositionForNode(node) {
39445 if (ts__default["default"].isTypeReferenceNode(node)) {
39446 return this.getShimPositionForNode(node.typeName);
39447 }
39448 else if (ts__default["default"].isQualifiedName(node)) {
39449 return node.right.getStart();
39450 }
39451 else if (ts__default["default"].isPropertyAccessExpression(node)) {
39452 return node.name.getStart();
39453 }
39454 else if (ts__default["default"].isElementAccessExpression(node)) {
39455 return node.argumentExpression.getStart();
39456 }
39457 else {
39458 return node.getStart();
39459 }
39460 }
39461 }
39462 /** Filter predicate function that matches any AST node. */
39463 function anyNodeFilter(n) {
39464 return true;
39465 }
39466 function sourceSpanEqual(a, b) {
39467 return a.start.offset === b.start.offset && a.end.offset === b.end.offset;
39468 }
39469
39470 /**
39471 * @license
39472 * Copyright Google LLC All Rights Reserved.
39473 *
39474 * Use of this source code is governed by an MIT-style license that can be
39475 * found in the LICENSE file at https://angular.io/license
39476 */
39477 const REGISTRY = new DomElementSchemaRegistry();
39478 /**
39479 * Primary template type-checking engine, which performs type-checking using a
39480 * `TypeCheckingProgramStrategy` for type-checking program maintenance, and the
39481 * `ProgramTypeCheckAdapter` for generation of template type-checking code.
39482 */
39483 class TemplateTypeCheckerImpl {
39484 constructor(originalProgram, programDriver, typeCheckAdapter, config, refEmitter, reflector, compilerHost, priorBuild, componentScopeReader, typeCheckScopeRegistry, perf) {
39485 this.originalProgram = originalProgram;
39486 this.programDriver = programDriver;
39487 this.typeCheckAdapter = typeCheckAdapter;
39488 this.config = config;
39489 this.refEmitter = refEmitter;
39490 this.reflector = reflector;
39491 this.compilerHost = compilerHost;
39492 this.priorBuild = priorBuild;
39493 this.componentScopeReader = componentScopeReader;
39494 this.typeCheckScopeRegistry = typeCheckScopeRegistry;
39495 this.perf = perf;
39496 this.state = new Map();
39497 /**
39498 * Stores the `CompletionEngine` which powers autocompletion for each component class.
39499 *
39500 * Must be invalidated whenever the component's template or the `ts.Program` changes. Invalidation
39501 * on template changes is performed within this `TemplateTypeCheckerImpl` instance. When the
39502 * `ts.Program` changes, the `TemplateTypeCheckerImpl` as a whole is destroyed and replaced.
39503 */
39504 this.completionCache = new Map();
39505 /**
39506 * Stores the `SymbolBuilder` which creates symbols for each component class.
39507 *
39508 * Must be invalidated whenever the component's template or the `ts.Program` changes. Invalidation
39509 * on template changes is performed within this `TemplateTypeCheckerImpl` instance. When the
39510 * `ts.Program` changes, the `TemplateTypeCheckerImpl` as a whole is destroyed and replaced.
39511 */
39512 this.symbolBuilderCache = new Map();
39513 /**
39514 * Stores directives and pipes that are in scope for each component.
39515 *
39516 * Unlike other caches, the scope of a component is not affected by its template. It will be
39517 * destroyed when the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is
39518 * destroyed and replaced.
39519 */
39520 this.scopeCache = new Map();
39521 /**
39522 * Stores potential element tags for each component (a union of DOM tags as well as directive
39523 * tags).
39524 *
39525 * Unlike other caches, the scope of a component is not affected by its template. It will be
39526 * destroyed when the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is
39527 * destroyed and replaced.
39528 */
39529 this.elementTagCache = new Map();
39530 this.isComplete = false;
39531 }
39532 getTemplate(component) {
39533 const { data } = this.getLatestComponentState(component);
39534 if (data === null) {
39535 return null;
39536 }
39537 return data.template;
39538 }
39539 getLatestComponentState(component) {
39540 this.ensureShimForComponent(component);
39541 const sf = component.getSourceFile();
39542 const sfPath = absoluteFromSourceFile(sf);
39543 const shimPath = TypeCheckShimGenerator.shimFor(sfPath);
39544 const fileRecord = this.getFileData(sfPath);
39545 if (!fileRecord.shimData.has(shimPath)) {
39546 return { data: null, tcb: null, shimPath };
39547 }
39548 const templateId = fileRecord.sourceManager.getTemplateId(component);
39549 const shimRecord = fileRecord.shimData.get(shimPath);
39550 const id = fileRecord.sourceManager.getTemplateId(component);
39551 const program = this.programDriver.getProgram();
39552 const shimSf = getSourceFileOrNull(program, shimPath);
39553 if (shimSf === null || !fileRecord.shimData.has(shimPath)) {
39554 throw new Error(`Error: no shim file in program: ${shimPath}`);
39555 }
39556 let tcb = findTypeCheckBlock(shimSf, id, /*isDiagnosticsRequest*/ false);
39557 if (tcb === null) {
39558 // Try for an inline block.
39559 const inlineSf = getSourceFileOrError(program, sfPath);
39560 tcb = findTypeCheckBlock(inlineSf, id, /*isDiagnosticsRequest*/ false);
39561 }
39562 let data = null;
39563 if (shimRecord.templates.has(templateId)) {
39564 data = shimRecord.templates.get(templateId);
39565 }
39566 return { data, tcb, shimPath };
39567 }
39568 isTrackedTypeCheckFile(filePath) {
39569 return this.getFileAndShimRecordsForPath(filePath) !== null;
39570 }
39571 getFileAndShimRecordsForPath(shimPath) {
39572 for (const fileRecord of this.state.values()) {
39573 if (fileRecord.shimData.has(shimPath)) {
39574 return { fileRecord, shimRecord: fileRecord.shimData.get(shimPath) };
39575 }
39576 }
39577 return null;
39578 }
39579 getTemplateMappingAtShimLocation({ shimPath, positionInShimFile }) {
39580 const records = this.getFileAndShimRecordsForPath(absoluteFrom(shimPath));
39581 if (records === null) {
39582 return null;
39583 }
39584 const { fileRecord } = records;
39585 const shimSf = this.programDriver.getProgram().getSourceFile(absoluteFrom(shimPath));
39586 if (shimSf === undefined) {
39587 return null;
39588 }
39589 return getTemplateMapping(shimSf, positionInShimFile, fileRecord.sourceManager, /*isDiagnosticsRequest*/ false);
39590 }
39591 generateAllTypeCheckBlocks() {
39592 this.ensureAllShimsForAllFiles();
39593 }
39594 /**
39595 * Retrieve type-checking and template parse diagnostics from the given `ts.SourceFile` using the
39596 * most recent type-checking program.
39597 */
39598 getDiagnosticsForFile(sf, optimizeFor) {
39599 switch (optimizeFor) {
39600 case OptimizeFor.WholeProgram:
39601 this.ensureAllShimsForAllFiles();
39602 break;
39603 case OptimizeFor.SingleFile:
39604 this.ensureAllShimsForOneFile(sf);
39605 break;
39606 }
39607 return this.perf.inPhase(PerfPhase.TtcDiagnostics, () => {
39608 const sfPath = absoluteFromSourceFile(sf);
39609 const fileRecord = this.state.get(sfPath);
39610 const typeCheckProgram = this.programDriver.getProgram();
39611 const diagnostics = [];
39612 if (fileRecord.hasInlines) {
39613 const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
39614 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
39615 }
39616 for (const [shimPath, shimRecord] of fileRecord.shimData) {
39617 const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
39618 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
39619 diagnostics.push(...shimRecord.genesisDiagnostics);
39620 for (const templateData of shimRecord.templates.values()) {
39621 diagnostics.push(...templateData.templateDiagnostics);
39622 }
39623 }
39624 return diagnostics.filter((diag) => diag !== null);
39625 });
39626 }
39627 getDiagnosticsForComponent(component) {
39628 this.ensureShimForComponent(component);
39629 return this.perf.inPhase(PerfPhase.TtcDiagnostics, () => {
39630 const sf = component.getSourceFile();
39631 const sfPath = absoluteFromSourceFile(sf);
39632 const shimPath = TypeCheckShimGenerator.shimFor(sfPath);
39633 const fileRecord = this.getFileData(sfPath);
39634 if (!fileRecord.shimData.has(shimPath)) {
39635 return [];
39636 }
39637 const templateId = fileRecord.sourceManager.getTemplateId(component);
39638 const shimRecord = fileRecord.shimData.get(shimPath);
39639 const typeCheckProgram = this.programDriver.getProgram();
39640 const diagnostics = [];
39641 if (shimRecord.hasInlines) {
39642 const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
39643 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
39644 }
39645 const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
39646 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
39647 diagnostics.push(...shimRecord.genesisDiagnostics);
39648 for (const templateData of shimRecord.templates.values()) {
39649 diagnostics.push(...templateData.templateDiagnostics);
39650 }
39651 return diagnostics.filter((diag) => diag !== null && diag.templateId === templateId);
39652 });
39653 }
39654 getTypeCheckBlock(component) {
39655 return this.getLatestComponentState(component).tcb;
39656 }
39657 getGlobalCompletions(context, component, node) {
39658 const engine = this.getOrCreateCompletionEngine(component);
39659 if (engine === null) {
39660 return null;
39661 }
39662 return this.perf.inPhase(PerfPhase.TtcAutocompletion, () => engine.getGlobalCompletions(context, node));
39663 }
39664 getExpressionCompletionLocation(ast, component) {
39665 const engine = this.getOrCreateCompletionEngine(component);
39666 if (engine === null) {
39667 return null;
39668 }
39669 return this.perf.inPhase(PerfPhase.TtcAutocompletion, () => engine.getExpressionCompletionLocation(ast));
39670 }
39671 getLiteralCompletionLocation(node, component) {
39672 const engine = this.getOrCreateCompletionEngine(component);
39673 if (engine === null) {
39674 return null;
39675 }
39676 return this.perf.inPhase(PerfPhase.TtcAutocompletion, () => engine.getLiteralCompletionLocation(node));
39677 }
39678 invalidateClass(clazz) {
39679 this.completionCache.delete(clazz);
39680 this.symbolBuilderCache.delete(clazz);
39681 this.scopeCache.delete(clazz);
39682 this.elementTagCache.delete(clazz);
39683 const sf = clazz.getSourceFile();
39684 const sfPath = absoluteFromSourceFile(sf);
39685 const shimPath = TypeCheckShimGenerator.shimFor(sfPath);
39686 const fileData = this.getFileData(sfPath);
39687 fileData.sourceManager.getTemplateId(clazz);
39688 fileData.shimData.delete(shimPath);
39689 fileData.isComplete = false;
39690 this.isComplete = false;
39691 }
39692 makeTemplateDiagnostic(clazz, sourceSpan, category, errorCode, message, relatedInformation) {
39693 const sfPath = absoluteFromSourceFile(clazz.getSourceFile());
39694 const fileRecord = this.state.get(sfPath);
39695 const templateId = fileRecord.sourceManager.getTemplateId(clazz);
39696 const mapping = fileRecord.sourceManager.getSourceMapping(templateId);
39697 return {
39698 ...makeTemplateDiagnostic(templateId, mapping, sourceSpan, category, ngErrorCode(errorCode), message, relatedInformation),
39699 __ngCode: errorCode
39700 };
39701 }
39702 getOrCreateCompletionEngine(component) {
39703 if (this.completionCache.has(component)) {
39704 return this.completionCache.get(component);
39705 }
39706 const { tcb, data, shimPath } = this.getLatestComponentState(component);
39707 if (tcb === null || data === null) {
39708 return null;
39709 }
39710 const engine = new CompletionEngine(tcb, data, shimPath);
39711 this.completionCache.set(component, engine);
39712 return engine;
39713 }
39714 maybeAdoptPriorResultsForFile(sf) {
39715 const sfPath = absoluteFromSourceFile(sf);
39716 if (this.state.has(sfPath)) {
39717 const existingResults = this.state.get(sfPath);
39718 if (existingResults.isComplete) {
39719 // All data for this file has already been generated, so no need to adopt anything.
39720 return;
39721 }
39722 }
39723 const previousResults = this.priorBuild.priorTypeCheckingResultsFor(sf);
39724 if (previousResults === null || !previousResults.isComplete) {
39725 return;
39726 }
39727 this.perf.eventCount(PerfEvent.ReuseTypeCheckFile);
39728 this.state.set(sfPath, previousResults);
39729 }
39730 ensureAllShimsForAllFiles() {
39731 if (this.isComplete) {
39732 return;
39733 }
39734 this.perf.inPhase(PerfPhase.TcbGeneration, () => {
39735 const host = new WholeProgramTypeCheckingHost(this);
39736 const ctx = this.newContext(host);
39737 for (const sf of this.originalProgram.getSourceFiles()) {
39738 if (sf.isDeclarationFile || isShim(sf)) {
39739 continue;
39740 }
39741 this.maybeAdoptPriorResultsForFile(sf);
39742 const sfPath = absoluteFromSourceFile(sf);
39743 const fileData = this.getFileData(sfPath);
39744 if (fileData.isComplete) {
39745 continue;
39746 }
39747 this.typeCheckAdapter.typeCheck(sf, ctx);
39748 fileData.isComplete = true;
39749 }
39750 this.updateFromContext(ctx);
39751 this.isComplete = true;
39752 });
39753 }
39754 ensureAllShimsForOneFile(sf) {
39755 this.perf.inPhase(PerfPhase.TcbGeneration, () => {
39756 this.maybeAdoptPriorResultsForFile(sf);
39757 const sfPath = absoluteFromSourceFile(sf);
39758 const fileData = this.getFileData(sfPath);
39759 if (fileData.isComplete) {
39760 // All data for this file is present and accounted for already.
39761 return;
39762 }
39763 const host = new SingleFileTypeCheckingHost(sfPath, fileData, this);
39764 const ctx = this.newContext(host);
39765 this.typeCheckAdapter.typeCheck(sf, ctx);
39766 fileData.isComplete = true;
39767 this.updateFromContext(ctx);
39768 });
39769 }
39770 ensureShimForComponent(component) {
39771 const sf = component.getSourceFile();
39772 const sfPath = absoluteFromSourceFile(sf);
39773 const shimPath = TypeCheckShimGenerator.shimFor(sfPath);
39774 this.maybeAdoptPriorResultsForFile(sf);
39775 const fileData = this.getFileData(sfPath);
39776 if (fileData.shimData.has(shimPath)) {
39777 // All data for this component is available.
39778 return;
39779 }
39780 const host = new SingleShimTypeCheckingHost(sfPath, fileData, this, shimPath);
39781 const ctx = this.newContext(host);
39782 this.typeCheckAdapter.typeCheck(sf, ctx);
39783 this.updateFromContext(ctx);
39784 }
39785 newContext(host) {
39786 const inlining = this.programDriver.supportsInlineOperations ? InliningMode.InlineOps : InliningMode.Error;
39787 return new TypeCheckContextImpl(this.config, this.compilerHost, this.refEmitter, this.reflector, host, inlining, this.perf);
39788 }
39789 /**
39790 * Remove any shim data that depends on inline operations applied to the type-checking program.
39791 *
39792 * This can be useful if new inlines need to be applied, and it's not possible to guarantee that
39793 * they won't overwrite or corrupt existing inlines that are used by such shims.
39794 */
39795 clearAllShimDataUsingInlines() {
39796 for (const fileData of this.state.values()) {
39797 if (!fileData.hasInlines) {
39798 continue;
39799 }
39800 for (const [shimFile, shimData] of fileData.shimData.entries()) {
39801 if (shimData.hasInlines) {
39802 fileData.shimData.delete(shimFile);
39803 }
39804 }
39805 fileData.hasInlines = false;
39806 fileData.isComplete = false;
39807 this.isComplete = false;
39808 }
39809 }
39810 updateFromContext(ctx) {
39811 const updates = ctx.finalize();
39812 return this.perf.inPhase(PerfPhase.TcbUpdateProgram, () => {
39813 if (updates.size > 0) {
39814 this.perf.eventCount(PerfEvent.UpdateTypeCheckProgram);
39815 }
39816 this.programDriver.updateFiles(updates, UpdateMode.Incremental);
39817 this.priorBuild.recordSuccessfulTypeCheck(this.state);
39818 this.perf.memory(PerfCheckpoint.TtcUpdateProgram);
39819 });
39820 }
39821 getFileData(path) {
39822 if (!this.state.has(path)) {
39823 this.state.set(path, {
39824 hasInlines: false,
39825 sourceManager: new TemplateSourceManager(),
39826 isComplete: false,
39827 shimData: new Map(),
39828 });
39829 }
39830 return this.state.get(path);
39831 }
39832 getSymbolOfNode(node, component) {
39833 const builder = this.getOrCreateSymbolBuilder(component);
39834 if (builder === null) {
39835 return null;
39836 }
39837 return this.perf.inPhase(PerfPhase.TtcSymbol, () => builder.getSymbol(node));
39838 }
39839 getOrCreateSymbolBuilder(component) {
39840 if (this.symbolBuilderCache.has(component)) {
39841 return this.symbolBuilderCache.get(component);
39842 }
39843 const { tcb, data, shimPath } = this.getLatestComponentState(component);
39844 if (tcb === null || data === null) {
39845 return null;
39846 }
39847 const builder = new SymbolBuilder(shimPath, tcb, data, this.componentScopeReader, () => this.programDriver.getProgram().getTypeChecker());
39848 this.symbolBuilderCache.set(component, builder);
39849 return builder;
39850 }
39851 getDirectivesInScope(component) {
39852 const data = this.getScopeData(component);
39853 if (data === null) {
39854 return null;
39855 }
39856 return data.directives;
39857 }
39858 getPipesInScope(component) {
39859 const data = this.getScopeData(component);
39860 if (data === null) {
39861 return null;
39862 }
39863 return data.pipes;
39864 }
39865 getDirectiveMetadata(dir) {
39866 if (!isNamedClassDeclaration(dir)) {
39867 return null;
39868 }
39869 return this.typeCheckScopeRegistry.getTypeCheckDirectiveMetadata(new Reference(dir));
39870 }
39871 getPotentialElementTags(component) {
39872 if (this.elementTagCache.has(component)) {
39873 return this.elementTagCache.get(component);
39874 }
39875 const tagMap = new Map();
39876 for (const tag of REGISTRY.allKnownElementNames()) {
39877 tagMap.set(tag, null);
39878 }
39879 const scope = this.getScopeData(component);
39880 if (scope !== null) {
39881 for (const directive of scope.directives) {
39882 for (const selector of CssSelector.parse(directive.selector)) {
39883 if (selector.element === null || tagMap.has(selector.element)) {
39884 // Skip this directive if it doesn't match an element tag, or if another directive has
39885 // already been included with the same element name.
39886 continue;
39887 }
39888 tagMap.set(selector.element, directive);
39889 }
39890 }
39891 }
39892 this.elementTagCache.set(component, tagMap);
39893 return tagMap;
39894 }
39895 getPotentialDomBindings(tagName) {
39896 const attributes = REGISTRY.allKnownAttributesOfElement(tagName);
39897 return attributes.map(attribute => ({
39898 attribute,
39899 property: REGISTRY.getMappedPropName(attribute),
39900 }));
39901 }
39902 getPotentialDomEvents(tagName) {
39903 return REGISTRY.allKnownEventsOfElement(tagName);
39904 }
39905 getScopeData(component) {
39906 if (this.scopeCache.has(component)) {
39907 return this.scopeCache.get(component);
39908 }
39909 if (!isNamedClassDeclaration(component)) {
39910 throw new Error(`AssertionError: components must have names`);
39911 }
39912 const scope = this.componentScopeReader.getScopeForComponent(component);
39913 if (scope === null) {
39914 return null;
39915 }
39916 const data = {
39917 directives: [],
39918 pipes: [],
39919 isPoisoned: scope.compilation.isPoisoned,
39920 };
39921 const typeChecker = this.programDriver.getProgram().getTypeChecker();
39922 for (const dir of scope.compilation.directives) {
39923 if (dir.selector === null) {
39924 // Skip this directive, it can't be added to a template anyway.
39925 continue;
39926 }
39927 const tsSymbol = typeChecker.getSymbolAtLocation(dir.ref.node.name);
39928 if (!isSymbolWithValueDeclaration(tsSymbol)) {
39929 continue;
39930 }
39931 let ngModule = null;
39932 const moduleScopeOfDir = this.componentScopeReader.getScopeForComponent(dir.ref.node);
39933 if (moduleScopeOfDir !== null) {
39934 ngModule = moduleScopeOfDir.ngModule;
39935 }
39936 data.directives.push({
39937 isComponent: dir.isComponent,
39938 isStructural: dir.isStructural,
39939 selector: dir.selector,
39940 tsSymbol,
39941 ngModule,
39942 });
39943 }
39944 for (const pipe of scope.compilation.pipes) {
39945 const tsSymbol = typeChecker.getSymbolAtLocation(pipe.ref.node.name);
39946 if (tsSymbol === undefined) {
39947 continue;
39948 }
39949 data.pipes.push({
39950 name: pipe.name,
39951 tsSymbol,
39952 });
39953 }
39954 this.scopeCache.set(component, data);
39955 return data;
39956 }
39957 }
39958 function convertDiagnostic(diag, sourceResolver) {
39959 if (!shouldReportDiagnostic(diag)) {
39960 return null;
39961 }
39962 return translateDiagnostic(diag, sourceResolver);
39963 }
39964 /**
39965 * Drives a `TypeCheckContext` to generate type-checking code for every component in the program.
39966 */
39967 class WholeProgramTypeCheckingHost {
39968 constructor(impl) {
39969 this.impl = impl;
39970 }
39971 getSourceManager(sfPath) {
39972 return this.impl.getFileData(sfPath).sourceManager;
39973 }
39974 shouldCheckComponent(node) {
39975 const sfPath = absoluteFromSourceFile(node.getSourceFile());
39976 const shimPath = TypeCheckShimGenerator.shimFor(sfPath);
39977 const fileData = this.impl.getFileData(sfPath);
39978 // The component needs to be checked unless the shim which would contain it already exists.
39979 return !fileData.shimData.has(shimPath);
39980 }
39981 recordShimData(sfPath, data) {
39982 const fileData = this.impl.getFileData(sfPath);
39983 fileData.shimData.set(data.path, data);
39984 if (data.hasInlines) {
39985 fileData.hasInlines = true;
39986 }
39987 }
39988 recordComplete(sfPath) {
39989 this.impl.getFileData(sfPath).isComplete = true;
39990 }
39991 }
39992 /**
39993 * Drives a `TypeCheckContext` to generate type-checking code efficiently for a single input file.
39994 */
39995 class SingleFileTypeCheckingHost {
39996 constructor(sfPath, fileData, impl) {
39997 this.sfPath = sfPath;
39998 this.fileData = fileData;
39999 this.impl = impl;
40000 this.seenInlines = false;
40001 }
40002 assertPath(sfPath) {
40003 if (this.sfPath !== sfPath) {
40004 throw new Error(`AssertionError: querying TypeCheckingHost outside of assigned file`);
40005 }
40006 }
40007 getSourceManager(sfPath) {
40008 this.assertPath(sfPath);
40009 return this.fileData.sourceManager;
40010 }
40011 shouldCheckComponent(node) {
40012 if (this.sfPath !== absoluteFromSourceFile(node.getSourceFile())) {
40013 return false;
40014 }
40015 const shimPath = TypeCheckShimGenerator.shimFor(this.sfPath);
40016 // Only need to generate a TCB for the class if no shim exists for it currently.
40017 return !this.fileData.shimData.has(shimPath);
40018 }
40019 recordShimData(sfPath, data) {
40020 this.assertPath(sfPath);
40021 // Previous type-checking state may have required the use of inlines (assuming they were
40022 // supported). If the current operation also requires inlines, this presents a problem:
40023 // generating new inlines may invalidate any old inlines that old state depends on.
40024 //
40025 // Rather than resolve this issue by tracking specific dependencies on inlines, if the new state
40026 // relies on inlines, any old state that relied on them is simply cleared. This happens when the
40027 // first new state that uses inlines is encountered.
40028 if (data.hasInlines && !this.seenInlines) {
40029 this.impl.clearAllShimDataUsingInlines();
40030 this.seenInlines = true;
40031 }
40032 this.fileData.shimData.set(data.path, data);
40033 if (data.hasInlines) {
40034 this.fileData.hasInlines = true;
40035 }
40036 }
40037 recordComplete(sfPath) {
40038 this.assertPath(sfPath);
40039 this.fileData.isComplete = true;
40040 }
40041 }
40042 /**
40043 * Drives a `TypeCheckContext` to generate type-checking code efficiently for only those components
40044 * which map to a single shim of a single input file.
40045 */
40046 class SingleShimTypeCheckingHost extends SingleFileTypeCheckingHost {
40047 constructor(sfPath, fileData, impl, shimPath) {
40048 super(sfPath, fileData, impl);
40049 this.shimPath = shimPath;
40050 }
40051 shouldCheckNode(node) {
40052 if (this.sfPath !== absoluteFromSourceFile(node.getSourceFile())) {
40053 return false;
40054 }
40055 // Only generate a TCB for the component if it maps to the requested shim file.
40056 const shimPath = TypeCheckShimGenerator.shimFor(this.sfPath);
40057 if (shimPath !== this.shimPath) {
40058 return false;
40059 }
40060 // Only need to generate a TCB for the class if no shim exists for it currently.
40061 return !this.fileData.shimData.has(shimPath);
40062 }
40063 }
40064
40065 /**
40066 * @license
40067 * Copyright Google LLC All Rights Reserved.
40068 *
40069 * Use of this source code is governed by an MIT-style license that can be
40070 * found in the LICENSE file at https://angular.io/license
40071 */
40072 class ExtendedTemplateCheckerImpl {
40073 constructor(templateTypeChecker, typeChecker, templateChecks) {
40074 this.templateChecks = templateChecks;
40075 this.ctx = { templateTypeChecker: templateTypeChecker, typeChecker: typeChecker };
40076 }
40077 getDiagnosticsForComponent(component) {
40078 const template = this.ctx.templateTypeChecker.getTemplate(component);
40079 // Skip checks if component has no template. This can happen if the user writes a
40080 // `@Component()` but doesn't add the template, could happen in the language service
40081 // when users are in the middle of typing code.
40082 if (template === null) {
40083 return [];
40084 }
40085 const diagnostics = [];
40086 for (const check of this.templateChecks) {
40087 diagnostics.push(...check.run(this.ctx, component, template));
40088 }
40089 return diagnostics;
40090 }
40091 }
40092
40093 /**
40094 * @license
40095 * Copyright Google LLC All Rights Reserved.
40096 *
40097 * Use of this source code is governed by an MIT-style license that can be
40098 * found in the LICENSE file at https://angular.io/license
40099 */
40100 /**
40101 * This abstract class provides a base implementation for the run method.
40102 */
40103 class TemplateCheckWithVisitor {
40104 /**
40105 * Base implementation for run function, visits all nodes in template and calls
40106 * `visitNode()` for each one.
40107 */
40108 run(ctx, component, template) {
40109 const visitor = new TemplateVisitor(ctx, component, this);
40110 return visitor.getDiagnostics(template);
40111 }
40112 }
40113 /**
40114 * Visits all nodes in a template (TmplAstNode and AST) and calls `visitNode` for each one.
40115 */
40116 class TemplateVisitor extends RecursiveAstVisitor {
40117 constructor(ctx, component, check) {
40118 super();
40119 this.ctx = ctx;
40120 this.component = component;
40121 this.check = check;
40122 this.diagnostics = [];
40123 }
40124 visit(node, context) {
40125 this.diagnostics.push(...this.check.visitNode(this.ctx, this.component, node));
40126 node.visit(this);
40127 }
40128 visitAllNodes(nodes) {
40129 for (const node of nodes) {
40130 this.visit(node);
40131 }
40132 }
40133 visitAst(ast) {
40134 if (ast instanceof ASTWithSource) {
40135 ast = ast.ast;
40136 }
40137 this.visit(ast);
40138 }
40139 visitElement(element) {
40140 this.visitAllNodes(element.attributes);
40141 this.visitAllNodes(element.inputs);
40142 this.visitAllNodes(element.outputs);
40143 this.visitAllNodes(element.references);
40144 this.visitAllNodes(element.children);
40145 }
40146 visitTemplate(template) {
40147 this.visitAllNodes(template.attributes);
40148 if (template.tagName === 'ng-template') {
40149 // Only visit input/outputs/templateAttrs if this isn't an inline template node
40150 // generated for a structural directive (like `<div *ngIf></div>`). These nodes
40151 // would be visited when the underlying element of an inline template node is processed.
40152 this.visitAllNodes(template.inputs);
40153 this.visitAllNodes(template.outputs);
40154 this.visitAllNodes(template.templateAttrs);
40155 }
40156 this.visitAllNodes(template.variables);
40157 this.visitAllNodes(template.references);
40158 this.visitAllNodes(template.children);
40159 }
40160 visitContent(content) { }
40161 visitVariable(variable) { }
40162 visitReference(reference) { }
40163 visitTextAttribute(attribute) { }
40164 visitBoundAttribute(attribute) {
40165 this.visitAst(attribute.value);
40166 }
40167 visitBoundEvent(attribute) {
40168 this.visitAst(attribute.handler);
40169 }
40170 visitText(text) { }
40171 visitBoundText(text) {
40172 this.visitAst(text.value);
40173 }
40174 visitIcu(icu) { }
40175 getDiagnostics(template) {
40176 this.diagnostics = [];
40177 this.visitAllNodes(template);
40178 return this.diagnostics;
40179 }
40180 }
40181
40182 /**
40183 * @license
40184 * Copyright Google LLC All Rights Reserved.
40185 *
40186 * Use of this source code is governed by an MIT-style license that can be
40187 * found in the LICENSE file at https://angular.io/license
40188 */
40189 /**
40190 * Ensures the two-way binding syntax is correct.
40191 * Parentheses should be inside the brackets "[()]".
40192 * Will return diagnostic information when "([])" is found.
40193 */
40194 class InvalidBananaInBoxCheck extends TemplateCheckWithVisitor {
40195 constructor() {
40196 super(...arguments);
40197 this.code = ErrorCode.INVALID_BANANA_IN_BOX;
40198 }
40199 visitNode(ctx, component, node) {
40200 if (!(node instanceof BoundEvent))
40201 return [];
40202 const name = node.name;
40203 if (!name.startsWith('[') || !name.endsWith(']'))
40204 return [];
40205 const boundSyntax = node.sourceSpan.toString();
40206 const expectedBoundSyntax = boundSyntax.replace(`(${name})`, `[(${name.slice(1, -1)})]`);
40207 const diagnostic = ctx.templateTypeChecker.makeTemplateDiagnostic(component, node.sourceSpan, ts__default["default"].DiagnosticCategory.Warning, ErrorCode.INVALID_BANANA_IN_BOX, `In the two-way binding syntax the parentheses should be inside the brackets, ex. '${expectedBoundSyntax}'.
40208 Find more at https://angular.io/guide/two-way-binding`);
40209 return [diagnostic];
40210 }
40211 }
40212
40213 /**
40214 * @license
40215 * Copyright Google LLC All Rights Reserved.
40216 *
40217 * Use of this source code is governed by an MIT-style license that can be
40218 * found in the LICENSE file at https://angular.io/license
40219 */
40220 /**
40221 * Ensures the left side of a nullish coalescing operation is nullable.
40222 * Returns diagnostics for the cases where the operator is useless.
40223 * This check should only be use if `strictNullChecks` is enabled,
40224 * otherwise it would produce inaccurate results.
40225 */
40226 class NullishCoalescingNotNullableCheck extends TemplateCheckWithVisitor {
40227 constructor() {
40228 super(...arguments);
40229 this.code = ErrorCode.NULLISH_COALESCING_NOT_NULLABLE;
40230 }
40231 visitNode(ctx, component, node) {
40232 if (!(node instanceof Binary) || node.operation !== '??')
40233 return [];
40234 const symbolLeft = ctx.templateTypeChecker.getSymbolOfNode(node.left, component);
40235 if (symbolLeft === null || symbolLeft.kind !== SymbolKind.Expression) {
40236 return [];
40237 }
40238 const typeLeft = symbolLeft.tsType;
40239 // If the left operand's type is different from its non-nullable self, then it must
40240 // contain a null or undefined so this nullish coalescing operator is useful. No diagnostic to
40241 // report.
40242 if (typeLeft.getNonNullableType() !== typeLeft)
40243 return [];
40244 const symbol = ctx.templateTypeChecker.getSymbolOfNode(node, component);
40245 if (symbol.kind !== SymbolKind.Expression) {
40246 return [];
40247 }
40248 const span = ctx.templateTypeChecker.getTemplateMappingAtShimLocation(symbol.shimLocation).span;
40249 const diagnostic = ctx.templateTypeChecker.makeTemplateDiagnostic(component, span, ts__default["default"].DiagnosticCategory.Warning, ErrorCode.NULLISH_COALESCING_NOT_NULLABLE, `The left side of this nullish coalescing operation does not include 'null' or 'undefined' in its type, therefore the '??' operator can be safely removed.`);
40250 return [diagnostic];
40251 }
40252 }
40253
40254 /**
40255 * @license
40256 * Copyright Google LLC All Rights Reserved.
40257 *
40258 * Use of this source code is governed by an MIT-style license that can be
40259 * found in the LICENSE file at https://angular.io/license
40260 */
40261 /**
40262 * Discriminant type for a `CompilationTicket`.
40263 */
40264 var CompilationTicketKind;
40265 (function (CompilationTicketKind) {
40266 CompilationTicketKind[CompilationTicketKind["Fresh"] = 0] = "Fresh";
40267 CompilationTicketKind[CompilationTicketKind["IncrementalTypeScript"] = 1] = "IncrementalTypeScript";
40268 CompilationTicketKind[CompilationTicketKind["IncrementalResource"] = 2] = "IncrementalResource";
40269 })(CompilationTicketKind || (CompilationTicketKind = {}));
40270 /**
40271 * Create a `CompilationTicket` for a brand new compilation, using no prior state.
40272 */
40273 function freshCompilationTicket(tsProgram, options, incrementalBuildStrategy, programDriver, perfRecorder, enableTemplateTypeChecker, usePoisonedData) {
40274 return {
40275 kind: CompilationTicketKind.Fresh,
40276 tsProgram,
40277 options,
40278 incrementalBuildStrategy,
40279 programDriver,
40280 enableTemplateTypeChecker,
40281 usePoisonedData,
40282 perfRecorder: perfRecorder ?? ActivePerfRecorder.zeroedToNow(),
40283 };
40284 }
40285 /**
40286 * Create a `CompilationTicket` as efficiently as possible, based on a previous `NgCompiler`
40287 * instance and a new `ts.Program`.
40288 */
40289 function incrementalFromCompilerTicket(oldCompiler, newProgram, incrementalBuildStrategy, programDriver, modifiedResourceFiles, perfRecorder) {
40290 const oldProgram = oldCompiler.getCurrentProgram();
40291 const oldState = oldCompiler.incrementalStrategy.getIncrementalState(oldProgram);
40292 if (oldState === null) {
40293 // No incremental step is possible here, since no IncrementalDriver was found for the old
40294 // program.
40295 return freshCompilationTicket(newProgram, oldCompiler.options, incrementalBuildStrategy, programDriver, perfRecorder, oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData);
40296 }
40297 if (perfRecorder === null) {
40298 perfRecorder = ActivePerfRecorder.zeroedToNow();
40299 }
40300 const incrementalCompilation = IncrementalCompilation.incremental(newProgram, versionMapFromProgram(newProgram, programDriver), oldProgram, oldState, modifiedResourceFiles, perfRecorder);
40301 return {
40302 kind: CompilationTicketKind.IncrementalTypeScript,
40303 enableTemplateTypeChecker: oldCompiler.enableTemplateTypeChecker,
40304 usePoisonedData: oldCompiler.usePoisonedData,
40305 options: oldCompiler.options,
40306 incrementalBuildStrategy,
40307 incrementalCompilation,
40308 programDriver,
40309 newProgram,
40310 perfRecorder,
40311 };
40312 }
40313 function resourceChangeTicket(compiler, modifiedResourceFiles) {
40314 return {
40315 kind: CompilationTicketKind.IncrementalResource,
40316 compiler,
40317 modifiedResourceFiles,
40318 perfRecorder: ActivePerfRecorder.zeroedToNow(),
40319 };
40320 }
40321 /**
40322 * The heart of the Angular Ivy compiler.
40323 *
40324 * The `NgCompiler` provides an API for performing Angular compilation within a custom TypeScript
40325 * compiler. Each instance of `NgCompiler` supports a single compilation, which might be
40326 * incremental.
40327 *
40328 * `NgCompiler` is lazy, and does not perform any of the work of the compilation until one of its
40329 * output methods (e.g. `getDiagnostics`) is called.
40330 *
40331 * See the README.md for more information.
40332 */
40333 class NgCompiler {
40334 constructor(adapter, options, inputProgram, programDriver, incrementalStrategy, incrementalCompilation, enableTemplateTypeChecker, usePoisonedData, livePerfRecorder) {
40335 this.adapter = adapter;
40336 this.options = options;
40337 this.inputProgram = inputProgram;
40338 this.programDriver = programDriver;
40339 this.incrementalStrategy = incrementalStrategy;
40340 this.incrementalCompilation = incrementalCompilation;
40341 this.enableTemplateTypeChecker = enableTemplateTypeChecker;
40342 this.usePoisonedData = usePoisonedData;
40343 this.livePerfRecorder = livePerfRecorder;
40344 /**
40345 * Lazily evaluated state of the compilation.
40346 *
40347 * This is created on demand by calling `ensureAnalyzed`.
40348 */
40349 this.compilation = null;
40350 /**
40351 * Any diagnostics related to the construction of the compilation.
40352 *
40353 * These are diagnostics which arose during setup of the host and/or program.
40354 */
40355 this.constructionDiagnostics = [];
40356 /**
40357 * Non-template diagnostics related to the program itself. Does not include template
40358 * diagnostics because the template type checker memoizes them itself.
40359 *
40360 * This is set by (and memoizes) `getNonTemplateDiagnostics`.
40361 */
40362 this.nonTemplateDiagnostics = null;
40363 /**
40364 * `NgCompiler` can be reused for multiple compilations (for resource-only changes), and each
40365 * new compilation uses a fresh `PerfRecorder`. Thus, classes created with a lifespan of the
40366 * `NgCompiler` use a `DelegatingPerfRecorder` so the `PerfRecorder` they write to can be updated
40367 * with each fresh compilation.
40368 */
40369 this.delegatingPerfRecorder = new DelegatingPerfRecorder(this.perfRecorder);
40370 if (this.options._extendedTemplateDiagnostics === true &&
40371 this.options.strictTemplates === false) {
40372 throw new Error('The \'_extendedTemplateDiagnostics\' option requires \'strictTemplates\' to also be enabled.');
40373 }
40374 this.constructionDiagnostics.push(...this.adapter.constructionDiagnostics);
40375 const incompatibleTypeCheckOptionsDiagnostic = verifyCompatibleTypeCheckOptions(this.options);
40376 if (incompatibleTypeCheckOptionsDiagnostic !== null) {
40377 this.constructionDiagnostics.push(incompatibleTypeCheckOptionsDiagnostic);
40378 }
40379 this.currentProgram = inputProgram;
40380 this.closureCompilerEnabled = !!this.options.annotateForClosureCompiler;
40381 this.entryPoint =
40382 adapter.entryPoint !== null ? getSourceFileOrNull(inputProgram, adapter.entryPoint) : null;
40383 const moduleResolutionCache = ts__default["default"].createModuleResolutionCache(this.adapter.getCurrentDirectory(),
40384 // doen't retain a reference to `this`, if other closures in the constructor here reference
40385 // `this` internally then a closure created here would retain them. This can cause major
40386 // memory leak issues since the `moduleResolutionCache` is a long-lived object and finds its
40387 // way into all kinds of places inside TS internal objects.
40388 this.adapter.getCanonicalFileName.bind(this.adapter));
40389 this.moduleResolver =
40390 new ModuleResolver(inputProgram, this.options, this.adapter, moduleResolutionCache);
40391 this.resourceManager = new AdapterResourceLoader(adapter, this.options);
40392 this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(inputProgram.getTypeChecker(), this.delegatingPerfRecorder));
40393 this.incrementalStrategy.setIncrementalState(this.incrementalCompilation.state, inputProgram);
40394 this.ignoreForDiagnostics =
40395 new Set(inputProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf)));
40396 this.ignoreForEmit = this.adapter.ignoreForEmit;
40397 let dtsFileCount = 0;
40398 let nonDtsFileCount = 0;
40399 for (const sf of inputProgram.getSourceFiles()) {
40400 if (sf.isDeclarationFile) {
40401 dtsFileCount++;
40402 }
40403 else {
40404 nonDtsFileCount++;
40405 }
40406 }
40407 livePerfRecorder.eventCount(PerfEvent.InputDtsFile, dtsFileCount);
40408 livePerfRecorder.eventCount(PerfEvent.InputTsFile, nonDtsFileCount);
40409 }
40410 /**
40411 * Convert a `CompilationTicket` into an `NgCompiler` instance for the requested compilation.
40412 *
40413 * Depending on the nature of the compilation request, the `NgCompiler` instance may be reused
40414 * from a previous compilation and updated with any changes, it may be a new instance which
40415 * incrementally reuses state from a previous compilation, or it may represent a fresh
40416 * compilation entirely.
40417 */
40418 static fromTicket(ticket, adapter) {
40419 switch (ticket.kind) {
40420 case CompilationTicketKind.Fresh:
40421 return new NgCompiler(adapter, ticket.options, ticket.tsProgram, ticket.programDriver, ticket.incrementalBuildStrategy, IncrementalCompilation.fresh(ticket.tsProgram, versionMapFromProgram(ticket.tsProgram, ticket.programDriver)), ticket.enableTemplateTypeChecker, ticket.usePoisonedData, ticket.perfRecorder);
40422 case CompilationTicketKind.IncrementalTypeScript:
40423 return new NgCompiler(adapter, ticket.options, ticket.newProgram, ticket.programDriver, ticket.incrementalBuildStrategy, ticket.incrementalCompilation, ticket.enableTemplateTypeChecker, ticket.usePoisonedData, ticket.perfRecorder);
40424 case CompilationTicketKind.IncrementalResource:
40425 const compiler = ticket.compiler;
40426 compiler.updateWithChangedResources(ticket.modifiedResourceFiles, ticket.perfRecorder);
40427 return compiler;
40428 }
40429 }
40430 get perfRecorder() {
40431 return this.livePerfRecorder;
40432 }
40433 /**
40434 * Exposes the `IncrementalCompilation` under an old property name that the CLI uses, avoiding a
40435 * chicken-and-egg problem with the rename to `incrementalCompilation`.
40436 *
40437 * TODO(alxhub): remove when the CLI uses the new name.
40438 */
40439 get incrementalDriver() {
40440 return this.incrementalCompilation;
40441 }
40442 updateWithChangedResources(changedResources, perfRecorder) {
40443 this.livePerfRecorder = perfRecorder;
40444 this.delegatingPerfRecorder.target = perfRecorder;
40445 perfRecorder.inPhase(PerfPhase.ResourceUpdate, () => {
40446 if (this.compilation === null) {
40447 // Analysis hasn't happened yet, so no update is necessary - any changes to resources will
40448 // be captured by the inital analysis pass itself.
40449 return;
40450 }
40451 this.resourceManager.invalidate();
40452 const classesToUpdate = new Set();
40453 for (const resourceFile of changedResources) {
40454 for (const templateClass of this.getComponentsWithTemplateFile(resourceFile)) {
40455 classesToUpdate.add(templateClass);
40456 }
40457 for (const styleClass of this.getComponentsWithStyleFile(resourceFile)) {
40458 classesToUpdate.add(styleClass);
40459 }
40460 }
40461 for (const clazz of classesToUpdate) {
40462 this.compilation.traitCompiler.updateResources(clazz);
40463 if (!ts__default["default"].isClassDeclaration(clazz)) {
40464 continue;
40465 }
40466 this.compilation.templateTypeChecker.invalidateClass(clazz);
40467 }
40468 });
40469 }
40470 /**
40471 * Get the resource dependencies of a file.
40472 *
40473 * If the file is not part of the compilation, an empty array will be returned.
40474 */
40475 getResourceDependencies(file) {
40476 this.ensureAnalyzed();
40477 return this.incrementalCompilation.depGraph.getResourceDependencies(file);
40478 }
40479 /**
40480 * Get all Angular-related diagnostics for this compilation.
40481 */
40482 getDiagnostics() {
40483 const diagnostics = [];
40484 diagnostics.push(...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics());
40485 if (this.options._extendedTemplateDiagnostics) {
40486 diagnostics.push(...this.getExtendedTemplateDiagnostics());
40487 }
40488 return this.addMessageTextDetails(diagnostics);
40489 }
40490 /**
40491 * Get all Angular-related diagnostics for this compilation.
40492 *
40493 * If a `ts.SourceFile` is passed, only diagnostics related to that file are returned.
40494 */
40495 getDiagnosticsForFile(file, optimizeFor) {
40496 const diagnostics = [];
40497 diagnostics.push(...this.getNonTemplateDiagnostics().filter(diag => diag.file === file), ...this.getTemplateDiagnosticsForFile(file, optimizeFor));
40498 if (this.options._extendedTemplateDiagnostics) {
40499 diagnostics.push(...this.getExtendedTemplateDiagnostics(file));
40500 }
40501 return this.addMessageTextDetails(diagnostics);
40502 }
40503 /**
40504 * Get all `ts.Diagnostic`s currently available that pertain to the given component.
40505 */
40506 getDiagnosticsForComponent(component) {
40507 const compilation = this.ensureAnalyzed();
40508 const ttc = compilation.templateTypeChecker;
40509 const diagnostics = [];
40510 diagnostics.push(...ttc.getDiagnosticsForComponent(component));
40511 if (this.options._extendedTemplateDiagnostics) {
40512 const extendedTemplateChecker = compilation.extendedTemplateChecker;
40513 diagnostics.push(...extendedTemplateChecker.getDiagnosticsForComponent(component));
40514 }
40515 return this.addMessageTextDetails(diagnostics);
40516 }
40517 /**
40518 * Add Angular.io error guide links to diagnostics for this compilation.
40519 */
40520 addMessageTextDetails(diagnostics) {
40521 return diagnostics.map(diag => {
40522 if (diag.code && COMPILER_ERRORS_WITH_GUIDES.has(ngErrorCode(diag.code))) {
40523 return {
40524 ...diag,
40525 messageText: diag.messageText +
40526 `. Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/NG${ngErrorCode(diag.code)}`
40527 };
40528 }
40529 return diag;
40530 });
40531 }
40532 /**
40533 * Get all setup-related diagnostics for this compilation.
40534 */
40535 getOptionDiagnostics() {
40536 return this.constructionDiagnostics;
40537 }
40538 /**
40539 * Get the current `ts.Program` known to this `NgCompiler`.
40540 *
40541 * Compilation begins with an input `ts.Program`, and during template type-checking operations new
40542 * `ts.Program`s may be produced using the `ProgramDriver`. The most recent such `ts.Program` to
40543 * be produced is available here.
40544 *
40545 * This `ts.Program` serves two key purposes:
40546 *
40547 * * As an incremental starting point for creating the next `ts.Program` based on files that the
40548 * user has changed (for clients using the TS compiler program APIs).
40549 *
40550 * * As the "before" point for an incremental compilation invocation, to determine what's changed
40551 * between the old and new programs (for all compilations).
40552 */
40553 getCurrentProgram() {
40554 return this.currentProgram;
40555 }
40556 getTemplateTypeChecker() {
40557 if (!this.enableTemplateTypeChecker) {
40558 throw new Error('The `TemplateTypeChecker` does not work without `enableTemplateTypeChecker`.');
40559 }
40560 return this.ensureAnalyzed().templateTypeChecker;
40561 }
40562 /**
40563 * Retrieves the `ts.Declaration`s for any component(s) which use the given template file.
40564 */
40565 getComponentsWithTemplateFile(templateFilePath) {
40566 const { resourceRegistry } = this.ensureAnalyzed();
40567 return resourceRegistry.getComponentsWithTemplate(resolve(templateFilePath));
40568 }
40569 /**
40570 * Retrieves the `ts.Declaration`s for any component(s) which use the given template file.
40571 */
40572 getComponentsWithStyleFile(styleFilePath) {
40573 const { resourceRegistry } = this.ensureAnalyzed();
40574 return resourceRegistry.getComponentsWithStyle(resolve(styleFilePath));
40575 }
40576 /**
40577 * Retrieves external resources for the given component.
40578 */
40579 getComponentResources(classDecl) {
40580 if (!isNamedClassDeclaration(classDecl)) {
40581 return null;
40582 }
40583 const { resourceRegistry } = this.ensureAnalyzed();
40584 const styles = resourceRegistry.getStyles(classDecl);
40585 const template = resourceRegistry.getTemplate(classDecl);
40586 if (template === null) {
40587 return null;
40588 }
40589 return { styles, template };
40590 }
40591 getMeta(classDecl) {
40592 if (!isNamedClassDeclaration(classDecl)) {
40593 return null;
40594 }
40595 const ref = new Reference(classDecl);
40596 const { metaReader } = this.ensureAnalyzed();
40597 const meta = metaReader.getPipeMetadata(ref) ?? metaReader.getDirectiveMetadata(ref);
40598 if (meta === null) {
40599 return null;
40600 }
40601 return meta;
40602 }
40603 /**
40604 * Perform Angular's analysis step (as a precursor to `getDiagnostics` or `prepareEmit`)
40605 * asynchronously.
40606 *
40607 * Normally, this operation happens lazily whenever `getDiagnostics` or `prepareEmit` are called.
40608 * However, certain consumers may wish to allow for an asynchronous phase of analysis, where
40609 * resources such as `styleUrls` are resolved asynchonously. In these cases `analyzeAsync` must be
40610 * called first, and its `Promise` awaited prior to calling any other APIs of `NgCompiler`.
40611 */
40612 async analyzeAsync() {
40613 if (this.compilation !== null) {
40614 return;
40615 }
40616 await this.perfRecorder.inPhase(PerfPhase.Analysis, async () => {
40617 this.compilation = this.makeCompilation();
40618 const promises = [];
40619 for (const sf of this.inputProgram.getSourceFiles()) {
40620 if (sf.isDeclarationFile) {
40621 continue;
40622 }
40623 let analysisPromise = this.compilation.traitCompiler.analyzeAsync(sf);
40624 if (analysisPromise !== undefined) {
40625 promises.push(analysisPromise);
40626 }
40627 }
40628 await Promise.all(promises);
40629 this.perfRecorder.memory(PerfCheckpoint.Analysis);
40630 this.resolveCompilation(this.compilation.traitCompiler);
40631 });
40632 }
40633 /**
40634 * Fetch transformers and other information which is necessary for a consumer to `emit` the
40635 * program with Angular-added definitions.
40636 */
40637 prepareEmit() {
40638 const compilation = this.ensureAnalyzed();
40639 const coreImportsFrom = compilation.isCore ? getR3SymbolsFile(this.inputProgram) : null;
40640 let importRewriter;
40641 if (coreImportsFrom !== null) {
40642 importRewriter = new R3SymbolsImportRewriter(coreImportsFrom.fileName);
40643 }
40644 else {
40645 importRewriter = new NoopImportRewriter();
40646 }
40647 const defaultImportTracker = new DefaultImportTracker();
40648 const before = [
40649 ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, defaultImportTracker, this.delegatingPerfRecorder, compilation.isCore, this.closureCompilerEnabled),
40650 aliasTransformFactory(compilation.traitCompiler.exportStatements),
40651 defaultImportTracker.importPreservingTransformer(),
40652 ];
40653 const afterDeclarations = [];
40654 if (compilation.dtsTransforms !== null) {
40655 afterDeclarations.push(declarationTransformFactory(compilation.dtsTransforms, importRewriter));
40656 }
40657 // Only add aliasing re-exports to the .d.ts output if the `AliasingHost` requests it.
40658 if (compilation.aliasingHost !== null && compilation.aliasingHost.aliasExportsInDts) {
40659 afterDeclarations.push(aliasTransformFactory(compilation.traitCompiler.exportStatements));
40660 }
40661 if (this.adapter.factoryTracker !== null) {
40662 before.push(generatedFactoryTransform(this.adapter.factoryTracker.sourceInfo, importRewriter));
40663 }
40664 before.push(ivySwitchTransform);
40665 return { transformers: { before, afterDeclarations } };
40666 }
40667 /**
40668 * Run the indexing process and return a `Map` of all indexed components.
40669 *
40670 * See the `indexing` package for more details.
40671 */
40672 getIndexedComponents() {
40673 const compilation = this.ensureAnalyzed();
40674 const context = new IndexingContext();
40675 compilation.traitCompiler.index(context);
40676 return generateAnalysis(context);
40677 }
40678 /**
40679 * Collect i18n messages into the `Xi18nContext`.
40680 */
40681 xi18n(ctx) {
40682 // Note that the 'resolve' phase is not strictly necessary for xi18n, but this is not currently
40683 // optimized.
40684 const compilation = this.ensureAnalyzed();
40685 compilation.traitCompiler.xi18n(ctx);
40686 }
40687 ensureAnalyzed() {
40688 if (this.compilation === null) {
40689 this.analyzeSync();
40690 }
40691 return this.compilation;
40692 }
40693 analyzeSync() {
40694 this.perfRecorder.inPhase(PerfPhase.Analysis, () => {
40695 this.compilation = this.makeCompilation();
40696 for (const sf of this.inputProgram.getSourceFiles()) {
40697 if (sf.isDeclarationFile) {
40698 continue;
40699 }
40700 this.compilation.traitCompiler.analyzeSync(sf);
40701 }
40702 this.perfRecorder.memory(PerfCheckpoint.Analysis);
40703 this.resolveCompilation(this.compilation.traitCompiler);
40704 });
40705 }
40706 resolveCompilation(traitCompiler) {
40707 this.perfRecorder.inPhase(PerfPhase.Resolve, () => {
40708 traitCompiler.resolve();
40709 // At this point, analysis is complete and the compiler can now calculate which files need to
40710 // be emitted, so do that.
40711 this.incrementalCompilation.recordSuccessfulAnalysis(traitCompiler);
40712 this.perfRecorder.memory(PerfCheckpoint.Resolve);
40713 });
40714 }
40715 get fullTemplateTypeCheck() {
40716 // Determine the strictness level of type checking based on compiler options. As
40717 // `strictTemplates` is a superset of `fullTemplateTypeCheck`, the former implies the latter.
40718 // Also see `verifyCompatibleTypeCheckOptions` where it is verified that `fullTemplateTypeCheck`
40719 // is not disabled when `strictTemplates` is enabled.
40720 const strictTemplates = !!this.options.strictTemplates;
40721 return strictTemplates || !!this.options.fullTemplateTypeCheck;
40722 }
40723 getTypeCheckingConfig() {
40724 // Determine the strictness level of type checking based on compiler options. As
40725 // `strictTemplates` is a superset of `fullTemplateTypeCheck`, the former implies the latter.
40726 // Also see `verifyCompatibleTypeCheckOptions` where it is verified that `fullTemplateTypeCheck`
40727 // is not disabled when `strictTemplates` is enabled.
40728 const strictTemplates = !!this.options.strictTemplates;
40729 const useInlineTypeConstructors = this.programDriver.supportsInlineOperations;
40730 // First select a type-checking configuration, based on whether full template type-checking is
40731 // requested.
40732 let typeCheckingConfig;
40733 if (this.fullTemplateTypeCheck) {
40734 typeCheckingConfig = {
40735 applyTemplateContextGuards: strictTemplates,
40736 checkQueries: false,
40737 checkTemplateBodies: true,
40738 alwaysCheckSchemaInTemplateBodies: true,
40739 checkTypeOfInputBindings: strictTemplates,
40740 honorAccessModifiersForInputBindings: false,
40741 strictNullInputBindings: strictTemplates,
40742 checkTypeOfAttributes: strictTemplates,
40743 // Even in full template type-checking mode, DOM binding checks are not quite ready yet.
40744 checkTypeOfDomBindings: false,
40745 checkTypeOfOutputEvents: strictTemplates,
40746 checkTypeOfAnimationEvents: strictTemplates,
40747 // Checking of DOM events currently has an adverse effect on developer experience,
40748 // e.g. for `<input (blur)="update($event.target.value)">` enabling this check results in:
40749 // - error TS2531: Object is possibly 'null'.
40750 // - error TS2339: Property 'value' does not exist on type 'EventTarget'.
40751 checkTypeOfDomEvents: strictTemplates,
40752 checkTypeOfDomReferences: strictTemplates,
40753 // Non-DOM references have the correct type in View Engine so there is no strictness flag.
40754 checkTypeOfNonDomReferences: true,
40755 // Pipes are checked in View Engine so there is no strictness flag.
40756 checkTypeOfPipes: true,
40757 strictSafeNavigationTypes: strictTemplates,
40758 useContextGenericType: strictTemplates,
40759 strictLiteralTypes: true,
40760 enableTemplateTypeChecker: this.enableTemplateTypeChecker,
40761 useInlineTypeConstructors,
40762 // Warnings for suboptimal type inference are only enabled if in Language Service mode
40763 // (providing the full TemplateTypeChecker API) and if strict mode is not enabled. In strict
40764 // mode, the user is in full control of type inference.
40765 suggestionsForSuboptimalTypeInference: this.enableTemplateTypeChecker && !strictTemplates,
40766 };
40767 }
40768 else {
40769 typeCheckingConfig = {
40770 applyTemplateContextGuards: false,
40771 checkQueries: false,
40772 checkTemplateBodies: false,
40773 // Enable deep schema checking in "basic" template type-checking mode only if Closure
40774 // compilation is requested, which is a good proxy for "only in google3".
40775 alwaysCheckSchemaInTemplateBodies: this.closureCompilerEnabled,
40776 checkTypeOfInputBindings: false,
40777 strictNullInputBindings: false,
40778 honorAccessModifiersForInputBindings: false,
40779 checkTypeOfAttributes: false,
40780 checkTypeOfDomBindings: false,
40781 checkTypeOfOutputEvents: false,
40782 checkTypeOfAnimationEvents: false,
40783 checkTypeOfDomEvents: false,
40784 checkTypeOfDomReferences: false,
40785 checkTypeOfNonDomReferences: false,
40786 checkTypeOfPipes: false,
40787 strictSafeNavigationTypes: false,
40788 useContextGenericType: false,
40789 strictLiteralTypes: false,
40790 enableTemplateTypeChecker: this.enableTemplateTypeChecker,
40791 useInlineTypeConstructors,
40792 // In "basic" template type-checking mode, no warnings are produced since most things are
40793 // not checked anyways.
40794 suggestionsForSuboptimalTypeInference: false,
40795 };
40796 }
40797 // Apply explicitly configured strictness flags on top of the default configuration
40798 // based on "fullTemplateTypeCheck".
40799 if (this.options.strictInputTypes !== undefined) {
40800 typeCheckingConfig.checkTypeOfInputBindings = this.options.strictInputTypes;
40801 typeCheckingConfig.applyTemplateContextGuards = this.options.strictInputTypes;
40802 }
40803 if (this.options.strictInputAccessModifiers !== undefined) {
40804 typeCheckingConfig.honorAccessModifiersForInputBindings =
40805 this.options.strictInputAccessModifiers;
40806 }
40807 if (this.options.strictNullInputTypes !== undefined) {
40808 typeCheckingConfig.strictNullInputBindings = this.options.strictNullInputTypes;
40809 }
40810 if (this.options.strictOutputEventTypes !== undefined) {
40811 typeCheckingConfig.checkTypeOfOutputEvents = this.options.strictOutputEventTypes;
40812 typeCheckingConfig.checkTypeOfAnimationEvents = this.options.strictOutputEventTypes;
40813 }
40814 if (this.options.strictDomEventTypes !== undefined) {
40815 typeCheckingConfig.checkTypeOfDomEvents = this.options.strictDomEventTypes;
40816 }
40817 if (this.options.strictSafeNavigationTypes !== undefined) {
40818 typeCheckingConfig.strictSafeNavigationTypes = this.options.strictSafeNavigationTypes;
40819 }
40820 if (this.options.strictDomLocalRefTypes !== undefined) {
40821 typeCheckingConfig.checkTypeOfDomReferences = this.options.strictDomLocalRefTypes;
40822 }
40823 if (this.options.strictAttributeTypes !== undefined) {
40824 typeCheckingConfig.checkTypeOfAttributes = this.options.strictAttributeTypes;
40825 }
40826 if (this.options.strictContextGenerics !== undefined) {
40827 typeCheckingConfig.useContextGenericType = this.options.strictContextGenerics;
40828 }
40829 if (this.options.strictLiteralTypes !== undefined) {
40830 typeCheckingConfig.strictLiteralTypes = this.options.strictLiteralTypes;
40831 }
40832 return typeCheckingConfig;
40833 }
40834 getTemplateDiagnostics() {
40835 const compilation = this.ensureAnalyzed();
40836 // Get the diagnostics.
40837 const diagnostics = [];
40838 for (const sf of this.inputProgram.getSourceFiles()) {
40839 if (sf.isDeclarationFile || this.adapter.isShim(sf)) {
40840 continue;
40841 }
40842 diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
40843 }
40844 const program = this.programDriver.getProgram();
40845 this.incrementalStrategy.setIncrementalState(this.incrementalCompilation.state, program);
40846 this.currentProgram = program;
40847 return diagnostics;
40848 }
40849 getTemplateDiagnosticsForFile(sf, optimizeFor) {
40850 const compilation = this.ensureAnalyzed();
40851 // Get the diagnostics.
40852 const diagnostics = [];
40853 if (!sf.isDeclarationFile && !this.adapter.isShim(sf)) {
40854 diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, optimizeFor));
40855 }
40856 const program = this.programDriver.getProgram();
40857 this.incrementalStrategy.setIncrementalState(this.incrementalCompilation.state, program);
40858 this.currentProgram = program;
40859 return diagnostics;
40860 }
40861 getNonTemplateDiagnostics() {
40862 if (this.nonTemplateDiagnostics === null) {
40863 const compilation = this.ensureAnalyzed();
40864 this.nonTemplateDiagnostics = [...compilation.traitCompiler.diagnostics];
40865 if (this.entryPoint !== null && compilation.exportReferenceGraph !== null) {
40866 this.nonTemplateDiagnostics.push(...checkForPrivateExports(this.entryPoint, this.inputProgram.getTypeChecker(), compilation.exportReferenceGraph));
40867 }
40868 }
40869 return this.nonTemplateDiagnostics;
40870 }
40871 /**
40872 * Calls the `extendedTemplateCheck` phase of the trait compiler
40873 * @param sf optional parameter to get diagnostics for a certain file
40874 * or all files in the program if `sf` is undefined
40875 * @returns generated extended template diagnostics
40876 */
40877 getExtendedTemplateDiagnostics(sf) {
40878 const diagnostics = [];
40879 const compilation = this.ensureAnalyzed();
40880 const extendedTemplateChecker = compilation.extendedTemplateChecker;
40881 if (sf !== undefined) {
40882 return compilation.traitCompiler.extendedTemplateCheck(sf, extendedTemplateChecker);
40883 }
40884 for (const sf of this.inputProgram.getSourceFiles()) {
40885 diagnostics.push(...compilation.traitCompiler.extendedTemplateCheck(sf, extendedTemplateChecker));
40886 }
40887 return diagnostics;
40888 }
40889 makeCompilation() {
40890 const checker = this.inputProgram.getTypeChecker();
40891 const reflector = new TypeScriptReflectionHost(checker);
40892 // Construct the ReferenceEmitter.
40893 let refEmitter;
40894 let aliasingHost = null;
40895 if (this.adapter.unifiedModulesHost === null || !this.options._useHostForImportGeneration) {
40896 let localImportStrategy;
40897 // The strategy used for local, in-project imports depends on whether TS has been configured
40898 // with rootDirs. If so, then multiple directories may be mapped in the same "module
40899 // namespace" and the logic of `LogicalProjectStrategy` is required to generate correct
40900 // imports which may cross these multiple directories. Otherwise, plain relative imports are
40901 // sufficient.
40902 if (this.options.rootDir !== undefined ||
40903 (this.options.rootDirs !== undefined && this.options.rootDirs.length > 0)) {
40904 // rootDirs logic is in effect - use the `LogicalProjectStrategy` for in-project relative
40905 // imports.
40906 localImportStrategy = new LogicalProjectStrategy(reflector, new LogicalFileSystem([...this.adapter.rootDirs], this.adapter));
40907 }
40908 else {
40909 // Plain relative imports are all that's needed.
40910 localImportStrategy = new RelativePathStrategy(reflector);
40911 }
40912 // The CompilerHost doesn't have fileNameToModuleName, so build an NPM-centric reference
40913 // resolution strategy.
40914 refEmitter = new ReferenceEmitter([
40915 // First, try to use local identifiers if available.
40916 new LocalIdentifierStrategy(),
40917 // Next, attempt to use an absolute import.
40918 new AbsoluteModuleStrategy(this.inputProgram, checker, this.moduleResolver, reflector),
40919 // Finally, check if the reference is being written into a file within the project's .ts
40920 // sources, and use a relative import if so. If this fails, ReferenceEmitter will throw
40921 // an error.
40922 localImportStrategy,
40923 ]);
40924 // If an entrypoint is present, then all user imports should be directed through the
40925 // entrypoint and private exports are not needed. The compiler will validate that all publicly
40926 // visible directives/pipes are importable via this entrypoint.
40927 if (this.entryPoint === null && this.options.generateDeepReexports === true) {
40928 // No entrypoint is present and deep re-exports were requested, so configure the aliasing
40929 // system to generate them.
40930 aliasingHost = new PrivateExportAliasingHost(reflector);
40931 }
40932 }
40933 else {
40934 // The CompilerHost supports fileNameToModuleName, so use that to emit imports.
40935 refEmitter = new ReferenceEmitter([
40936 // First, try to use local identifiers if available.
40937 new LocalIdentifierStrategy(),
40938 // Then use aliased references (this is a workaround to StrictDeps checks).
40939 new AliasStrategy(),
40940 // Then use fileNameToModuleName to emit imports.
40941 new UnifiedModulesStrategy(reflector, this.adapter.unifiedModulesHost),
40942 ]);
40943 aliasingHost = new UnifiedModulesAliasingHost(this.adapter.unifiedModulesHost);
40944 }
40945 const evaluator = new PartialEvaluator(reflector, checker, this.incrementalCompilation.depGraph);
40946 const dtsReader = new DtsMetadataReader(checker, reflector);
40947 const localMetaRegistry = new LocalMetadataRegistry();
40948 const localMetaReader = localMetaRegistry;
40949 const depScopeReader = new MetadataDtsModuleScopeResolver(dtsReader, aliasingHost);
40950 const scopeRegistry = new LocalModuleScopeRegistry(localMetaReader, depScopeReader, refEmitter, aliasingHost);
40951 const scopeReader = scopeRegistry;
40952 const semanticDepGraphUpdater = this.incrementalCompilation.semanticDepGraphUpdater;
40953 const metaRegistry = new CompoundMetadataRegistry([localMetaRegistry, scopeRegistry]);
40954 const injectableRegistry = new InjectableClassRegistry(reflector);
40955 const metaReader = new CompoundMetadataReader([localMetaReader, dtsReader]);
40956 const typeCheckScopeRegistry = new TypeCheckScopeRegistry(scopeReader, metaReader);
40957 // If a flat module entrypoint was specified, then track references via a `ReferenceGraph` in
40958 // order to produce proper diagnostics for incorrectly exported directives/pipes/etc. If there
40959 // is no flat module entrypoint then don't pay the cost of tracking references.
40960 let referencesRegistry;
40961 let exportReferenceGraph = null;
40962 if (this.entryPoint !== null) {
40963 exportReferenceGraph = new ReferenceGraph();
40964 referencesRegistry = new ReferenceGraphAdapter(exportReferenceGraph);
40965 }
40966 else {
40967 referencesRegistry = new NoopReferencesRegistry();
40968 }
40969 const dtsTransforms = new DtsTransformRegistry();
40970 const isCore = isAngularCorePackage(this.inputProgram);
40971 const resourceRegistry = new ResourceRegistry();
40972 // Note: If this compilation builds `@angular/core`, we always build in full compilation
40973 // mode. Code inside the core package is always compatible with itself, so it does not
40974 // make sense to go through the indirection of partial compilation
40975 const compilationMode = this.options.compilationMode === 'partial' && !isCore ?
40976 CompilationMode.PARTIAL :
40977 CompilationMode.FULL;
40978 // Cycles are handled in full compilation mode by "remote scoping".
40979 // "Remote scoping" does not work well with tree shaking for libraries.
40980 // So in partial compilation mode, when building a library, a cycle will cause an error.
40981 const cycleHandlingStrategy = compilationMode === CompilationMode.FULL ?
40982 0 /* UseRemoteScoping */ :
40983 1 /* Error */;
40984 // Set up the IvyCompilation, which manages state for the Ivy transformer.
40985 const handlers = [
40986 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, this.incrementalCompilation.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder),
40987 // TODO(alxhub): understand why the cast here is necessary (something to do with `null`
40988 // not being assignable to `unknown` when wrapped in `Readonly`).
40989 // clang-format off
40990 new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, injectableRegistry, isCore, semanticDepGraphUpdater, this.closureCompilerEnabled, /** compileUndecoratedClassesWithAngularFeatures */ false, this.delegatingPerfRecorder),
40991 // clang-format on
40992 // Pipe handler must be before injectable handler in list so pipe factories are printed
40993 // before injectable factories (so injectable factories can delegate to them)
40994 new PipeDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, injectableRegistry, isCore, this.delegatingPerfRecorder),
40995 new InjectableDecoratorHandler(reflector, isCore, this.options.strictInjectionParameters || false, injectableRegistry, this.delegatingPerfRecorder),
40996 new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, refEmitter, this.adapter.factoryTracker, this.closureCompilerEnabled, injectableRegistry, this.delegatingPerfRecorder),
40997 ];
40998 const traitCompiler = new TraitCompiler(handlers, reflector, this.delegatingPerfRecorder, this.incrementalCompilation, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, semanticDepGraphUpdater);
40999 // Template type-checking may use the `ProgramDriver` to produce new `ts.Program`(s). If this
41000 // happens, they need to be tracked by the `NgCompiler`.
41001 const notifyingDriver = new NotifyingProgramDriverWrapper(this.programDriver, (program) => {
41002 this.incrementalStrategy.setIncrementalState(this.incrementalCompilation.state, program);
41003 this.currentProgram = program;
41004 });
41005 const templateTypeChecker = new TemplateTypeCheckerImpl(this.inputProgram, notifyingDriver, traitCompiler, this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalCompilation, scopeRegistry, typeCheckScopeRegistry, this.delegatingPerfRecorder);
41006 const templateChecks = [new InvalidBananaInBoxCheck()];
41007 if (this.options.strictNullChecks) {
41008 templateChecks.push(new NullishCoalescingNotNullableCheck());
41009 }
41010 const extendedTemplateChecker = new ExtendedTemplateCheckerImpl(templateTypeChecker, checker, templateChecks);
41011 return {
41012 isCore,
41013 traitCompiler,
41014 reflector,
41015 scopeRegistry,
41016 dtsTransforms,
41017 exportReferenceGraph,
41018 metaReader,
41019 typeCheckScopeRegistry,
41020 aliasingHost,
41021 refEmitter,
41022 templateTypeChecker,
41023 resourceRegistry,
41024 extendedTemplateChecker
41025 };
41026 }
41027 }
41028 /**
41029 * Determine if the given `Program` is @angular/core.
41030 */
41031 function isAngularCorePackage(program) {
41032 // Look for its_just_angular.ts somewhere in the program.
41033 const r3Symbols = getR3SymbolsFile(program);
41034 if (r3Symbols === null) {
41035 return false;
41036 }
41037 // Look for the constant ITS_JUST_ANGULAR in that file.
41038 return r3Symbols.statements.some(stmt => {
41039 // The statement must be a variable declaration statement.
41040 if (!ts__default["default"].isVariableStatement(stmt)) {
41041 return false;
41042 }
41043 // It must be exported.
41044 if (stmt.modifiers === undefined ||
41045 !stmt.modifiers.some(mod => mod.kind === ts__default["default"].SyntaxKind.ExportKeyword)) {
41046 return false;
41047 }
41048 // It must declare ITS_JUST_ANGULAR.
41049 return stmt.declarationList.declarations.some(decl => {
41050 // The declaration must match the name.
41051 if (!ts__default["default"].isIdentifier(decl.name) || decl.name.text !== 'ITS_JUST_ANGULAR') {
41052 return false;
41053 }
41054 // It must initialize the variable to true.
41055 if (decl.initializer === undefined || decl.initializer.kind !== ts__default["default"].SyntaxKind.TrueKeyword) {
41056 return false;
41057 }
41058 // This definition matches.
41059 return true;
41060 });
41061 });
41062 }
41063 /**
41064 * Find the 'r3_symbols.ts' file in the given `Program`, or return `null` if it wasn't there.
41065 */
41066 function getR3SymbolsFile(program) {
41067 return program.getSourceFiles().find(file => file.fileName.indexOf('r3_symbols.ts') >= 0) || null;
41068 }
41069 /**
41070 * Since "strictTemplates" is a true superset of type checking capabilities compared to
41071 * "fullTemplateTypeCheck", it is required that the latter is not explicitly disabled if the
41072 * former is enabled.
41073 */
41074 function verifyCompatibleTypeCheckOptions(options) {
41075 if (options.fullTemplateTypeCheck === false && options.strictTemplates === true) {
41076 return {
41077 category: ts__default["default"].DiagnosticCategory.Error,
41078 code: ngErrorCode(ErrorCode.CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK),
41079 file: undefined,
41080 start: undefined,
41081 length: undefined,
41082 messageText: `Angular compiler option "strictTemplates" is enabled, however "fullTemplateTypeCheck" is disabled.
41083
41084Having the "strictTemplates" flag enabled implies that "fullTemplateTypeCheck" is also enabled, so
41085the latter can not be explicitly disabled.
41086
41087One of the following actions is required:
410881. Remove the "fullTemplateTypeCheck" option.
410892. Remove "strictTemplates" or set it to 'false'.
41090
41091More information about the template type checking compiler options can be found in the documentation:
41092https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41093 };
41094 }
41095 return null;
41096 }
41097 class ReferenceGraphAdapter {
41098 constructor(graph) {
41099 this.graph = graph;
41100 }
41101 add(source, ...references) {
41102 for (const { node } of references) {
41103 let sourceFile = node.getSourceFile();
41104 if (sourceFile === undefined) {
41105 sourceFile = ts__default["default"].getOriginalNode(node).getSourceFile();
41106 }
41107 // Only record local references (not references into .d.ts files).
41108 if (sourceFile === undefined || !isDtsPath(sourceFile.fileName)) {
41109 this.graph.add(source, node);
41110 }
41111 }
41112 }
41113 }
41114 class NotifyingProgramDriverWrapper {
41115 constructor(delegate, notifyNewProgram) {
41116 this.delegate = delegate;
41117 this.notifyNewProgram = notifyNewProgram;
41118 this.getSourceFileVersion = this.delegate.getSourceFileVersion?.bind(this);
41119 }
41120 get supportsInlineOperations() {
41121 return this.delegate.supportsInlineOperations;
41122 }
41123 getProgram() {
41124 return this.delegate.getProgram();
41125 }
41126 updateFiles(contents, updateMode) {
41127 this.delegate.updateFiles(contents, updateMode);
41128 this.notifyNewProgram(this.delegate.getProgram());
41129 }
41130 }
41131 function versionMapFromProgram(program, driver) {
41132 if (driver.getSourceFileVersion === undefined) {
41133 return null;
41134 }
41135 const versions = new Map();
41136 for (const possiblyRedirectedSourceFile of program.getSourceFiles()) {
41137 const sf = toUnredirectedSourceFile(possiblyRedirectedSourceFile);
41138 versions.set(absoluteFromSourceFile(sf), driver.getSourceFileVersion(sf));
41139 }
41140 return versions;
41141 }
41142
41143 /**
41144 * @license
41145 * Copyright Google LLC All Rights Reserved.
41146 *
41147 * Use of this source code is governed by an MIT-style license that can be
41148 * found in the LICENSE file at https://angular.io/license
41149 */
41150 /**
41151 * The currently used version of TypeScript, which can be adjusted for testing purposes using
41152 * `setTypeScriptVersionForTesting` and `restoreTypeScriptVersionForTesting` below.
41153 */
41154 ts__default["default"].version;
41155
41156 /**
41157 * @license
41158 * Copyright Google LLC All Rights Reserved.
41159 *
41160 * Use of this source code is governed by an MIT-style license that can be
41161 * found in the LICENSE file at https://angular.io/license
41162 */
41163 /** Error message to show when attempting to build View Engine. */
41164`
41165This compilation is using the View Engine compiler which is no longer supported by the Angular team
41166and is being removed. Please upgrade to the Ivy compiler by switching to \`NgtscProgram\`. See
41167https://angular.io/guide/ivy for more information.
41168` .trim().split('\n').join(' ');
41169
41170 /**
41171 * @license
41172 * Copyright Google LLC All Rights Reserved.
41173 *
41174 * Use of this source code is governed by an MIT-style license that can be
41175 * found in the LICENSE file at https://angular.io/license
41176 */
41177 function calcProjectFileAndBasePath(project, host = getFileSystem()) {
41178 const absProject = host.resolve(project);
41179 const projectIsDir = host.lstat(absProject).isDirectory();
41180 const projectFile = projectIsDir ? host.join(absProject, 'tsconfig.json') : absProject;
41181 const projectDir = projectIsDir ? absProject : host.dirname(absProject);
41182 const basePath = host.resolve(projectDir);
41183 return { projectFile, basePath };
41184 }
41185 function readConfiguration(project, existingOptions, host = getFileSystem()) {
41186 try {
41187 const fs = getFileSystem();
41188 const readConfigFile = (configFile) => ts__default["default"].readConfigFile(configFile, file => host.readFile(host.resolve(file)));
41189 const readAngularCompilerOptions = (configFile, parentOptions = {}) => {
41190 const { config, error } = readConfigFile(configFile);
41191 if (error) {
41192 // Errors are handled later on by 'parseJsonConfigFileContent'
41193 return parentOptions;
41194 }
41195 // we are only interested into merging 'angularCompilerOptions' as
41196 // other options like 'compilerOptions' are merged by TS
41197 const existingNgCompilerOptions = { ...config.angularCompilerOptions, ...parentOptions };
41198 if (config.extends && typeof config.extends === 'string') {
41199 const extendedConfigPath = getExtendedConfigPath(configFile, config.extends, host, fs);
41200 if (extendedConfigPath !== null) {
41201 // Call readAngularCompilerOptions recursively to merge NG Compiler options
41202 return readAngularCompilerOptions(extendedConfigPath, existingNgCompilerOptions);
41203 }
41204 }
41205 return existingNgCompilerOptions;
41206 };
41207 const { projectFile, basePath } = calcProjectFileAndBasePath(project, host);
41208 const configFileName = host.resolve(host.pwd(), projectFile);
41209 const { config, error } = readConfigFile(projectFile);
41210 if (error) {
41211 return {
41212 project,
41213 errors: [error],
41214 rootNames: [],
41215 options: {},
41216 emitFlags: EmitFlags.Default
41217 };
41218 }
41219 const existingCompilerOptions = {
41220 genDir: basePath,
41221 basePath,
41222 ...readAngularCompilerOptions(configFileName),
41223 ...existingOptions,
41224 };
41225 const parseConfigHost = createParseConfigHost(host, fs);
41226 const { options, errors, fileNames: rootNames, projectReferences } = ts__default["default"].parseJsonConfigFileContent(config, parseConfigHost, basePath, existingCompilerOptions, configFileName);
41227 // Coerce to boolean as `enableIvy` can be `ngtsc|true|false|undefined` here.
41228 options.enableIvy = !!(options.enableIvy ?? true);
41229 let emitFlags = EmitFlags.Default;
41230 if (!(options.skipMetadataEmit || options.flatModuleOutFile)) {
41231 emitFlags |= EmitFlags.Metadata;
41232 }
41233 if (options.skipTemplateCodegen) {
41234 emitFlags = emitFlags & ~EmitFlags.Codegen;
41235 }
41236 return { project: projectFile, rootNames, projectReferences, options, errors, emitFlags };
41237 }
41238 catch (e) {
41239 const errors = [{
41240 category: ts__default["default"].DiagnosticCategory.Error,
41241 messageText: e.stack,
41242 file: undefined,
41243 start: undefined,
41244 length: undefined,
41245 source: 'angular',
41246 code: UNKNOWN_ERROR_CODE,
41247 }];
41248 return { project: '', errors, rootNames: [], options: {}, emitFlags: EmitFlags.Default };
41249 }
41250 }
41251 function createParseConfigHost(host, fs = getFileSystem()) {
41252 return {
41253 fileExists: host.exists.bind(host),
41254 readDirectory: ts__default["default"].sys.readDirectory,
41255 readFile: host.readFile.bind(host),
41256 useCaseSensitiveFileNames: fs.isCaseSensitive(),
41257 };
41258 }
41259 function getExtendedConfigPath(configFile, extendsValue, host, fs) {
41260 const result = getExtendedConfigPathWorker(configFile, extendsValue, host, fs);
41261 if (result !== null) {
41262 return result;
41263 }
41264 // Try to resolve the paths with a json extension append a json extension to the file in case if
41265 // it is missing and the resolution failed. This is to replicate TypeScript behaviour, see:
41266 // https://github.com/microsoft/TypeScript/blob/294a5a7d784a5a95a8048ee990400979a6bc3a1c/src/compiler/commandLineParser.ts#L2806
41267 return getExtendedConfigPathWorker(configFile, `${extendsValue}.json`, host, fs);
41268 }
41269 function getExtendedConfigPathWorker(configFile, extendsValue, host, fs) {
41270 if (extendsValue.startsWith('.') || fs.isRooted(extendsValue)) {
41271 const extendedConfigPath = host.resolve(host.dirname(configFile), extendsValue);
41272 if (host.exists(extendedConfigPath)) {
41273 return extendedConfigPath;
41274 }
41275 }
41276 else {
41277 const parseConfigHost = createParseConfigHost(host, fs);
41278 // Path isn't a rooted or relative path, resolve like a module.
41279 const { resolvedModule, } = ts__default["default"].nodeModuleNameResolver(extendsValue, configFile, { moduleResolution: ts__default["default"].ModuleResolutionKind.NodeJs, resolveJsonModule: true }, parseConfigHost);
41280 if (resolvedModule) {
41281 return absoluteFrom(resolvedModule.resolvedFileName);
41282 }
41283 }
41284 return null;
41285 }
41286
41287 /**
41288 * @license
41289 * Copyright Google LLC All Rights Reserved.
41290 *
41291 * Use of this source code is governed by an MIT-style license that can be
41292 * found in the LICENSE file at https://angular.io/license
41293 */
41294 var LogLevel;
41295 (function (LogLevel) {
41296 LogLevel[LogLevel["debug"] = 0] = "debug";
41297 LogLevel[LogLevel["info"] = 1] = "info";
41298 LogLevel[LogLevel["warn"] = 2] = "warn";
41299 LogLevel[LogLevel["error"] = 3] = "error";
41300 })(LogLevel || (LogLevel = {}));
41301
41302 /**
41303 * @license
41304 * Copyright Google LLC All Rights Reserved.
41305 *
41306 * Use of this source code is governed by an MIT-style license that can be
41307 * found in the LICENSE file at https://angular.io/license
41308 */
41309 setFileSystem(new NodeJSFileSystem());
41310
41311 /**
41312 * @license
41313 * Copyright Google LLC All Rights Reserved.
41314 *
41315 * Use of this source code is governed by an MIT-style license that can be
41316 * found in the LICENSE file at https://angular.io/license
41317 */
41318 // Reverse mappings of enum would generate strings
41319 const ALIAS_NAME = ts__default["default"].SymbolDisplayPartKind[ts__default["default"].SymbolDisplayPartKind.aliasName];
41320 const SYMBOL_INTERFACE = ts__default["default"].SymbolDisplayPartKind[ts__default["default"].SymbolDisplayPartKind.interfaceName];
41321 const SYMBOL_PUNC = ts__default["default"].SymbolDisplayPartKind[ts__default["default"].SymbolDisplayPartKind.punctuation];
41322 const SYMBOL_SPACE = ts__default["default"].SymbolDisplayPartKind[ts__default["default"].SymbolDisplayPartKind.space];
41323 const SYMBOL_TEXT = ts__default["default"].SymbolDisplayPartKind[ts__default["default"].SymbolDisplayPartKind.text];
41324 /**
41325 * Label for various kinds of Angular entities for TS display info.
41326 */
41327 var DisplayInfoKind;
41328 (function (DisplayInfoKind) {
41329 DisplayInfoKind["ATTRIBUTE"] = "attribute";
41330 DisplayInfoKind["COMPONENT"] = "component";
41331 DisplayInfoKind["DIRECTIVE"] = "directive";
41332 DisplayInfoKind["EVENT"] = "event";
41333 DisplayInfoKind["REFERENCE"] = "reference";
41334 DisplayInfoKind["ELEMENT"] = "element";
41335 DisplayInfoKind["VARIABLE"] = "variable";
41336 DisplayInfoKind["PIPE"] = "pipe";
41337 DisplayInfoKind["PROPERTY"] = "property";
41338 DisplayInfoKind["METHOD"] = "method";
41339 DisplayInfoKind["TEMPLATE"] = "template";
41340 })(DisplayInfoKind || (DisplayInfoKind = {}));
41341 function getSymbolDisplayInfo(tsLS, typeChecker, symbol) {
41342 let kind;
41343 if (symbol.kind === SymbolKind.Reference) {
41344 kind = DisplayInfoKind.REFERENCE;
41345 }
41346 else if (symbol.kind === SymbolKind.Variable) {
41347 kind = DisplayInfoKind.VARIABLE;
41348 }
41349 else {
41350 throw new Error(`AssertionError: unexpected symbol kind ${SymbolKind[symbol.kind]}`);
41351 }
41352 const displayParts = createDisplayParts(symbol.declaration.name, kind, /* containerName */ undefined, typeChecker.typeToString(symbol.tsType));
41353 const documentation = symbol.kind === SymbolKind.Reference ?
41354 getDocumentationFromTypeDefAtLocation(tsLS, symbol.targetLocation) :
41355 getDocumentationFromTypeDefAtLocation(tsLS, symbol.initializerLocation);
41356 return {
41357 kind,
41358 displayParts,
41359 documentation,
41360 };
41361 }
41362 /**
41363 * Construct a compound `ts.SymbolDisplayPart[]` which incorporates the container and type of a
41364 * target declaration.
41365 * @param name Name of the target
41366 * @param kind component, directive, pipe, etc.
41367 * @param containerName either the Symbol's container or the NgModule that contains the directive
41368 * @param type user-friendly name of the type
41369 * @param documentation docstring or comment
41370 */
41371 function createDisplayParts(name, kind, containerName, type) {
41372 const containerDisplayParts = containerName !== undefined ?
41373 [
41374 { text: containerName, kind: SYMBOL_INTERFACE },
41375 { text: '.', kind: SYMBOL_PUNC },
41376 ] :
41377 [];
41378 const typeDisplayParts = type !== undefined ?
41379 [
41380 { text: ':', kind: SYMBOL_PUNC },
41381 { text: ' ', kind: SYMBOL_SPACE },
41382 { text: type, kind: SYMBOL_INTERFACE },
41383 ] :
41384 [];
41385 return [
41386 { text: '(', kind: SYMBOL_PUNC },
41387 { text: kind, kind: SYMBOL_TEXT },
41388 { text: ')', kind: SYMBOL_PUNC },
41389 { text: ' ', kind: SYMBOL_SPACE },
41390 ...containerDisplayParts,
41391 { text: name, kind: SYMBOL_INTERFACE },
41392 ...typeDisplayParts,
41393 ];
41394 }
41395 /**
41396 * Convert a `SymbolDisplayInfoKind` to a `ts.ScriptElementKind` type, allowing it to pass through
41397 * TypeScript APIs.
41398 *
41399 * In practice, this is an "illegal" type cast. Since `ts.ScriptElementKind` is a string, this is
41400 * safe to do if TypeScript only uses the value in a string context. Consumers of this conversion
41401 * function are responsible for ensuring this is the case.
41402 */
41403 function unsafeCastDisplayInfoKindToScriptElementKind(kind) {
41404 return kind;
41405 }
41406 function getDocumentationFromTypeDefAtLocation(tsLS, shimLocation) {
41407 const typeDefs = tsLS.getTypeDefinitionAtPosition(shimLocation.shimPath, shimLocation.positionInShimFile);
41408 if (typeDefs === undefined || typeDefs.length === 0) {
41409 return undefined;
41410 }
41411 return tsLS.getQuickInfoAtPosition(typeDefs[0].fileName, typeDefs[0].textSpan.start)
41412 ?.documentation;
41413 }
41414 function getDirectiveDisplayInfo(tsLS, dir) {
41415 const kind = dir.isComponent ? DisplayInfoKind.COMPONENT : DisplayInfoKind.DIRECTIVE;
41416 const decl = dir.tsSymbol.declarations.find(ts__default["default"].isClassDeclaration);
41417 if (decl === undefined || decl.name === undefined) {
41418 return { kind, displayParts: [], documentation: [] };
41419 }
41420 const res = tsLS.getQuickInfoAtPosition(decl.getSourceFile().fileName, decl.name.getStart());
41421 if (res === undefined) {
41422 return { kind, displayParts: [], documentation: [] };
41423 }
41424 const displayParts = createDisplayParts(dir.tsSymbol.name, kind, dir.ngModule?.name?.text, undefined);
41425 return {
41426 kind,
41427 displayParts,
41428 documentation: res.documentation,
41429 };
41430 }
41431 function getTsSymbolDisplayInfo(tsLS, checker, symbol, kind, ownerName) {
41432 const decl = symbol.valueDeclaration;
41433 if (decl === undefined ||
41434 (!ts__default["default"].isPropertyDeclaration(decl) && !ts__default["default"].isMethodDeclaration(decl) &&
41435 !isNamedClassDeclaration(decl)) ||
41436 !ts__default["default"].isIdentifier(decl.name)) {
41437 return null;
41438 }
41439 const res = tsLS.getQuickInfoAtPosition(decl.getSourceFile().fileName, decl.name.getStart());
41440 if (res === undefined) {
41441 return { kind, displayParts: [], documentation: [] };
41442 }
41443 const type = checker.getDeclaredTypeOfSymbol(symbol);
41444 const typeString = checker.typeToString(type);
41445 const displayParts = createDisplayParts(symbol.name, kind, ownerName ?? undefined, typeString);
41446 return {
41447 kind,
41448 displayParts,
41449 documentation: res.documentation,
41450 };
41451 }
41452
41453 /**
41454 * @license
41455 * Copyright Google LLC All Rights Reserved.
41456 *
41457 * Use of this source code is governed by an MIT-style license that can be
41458 * found in the LICENSE file at https://angular.io/license
41459 */
41460 /**
41461 * Return the node that most tightly encompasses the specified `position`.
41462 * @param node The starting node to start the top-down search.
41463 * @param position The target position within the `node`.
41464 */
41465 function findTightestNode(node, position) {
41466 if (node.getStart() <= position && position < node.getEnd()) {
41467 return node.forEachChild(c => findTightestNode(c, position)) ?? node;
41468 }
41469 return undefined;
41470 }
41471 function getParentClassDeclaration(startNode) {
41472 while (startNode) {
41473 if (ts__default["default"].isClassDeclaration(startNode)) {
41474 return startNode;
41475 }
41476 startNode = startNode.parent;
41477 }
41478 return undefined;
41479 }
41480 /**
41481 * Returns a property assignment from the assignment value if the property name
41482 * matches the specified `key`, or `null` if there is no match.
41483 */
41484 function getPropertyAssignmentFromValue(value, key) {
41485 const propAssignment = value.parent;
41486 if (!propAssignment || !ts__default["default"].isPropertyAssignment(propAssignment) ||
41487 propAssignment.name.getText() !== key) {
41488 return null;
41489 }
41490 return propAssignment;
41491 }
41492 /**
41493 * Given a decorator property assignment, return the ClassDeclaration node that corresponds to the
41494 * directive class the property applies to.
41495 * If the property assignment is not on a class decorator, no declaration is returned.
41496 *
41497 * For example,
41498 *
41499 * @Component({
41500 * template: '<div></div>'
41501 * ^^^^^^^^^^^^^^^^^^^^^^^---- property assignment
41502 * })
41503 * class AppComponent {}
41504 * ^---- class declaration node
41505 *
41506 * @param propAsgnNode property assignment
41507 */
41508 function getClassDeclFromDecoratorProp(propAsgnNode) {
41509 if (!propAsgnNode.parent || !ts__default["default"].isObjectLiteralExpression(propAsgnNode.parent)) {
41510 return;
41511 }
41512 const objLitExprNode = propAsgnNode.parent;
41513 if (!objLitExprNode.parent || !ts__default["default"].isCallExpression(objLitExprNode.parent)) {
41514 return;
41515 }
41516 const callExprNode = objLitExprNode.parent;
41517 if (!callExprNode.parent || !ts__default["default"].isDecorator(callExprNode.parent)) {
41518 return;
41519 }
41520 const decorator = callExprNode.parent;
41521 if (!decorator.parent || !ts__default["default"].isClassDeclaration(decorator.parent)) {
41522 return;
41523 }
41524 const classDeclNode = decorator.parent;
41525 return classDeclNode;
41526 }
41527 /**
41528 * Collects all member methods, including those from base classes.
41529 */
41530 function collectMemberMethods(clazz, typeChecker) {
41531 const members = [];
41532 const apparentProps = typeChecker.getTypeAtLocation(clazz).getApparentProperties();
41533 for (const prop of apparentProps) {
41534 if (prop.valueDeclaration && ts__default["default"].isMethodDeclaration(prop.valueDeclaration)) {
41535 members.push(prop.valueDeclaration);
41536 }
41537 }
41538 return members;
41539 }
41540
41541 /**
41542 * @license
41543 * Copyright Google LLC All Rights Reserved.
41544 *
41545 * Use of this source code is governed by an MIT-style license that can be
41546 * found in the LICENSE file at https://angular.io/license
41547 */
41548 function getTextSpanOfNode(node) {
41549 if (isTemplateNodeWithKeyAndValue(node)) {
41550 return toTextSpan(node.keySpan);
41551 }
41552 else if (node instanceof PropertyWrite || node instanceof BindingPipe ||
41553 node instanceof PropertyRead) {
41554 // The `name` part of a `PropertyWrite` and `BindingPipe` does not have its own AST
41555 // so there is no way to retrieve a `Symbol` for just the `name` via a specific node.
41556 return toTextSpan(node.nameSpan);
41557 }
41558 else {
41559 return toTextSpan(node.sourceSpan);
41560 }
41561 }
41562 function toTextSpan(span) {
41563 let start, end;
41564 if (span instanceof AbsoluteSourceSpan$1 || span instanceof ParseSpan) {
41565 start = span.start;
41566 end = span.end;
41567 }
41568 else {
41569 start = span.start.offset;
41570 end = span.end.offset;
41571 }
41572 return { start, length: end - start };
41573 }
41574 function isTemplateNodeWithKeyAndValue(node) {
41575 return isTemplateNode(node) && node.hasOwnProperty('keySpan');
41576 }
41577 function isWithinKeyValue(position, node) {
41578 let { keySpan, valueSpan } = node;
41579 if (valueSpan === undefined && node instanceof BoundEvent) {
41580 valueSpan = node.handlerSpan;
41581 }
41582 const isWithinKeyValue = isWithin(position, keySpan) || !!(valueSpan && isWithin(position, valueSpan));
41583 return isWithinKeyValue;
41584 }
41585 function isTemplateNode(node) {
41586 // Template node implements the Node interface so we cannot use instanceof.
41587 return node.sourceSpan instanceof ParseSourceSpan;
41588 }
41589 function getInlineTemplateInfoAtPosition(sf, position, compiler) {
41590 const expression = findTightestNode(sf, position);
41591 if (expression === undefined) {
41592 return undefined;
41593 }
41594 const classDecl = getParentClassDeclaration(expression);
41595 if (classDecl === undefined) {
41596 return undefined;
41597 }
41598 // Return `undefined` if the position is not on the template expression or the template resource
41599 // is not inline.
41600 const resources = compiler.getComponentResources(classDecl);
41601 if (resources === null || isExternalResource(resources.template) ||
41602 expression !== resources.template.expression) {
41603 return undefined;
41604 }
41605 const template = compiler.getTemplateTypeChecker().getTemplate(classDecl);
41606 if (template === null) {
41607 return undefined;
41608 }
41609 return { template, component: classDecl };
41610 }
41611 /**
41612 * Retrieves the `ts.ClassDeclaration` at a location along with its template nodes.
41613 */
41614 function getTemplateInfoAtPosition(fileName, position, compiler) {
41615 if (isTypeScriptFile(fileName)) {
41616 const sf = compiler.getCurrentProgram().getSourceFile(fileName);
41617 if (sf === undefined) {
41618 return undefined;
41619 }
41620 return getInlineTemplateInfoAtPosition(sf, position, compiler);
41621 }
41622 else {
41623 return getFirstComponentForTemplateFile(fileName, compiler);
41624 }
41625 }
41626 /**
41627 * First, attempt to sort component declarations by file name.
41628 * If the files are the same, sort by start location of the declaration.
41629 */
41630 function tsDeclarationSortComparator(a, b) {
41631 const aFile = a.getSourceFile().fileName;
41632 const bFile = b.getSourceFile().fileName;
41633 if (aFile < bFile) {
41634 return -1;
41635 }
41636 else if (aFile > bFile) {
41637 return 1;
41638 }
41639 else {
41640 return b.getFullStart() - a.getFullStart();
41641 }
41642 }
41643 function getFirstComponentForTemplateFile(fileName, compiler) {
41644 const templateTypeChecker = compiler.getTemplateTypeChecker();
41645 const components = compiler.getComponentsWithTemplateFile(fileName);
41646 const sortedComponents = Array.from(components).sort(tsDeclarationSortComparator);
41647 for (const component of sortedComponents) {
41648 if (!ts__default["default"].isClassDeclaration(component)) {
41649 continue;
41650 }
41651 const template = templateTypeChecker.getTemplate(component);
41652 if (template === null) {
41653 continue;
41654 }
41655 return { template, component };
41656 }
41657 return undefined;
41658 }
41659 /**
41660 * Given an attribute node, converts it to string form.
41661 */
41662 function toAttributeString(attribute) {
41663 if (attribute instanceof BoundEvent || attribute instanceof BoundAttribute) {
41664 return `[${attribute.name}]`;
41665 }
41666 else {
41667 return `[${attribute.name}=${attribute.valueSpan?.toString() ?? ''}]`;
41668 }
41669 }
41670 function getNodeName(node) {
41671 return node instanceof Template ? node.tagName : node.name;
41672 }
41673 /**
41674 * Given a template or element node, returns all attributes on the node.
41675 */
41676 function getAttributes(node) {
41677 const attributes = [...node.attributes, ...node.inputs, ...node.outputs];
41678 if (node instanceof Template) {
41679 attributes.push(...node.templateAttrs);
41680 }
41681 return attributes;
41682 }
41683 /**
41684 * Given two `Set`s, returns all items in the `left` which do not appear in the `right`.
41685 */
41686 function difference(left, right) {
41687 const result = new Set();
41688 for (const dir of left) {
41689 if (!right.has(dir)) {
41690 result.add(dir);
41691 }
41692 }
41693 return result;
41694 }
41695 /**
41696 * Given an element or template, determines which directives match because the tag is present. For
41697 * example, if a directive selector is `div[myAttr]`, this would match div elements but would not if
41698 * the selector were just `[myAttr]`. We find which directives are applied because of this tag by
41699 * elimination: compare the directive matches with the tag present against the directive matches
41700 * without it. The difference would be the directives which match because the tag is present.
41701 *
41702 * @param element The element or template node that the attribute/tag is part of.
41703 * @param directives The list of directives to match against.
41704 * @returns The list of directives matching the tag name via the strategy described above.
41705 */
41706 // TODO(atscott): Add unit tests for this and the one for attributes
41707 function getDirectiveMatchesForElementTag(element, directives) {
41708 const attributes = getAttributes(element);
41709 const allAttrs = attributes.map(toAttributeString);
41710 const allDirectiveMatches = getDirectiveMatchesForSelector(directives, getNodeName(element) + allAttrs.join(''));
41711 const matchesWithoutElement = getDirectiveMatchesForSelector(directives, allAttrs.join(''));
41712 return difference(allDirectiveMatches, matchesWithoutElement);
41713 }
41714 function makeElementSelector(element) {
41715 const attributes = getAttributes(element);
41716 const allAttrs = attributes.map(toAttributeString);
41717 return getNodeName(element) + allAttrs.join('');
41718 }
41719 /**
41720 * Given an attribute name, determines which directives match because the attribute is present. We
41721 * find which directives are applied because of this attribute by elimination: compare the directive
41722 * matches with the attribute present against the directive matches without it. The difference would
41723 * be the directives which match because the attribute is present.
41724 *
41725 * @param name The name of the attribute
41726 * @param hostNode The node which the attribute appears on
41727 * @param directives The list of directives to match against.
41728 * @returns The list of directives matching the tag name via the strategy described above.
41729 */
41730 function getDirectiveMatchesForAttribute(name, hostNode, directives) {
41731 const attributes = getAttributes(hostNode);
41732 const allAttrs = attributes.map(toAttributeString);
41733 const allDirectiveMatches = getDirectiveMatchesForSelector(directives, getNodeName(hostNode) + allAttrs.join(''));
41734 const attrsExcludingName = attributes.filter(a => a.name !== name).map(toAttributeString);
41735 const matchesWithoutAttr = getDirectiveMatchesForSelector(directives, getNodeName(hostNode) + attrsExcludingName.join(''));
41736 return difference(allDirectiveMatches, matchesWithoutAttr);
41737 }
41738 /**
41739 * Given a list of directives and a text to use as a selector, returns the directives which match
41740 * for the selector.
41741 */
41742 function getDirectiveMatchesForSelector(directives, selector) {
41743 const selectors = CssSelector.parse(selector);
41744 if (selectors.length === 0) {
41745 return new Set();
41746 }
41747 return new Set(directives.filter((dir) => {
41748 if (dir.selector === null) {
41749 return false;
41750 }
41751 const matcher = new SelectorMatcher();
41752 matcher.addSelectables(CssSelector.parse(dir.selector));
41753 return selectors.some(selector => matcher.match(selector, null));
41754 }));
41755 }
41756 /**
41757 * Returns a new `ts.SymbolDisplayPart` array which has the alias imports from the tcb filtered
41758 * out, i.e. `i0.NgForOf`.
41759 */
41760 function filterAliasImports(displayParts) {
41761 const tcbAliasImportRegex = /i\d+/;
41762 function isImportAlias(part) {
41763 return part.kind === ALIAS_NAME && tcbAliasImportRegex.test(part.text);
41764 }
41765 function isDotPunctuation(part) {
41766 return part.kind === SYMBOL_PUNC && part.text === '.';
41767 }
41768 return displayParts.filter((part, i) => {
41769 const previousPart = displayParts[i - 1];
41770 const nextPart = displayParts[i + 1];
41771 const aliasNameFollowedByDot = isImportAlias(part) && nextPart !== undefined && isDotPunctuation(nextPart);
41772 const dotPrecededByAlias = isDotPunctuation(part) && previousPart !== undefined && isImportAlias(previousPart);
41773 return !aliasNameFollowedByDot && !dotPrecededByAlias;
41774 });
41775 }
41776 function isDollarEvent(n) {
41777 return n instanceof PropertyRead && n.name === '$event' &&
41778 n.receiver instanceof ImplicitReceiver && !(n.receiver instanceof ThisReceiver);
41779 }
41780 /**
41781 * Returns a new array formed by applying a given callback function to each element of the array,
41782 * and then flattening the result by one level.
41783 */
41784 function flatMap(items, f) {
41785 const results = [];
41786 for (const x of items) {
41787 results.push(...f(x));
41788 }
41789 return results;
41790 }
41791 function isTypeScriptFile(fileName) {
41792 return fileName.endsWith('.ts');
41793 }
41794 function isWithin(position, span) {
41795 let start, end;
41796 if (span instanceof ParseSourceSpan) {
41797 start = span.start.offset;
41798 end = span.end.offset;
41799 }
41800 else {
41801 start = span.start;
41802 end = span.end;
41803 }
41804 // Note both start and end are inclusive because we want to match conditions
41805 // like ¦start and end¦ where ¦ is the cursor.
41806 return start <= position && position <= end;
41807 }
41808 /**
41809 * For a given location in a shim file, retrieves the corresponding file url for the template and
41810 * the span in the template.
41811 */
41812 function getTemplateLocationFromShimLocation(templateTypeChecker, shimPath, positionInShimFile) {
41813 const mapping = templateTypeChecker.getTemplateMappingAtShimLocation({ shimPath, positionInShimFile });
41814 if (mapping === null) {
41815 return null;
41816 }
41817 const { templateSourceMapping, span } = mapping;
41818 let templateUrl;
41819 if (templateSourceMapping.type === 'direct') {
41820 templateUrl = absoluteFromSourceFile(templateSourceMapping.node.getSourceFile());
41821 }
41822 else if (templateSourceMapping.type === 'external') {
41823 templateUrl = absoluteFrom(templateSourceMapping.templateUrl);
41824 }
41825 else {
41826 // This includes indirect mappings, which are difficult to map directly to the code
41827 // location. Diagnostics similarly return a synthetic template string for this case rather
41828 // than a real location.
41829 return null;
41830 }
41831 return { templateUrl, span };
41832 }
41833 function isBoundEventWithSyntheticHandler(event) {
41834 // An event binding with no value (e.g. `(event|)`) parses to a `BoundEvent` with a
41835 // `LiteralPrimitive` handler with value `'ERROR'`, as opposed to a property binding with no
41836 // value which has an `EmptyExpr` as its value. This is a synthetic node created by the binding
41837 // parser, and is not suitable to use for Language Service analysis. Skip it.
41838 //
41839 // TODO(alxhub): modify the parser to generate an `EmptyExpr` instead.
41840 let handler = event.handler;
41841 if (handler instanceof ASTWithSource) {
41842 handler = handler.ast;
41843 }
41844 if (handler instanceof LiteralPrimitive && handler.value === 'ERROR') {
41845 return true;
41846 }
41847 return false;
41848 }
41849
41850 /**
41851 * @license
41852 * Copyright Google LLC All Rights Reserved.
41853 *
41854 * Use of this source code is governed by an MIT-style license that can be
41855 * found in the LICENSE file at https://angular.io/license
41856 */
41857 const PRE_COMPILED_STYLE_EXTENSIONS = ['.scss', '.sass', '.less', '.styl'];
41858 class LanguageServiceAdapter {
41859 constructor(project) {
41860 this.project = project;
41861 this.entryPoint = null;
41862 this.constructionDiagnostics = [];
41863 this.ignoreForEmit = new Set();
41864 this.factoryTracker = null; // no .ngfactory shims
41865 this.unifiedModulesHost = null; // only used in Bazel
41866 /**
41867 * Map of resource filenames to the version of the file last read via `readResource`.
41868 *
41869 * Used to implement `getModifiedResourceFiles`.
41870 */
41871 this.lastReadResourceVersion = new Map();
41872 this.rootDirs = getRootDirs(this, project.getCompilationSettings());
41873 }
41874 resourceNameToFileName(url, fromFile, fallbackResolve) {
41875 // If we are trying to resolve a `.css` file, see if we can find a pre-compiled file with the
41876 // same name instead. That way, we can provide go-to-definition for the pre-compiled files which
41877 // would generally be the desired behavior.
41878 if (url.endsWith('.css')) {
41879 const styleUrl = path__namespace.resolve(fromFile, '..', url);
41880 for (const ext of PRE_COMPILED_STYLE_EXTENSIONS) {
41881 const precompiledFileUrl = styleUrl.replace(/\.css$/, ext);
41882 if (this.fileExists(precompiledFileUrl)) {
41883 return precompiledFileUrl;
41884 }
41885 }
41886 }
41887 return fallbackResolve?.(url, fromFile) ?? null;
41888 }
41889 isShim(sf) {
41890 return isShim(sf);
41891 }
41892 fileExists(fileName) {
41893 return this.project.fileExists(fileName);
41894 }
41895 readFile(fileName) {
41896 return this.project.readFile(fileName);
41897 }
41898 getCurrentDirectory() {
41899 return this.project.getCurrentDirectory();
41900 }
41901 getCanonicalFileName(fileName) {
41902 return this.project.projectService.toCanonicalFileName(fileName);
41903 }
41904 /**
41905 * Return the real path of a symlink. This method is required in order to
41906 * resolve symlinks in node_modules.
41907 */
41908 realpath(path) {
41909 return this.project.realpath?.(path) ?? path;
41910 }
41911 /**
41912 * readResource() is an Angular-specific method for reading files that are not
41913 * managed by the TS compiler host, namely templates and stylesheets.
41914 * It is a method on ExtendedTsCompilerHost, see
41915 * packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts
41916 */
41917 readResource(fileName) {
41918 if (isTypeScriptFile(fileName)) {
41919 throw new Error(`readResource() should not be called on TS file: ${fileName}`);
41920 }
41921 // Calling getScriptSnapshot() will actually create a ScriptInfo if it does
41922 // not exist! The same applies for getScriptVersion().
41923 // getScriptInfo() will not create one if it does not exist.
41924 // In this case, we *want* a script info to be created so that we could
41925 // keep track of its version.
41926 const snapshot = this.project.getScriptSnapshot(fileName);
41927 if (!snapshot) {
41928 // This would fail if the file does not exist, or readFile() fails for
41929 // whatever reasons.
41930 throw new Error(`Failed to get script snapshot while trying to read ${fileName}`);
41931 }
41932 const version = this.project.getScriptVersion(fileName);
41933 this.lastReadResourceVersion.set(fileName, version);
41934 return snapshot.getText(0, snapshot.getLength());
41935 }
41936 getModifiedResourceFiles() {
41937 const modifiedFiles = new Set();
41938 for (const [fileName, oldVersion] of this.lastReadResourceVersion) {
41939 if (this.project.getScriptVersion(fileName) !== oldVersion) {
41940 modifiedFiles.add(fileName);
41941 }
41942 }
41943 return modifiedFiles.size > 0 ? modifiedFiles : undefined;
41944 }
41945 }
41946 /**
41947 * Used to read configuration files.
41948 *
41949 * A language service parse configuration host is independent of the adapter
41950 * because signatures of calls like `FileSystem#readFile` are a bit stricter
41951 * than those on the adapter.
41952 */
41953 class LSParseConfigHost {
41954 constructor(serverHost) {
41955 this.serverHost = serverHost;
41956 }
41957 exists(path) {
41958 return this.serverHost.fileExists(path) || this.serverHost.directoryExists(path);
41959 }
41960 readFile(path) {
41961 const content = this.serverHost.readFile(path);
41962 if (content === undefined) {
41963 throw new Error(`LanguageServiceFS#readFile called on unavailable file ${path}`);
41964 }
41965 return content;
41966 }
41967 lstat(path) {
41968 return {
41969 isFile: () => {
41970 return this.serverHost.fileExists(path);
41971 },
41972 isDirectory: () => {
41973 return this.serverHost.directoryExists(path);
41974 },
41975 isSymbolicLink: () => {
41976 throw new Error(`LanguageServiceFS#lstat#isSymbolicLink not implemented`);
41977 },
41978 };
41979 }
41980 pwd() {
41981 return this.serverHost.getCurrentDirectory();
41982 }
41983 extname(path) {
41984 return path__namespace.extname(path);
41985 }
41986 resolve(...paths) {
41987 return path__namespace.resolve(...paths);
41988 }
41989 dirname(file) {
41990 return path__namespace.dirname(file);
41991 }
41992 join(basePath, ...paths) {
41993 return path__namespace.join(basePath, ...paths);
41994 }
41995 }
41996
41997 /**
41998 * @license
41999 * Copyright Google LLC All Rights Reserved.
42000 *
42001 * Use of this source code is governed by an MIT-style license that can be
42002 * found in the LICENSE file at https://angular.io/license
42003 */
42004 /**
42005 * Manages the `NgCompiler` instance which backs the language service, updating or replacing it as
42006 * needed to produce an up-to-date understanding of the current program.
42007 *
42008 * TODO(alxhub): currently the options used for the compiler are specified at `CompilerFactory`
42009 * construction, and are not changable. In a real project, users can update `tsconfig.json`. We need
42010 * to properly handle a change in the compiler options, either by having an API to update the
42011 * `CompilerFactory` to use new options, or by replacing it entirely.
42012 */
42013 class CompilerFactory {
42014 constructor(adapter, programStrategy, options) {
42015 this.adapter = adapter;
42016 this.programStrategy = programStrategy;
42017 this.options = options;
42018 this.incrementalStrategy = new TrackedIncrementalBuildStrategy();
42019 this.compiler = null;
42020 }
42021 getOrCreate() {
42022 const program = this.programStrategy.getProgram();
42023 const modifiedResourceFiles = new Set();
42024 for (const fileName of this.adapter.getModifiedResourceFiles() ?? []) {
42025 modifiedResourceFiles.add(resolve(fileName));
42026 }
42027 if (this.compiler !== null && program === this.compiler.getCurrentProgram()) {
42028 if (modifiedResourceFiles.size > 0) {
42029 // Only resource files have changed since the last NgCompiler was created.
42030 const ticket = resourceChangeTicket(this.compiler, modifiedResourceFiles);
42031 this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
42032 }
42033 else {
42034 // The previous NgCompiler is being reused, but we still want to reset its performance
42035 // tracker to capture only the operations that are needed to service the current request.
42036 this.compiler.perfRecorder.reset();
42037 }
42038 return this.compiler;
42039 }
42040 let ticket;
42041 if (this.compiler === null) {
42042 ticket = freshCompilationTicket(program, this.options, this.incrementalStrategy, this.programStrategy,
42043 /* perfRecorder */ null, true, true);
42044 }
42045 else {
42046 ticket = incrementalFromCompilerTicket(this.compiler, program, this.incrementalStrategy, this.programStrategy, modifiedResourceFiles, /* perfRecorder */ null);
42047 }
42048 this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
42049 return this.compiler;
42050 }
42051 }
42052
42053 /**
42054 * @license
42055 * Copyright Google LLC All Rights Reserved.
42056 *
42057 * Use of this source code is governed by an MIT-style license that can be
42058 * found in the LICENSE file at https://angular.io/license
42059 */
42060 /**
42061 * Differentiates different kinds of `AttributeCompletion`s.
42062 */
42063 var AttributeCompletionKind;
42064 (function (AttributeCompletionKind) {
42065 /**
42066 * Completion of an attribute from the HTML schema.
42067 *
42068 * Attributes often have a corresponding DOM property of the same name.
42069 */
42070 AttributeCompletionKind[AttributeCompletionKind["DomAttribute"] = 0] = "DomAttribute";
42071 /**
42072 * Completion of a property from the DOM schema.
42073 *
42074 * `DomProperty` completions are generated only for properties which don't share their name with
42075 * an HTML attribute.
42076 */
42077 AttributeCompletionKind[AttributeCompletionKind["DomProperty"] = 1] = "DomProperty";
42078 /**
42079 * Completion of an event from the DOM schema.
42080 */
42081 AttributeCompletionKind[AttributeCompletionKind["DomEvent"] = 2] = "DomEvent";
42082 /**
42083 * Completion of an attribute that results in a new directive being matched on an element.
42084 */
42085 AttributeCompletionKind[AttributeCompletionKind["DirectiveAttribute"] = 3] = "DirectiveAttribute";
42086 /**
42087 * Completion of an attribute that results in a new structural directive being matched on an
42088 * element.
42089 */
42090 AttributeCompletionKind[AttributeCompletionKind["StructuralDirectiveAttribute"] = 4] = "StructuralDirectiveAttribute";
42091 /**
42092 * Completion of an input from a directive which is either present on the element, or becomes
42093 * present after the addition of this attribute.
42094 */
42095 AttributeCompletionKind[AttributeCompletionKind["DirectiveInput"] = 5] = "DirectiveInput";
42096 /**
42097 * Completion of an output from a directive which is either present on the element, or becomes
42098 * present after the addition of this attribute.
42099 */
42100 AttributeCompletionKind[AttributeCompletionKind["DirectiveOutput"] = 6] = "DirectiveOutput";
42101 })(AttributeCompletionKind || (AttributeCompletionKind = {}));
42102 /**
42103 * Given an element and its context, produce a `Map` of all possible attribute completions.
42104 *
42105 * 3 kinds of attributes are considered for completion, from highest to lowest priority:
42106 *
42107 * 1. Inputs/outputs of directives present on the element already.
42108 * 2. Inputs/outputs of directives that are not present on the element, but which would become
42109 * present if such a binding is added.
42110 * 3. Attributes from the DOM schema for the element.
42111 *
42112 * The priority of these options determines which completions are added to the `Map`. If a directive
42113 * input shares the same name as a DOM attribute, the `Map` will reflect the directive input
42114 * completion, not the DOM completion for that name.
42115 */
42116 function buildAttributeCompletionTable(component, element, checker) {
42117 const table = new Map();
42118 // Use the `ElementSymbol` or `TemplateSymbol` to iterate over directives present on the node, and
42119 // their inputs/outputs. These have the highest priority of completion results.
42120 const symbol = checker.getSymbolOfNode(element, component);
42121 const presentDirectives = new Set();
42122 if (symbol !== null) {
42123 // An `ElementSymbol` was available. This means inputs and outputs for directives on the
42124 // element can be added to the completion table.
42125 for (const dirSymbol of symbol.directives) {
42126 const directive = dirSymbol.tsSymbol.valueDeclaration;
42127 if (!ts__default["default"].isClassDeclaration(directive)) {
42128 continue;
42129 }
42130 presentDirectives.add(directive);
42131 const meta = checker.getDirectiveMetadata(directive);
42132 if (meta === null) {
42133 continue;
42134 }
42135 for (const [classPropertyName, propertyName] of meta.inputs) {
42136 if (table.has(propertyName)) {
42137 continue;
42138 }
42139 table.set(propertyName, {
42140 kind: AttributeCompletionKind.DirectiveInput,
42141 propertyName,
42142 directive: dirSymbol,
42143 classPropertyName,
42144 twoWayBindingSupported: meta.outputs.hasBindingPropertyName(propertyName + 'Change'),
42145 });
42146 }
42147 for (const [classPropertyName, propertyName] of meta.outputs) {
42148 if (table.has(propertyName)) {
42149 continue;
42150 }
42151 table.set(propertyName, {
42152 kind: AttributeCompletionKind.DirectiveOutput,
42153 eventName: propertyName,
42154 directive: dirSymbol,
42155 classPropertyName,
42156 });
42157 }
42158 }
42159 }
42160 // Next, explore hypothetical directives and determine if the addition of any single attributes
42161 // can cause the directive to match the element.
42162 const directivesInScope = checker.getDirectivesInScope(component);
42163 if (directivesInScope !== null) {
42164 const elementSelector = makeElementSelector(element);
42165 for (const dirInScope of directivesInScope) {
42166 const directive = dirInScope.tsSymbol.valueDeclaration;
42167 // Skip directives that are present on the element.
42168 if (!ts__default["default"].isClassDeclaration(directive) || presentDirectives.has(directive)) {
42169 continue;
42170 }
42171 const meta = checker.getDirectiveMetadata(directive);
42172 if (meta === null || meta.selector === null) {
42173 continue;
42174 }
42175 if (!meta.isStructural) {
42176 // For non-structural directives, the directive's attribute selector(s) are matched against
42177 // a hypothetical version of the element with those attributes. A match indicates that
42178 // adding that attribute/input/output binding would cause the directive to become present,
42179 // meaning that such a binding is a valid completion.
42180 const selectors = CssSelector.parse(meta.selector);
42181 const matcher = new SelectorMatcher();
42182 matcher.addSelectables(selectors);
42183 for (const selector of selectors) {
42184 for (const [attrName, attrValue] of selectorAttributes(selector)) {
42185 if (attrValue !== '') {
42186 // This attribute selector requires a value, which is not supported in completion.
42187 continue;
42188 }
42189 if (table.has(attrName)) {
42190 // Skip this attribute as there's already a binding for it.
42191 continue;
42192 }
42193 // Check whether adding this attribute would cause the directive to start matching.
42194 const newElementSelector = elementSelector + `[${attrName}]`;
42195 if (!matcher.match(CssSelector.parse(newElementSelector)[0], null)) {
42196 // Nope, move on with our lives.
42197 continue;
42198 }
42199 // Adding this attribute causes a new directive to be matched. Decide how to categorize
42200 // it based on the directive's inputs and outputs.
42201 if (meta.inputs.hasBindingPropertyName(attrName)) {
42202 // This attribute corresponds to an input binding.
42203 table.set(attrName, {
42204 kind: AttributeCompletionKind.DirectiveInput,
42205 directive: dirInScope,
42206 propertyName: attrName,
42207 classPropertyName: meta.inputs.getByBindingPropertyName(attrName)[0].classPropertyName,
42208 twoWayBindingSupported: meta.outputs.hasBindingPropertyName(attrName + 'Change'),
42209 });
42210 }
42211 else if (meta.outputs.hasBindingPropertyName(attrName)) {
42212 // This attribute corresponds to an output binding.
42213 table.set(attrName, {
42214 kind: AttributeCompletionKind.DirectiveOutput,
42215 directive: dirInScope,
42216 eventName: attrName,
42217 classPropertyName: meta.outputs.getByBindingPropertyName(attrName)[0].classPropertyName,
42218 });
42219 }
42220 else {
42221 // This attribute causes a new directive to be matched, but does not also correspond
42222 // to an input or output binding.
42223 table.set(attrName, {
42224 kind: AttributeCompletionKind.DirectiveAttribute,
42225 attribute: attrName,
42226 directive: dirInScope,
42227 });
42228 }
42229 }
42230 }
42231 }
42232 else {
42233 // Hypothetically matching a structural directive is a litle different than a plain
42234 // directive. Use of the '*' structural directive syntactic sugar means that the actual
42235 // directive is applied to a plain <ng-template> node, not the existing element with any
42236 // other attributes it might already have.
42237 // Additionally, more than one attribute/input might need to be present in order for the
42238 // directive to match (e.g. `ngFor` has a selector of `[ngFor][ngForOf]`). This gets a
42239 // little tricky.
42240 const structuralAttributes = getStructuralAttributes(meta);
42241 for (const attrName of structuralAttributes) {
42242 table.set(attrName, {
42243 kind: AttributeCompletionKind.StructuralDirectiveAttribute,
42244 attribute: attrName,
42245 directive: dirInScope,
42246 });
42247 }
42248 }
42249 }
42250 }
42251 // Finally, add any DOM attributes not already covered by inputs.
42252 if (element instanceof Element$1) {
42253 for (const { attribute, property } of checker.getPotentialDomBindings(element.name)) {
42254 const isAlsoProperty = attribute === property;
42255 if (!table.has(attribute) && isAlsoProperty) {
42256 table.set(attribute, {
42257 kind: AttributeCompletionKind.DomAttribute,
42258 attribute,
42259 isAlsoProperty,
42260 });
42261 }
42262 }
42263 for (const event of checker.getPotentialDomEvents(element.name)) {
42264 table.set(event, {
42265 kind: AttributeCompletionKind.DomEvent,
42266 eventName: event,
42267 });
42268 }
42269 }
42270 return table;
42271 }
42272 function buildSnippet(insertSnippet, text) {
42273 return insertSnippet ? `${text}="$1"` : undefined;
42274 }
42275 /**
42276 * Given an `AttributeCompletion`, add any available completions to a `ts.CompletionEntry` array of
42277 * results.
42278 *
42279 * The kind of completions generated depends on whether the current context is an attribute context
42280 * or not. For example, completing on `<element attr|>` will generate two results: `attribute` and
42281 * `[attribute]` - either a static attribute can be generated, or a property binding. However,
42282 * `<element [attr|]>` is not an attribute context, and so only the property completion `attribute`
42283 * is generated. Note that this completion does not have the `[]` property binding sugar as its
42284 * implicitly present in a property binding context (we're already completing within an `[attr|]`
42285 * expression).
42286 *
42287 * If the `insertSnippet` is `true`, the completion entries should includes the property or event
42288 * binding sugar in some case. For Example `<div (my¦) />`, the `replacementSpan` is `(my)`, and the
42289 * `insertText` is `(myOutput)="$0"`.
42290 */
42291 function addAttributeCompletionEntries(entries, completion, isAttributeContext, isElementContext, replacementSpan, insertSnippet) {
42292 switch (completion.kind) {
42293 case AttributeCompletionKind.DirectiveAttribute: {
42294 entries.push({
42295 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.DIRECTIVE),
42296 name: completion.attribute,
42297 sortText: completion.attribute,
42298 replacementSpan,
42299 });
42300 break;
42301 }
42302 case AttributeCompletionKind.StructuralDirectiveAttribute: {
42303 // In an element, the completion is offered with a leading '*' to activate the structural
42304 // directive. Once present, the structural attribute will be parsed as a template and not an
42305 // element, and the prefix is no longer necessary.
42306 const prefix = isElementContext ? '*' : '';
42307 entries.push({
42308 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.DIRECTIVE),
42309 name: prefix + completion.attribute,
42310 insertText: buildSnippet(insertSnippet, prefix + completion.attribute),
42311 isSnippet: insertSnippet,
42312 sortText: prefix + completion.attribute,
42313 replacementSpan,
42314 });
42315 break;
42316 }
42317 case AttributeCompletionKind.DirectiveInput: {
42318 if (isAttributeContext || insertSnippet) {
42319 // Offer a completion of a property binding.
42320 entries.push({
42321 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
42322 name: `[${completion.propertyName}]`,
42323 insertText: buildSnippet(insertSnippet, `[${completion.propertyName}]`),
42324 isSnippet: insertSnippet,
42325 sortText: completion.propertyName,
42326 replacementSpan,
42327 });
42328 // If the directive supports banana-in-a-box for this input, offer that as well.
42329 if (completion.twoWayBindingSupported) {
42330 entries.push({
42331 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
42332 name: `[(${completion.propertyName})]`,
42333 insertText: buildSnippet(insertSnippet, `[(${completion.propertyName})]`),
42334 isSnippet: insertSnippet,
42335 // This completion should sort after the property binding.
42336 sortText: completion.propertyName + '_1',
42337 replacementSpan,
42338 });
42339 }
42340 // Offer a completion of the input binding as an attribute.
42341 entries.push({
42342 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.ATTRIBUTE),
42343 name: completion.propertyName,
42344 insertText: buildSnippet(insertSnippet, completion.propertyName),
42345 isSnippet: insertSnippet,
42346 // This completion should sort after both property binding options (one-way and two-way).
42347 sortText: completion.propertyName + '_2',
42348 replacementSpan,
42349 });
42350 }
42351 else {
42352 entries.push({
42353 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
42354 name: completion.propertyName,
42355 insertText: buildSnippet(insertSnippet, completion.propertyName),
42356 isSnippet: insertSnippet,
42357 sortText: completion.propertyName,
42358 replacementSpan,
42359 });
42360 }
42361 break;
42362 }
42363 case AttributeCompletionKind.DirectiveOutput: {
42364 if (isAttributeContext || insertSnippet) {
42365 entries.push({
42366 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.EVENT),
42367 name: `(${completion.eventName})`,
42368 insertText: buildSnippet(insertSnippet, `(${completion.eventName})`),
42369 isSnippet: insertSnippet,
42370 sortText: completion.eventName,
42371 replacementSpan,
42372 });
42373 }
42374 else {
42375 entries.push({
42376 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.EVENT),
42377 name: completion.eventName,
42378 insertText: buildSnippet(insertSnippet, completion.eventName),
42379 isSnippet: insertSnippet,
42380 sortText: completion.eventName,
42381 replacementSpan,
42382 });
42383 }
42384 break;
42385 }
42386 case AttributeCompletionKind.DomAttribute: {
42387 if ((isAttributeContext || insertSnippet) && completion.isAlsoProperty) {
42388 // Offer a completion of a property binding to the DOM property.
42389 entries.push({
42390 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
42391 name: `[${completion.attribute}]`,
42392 insertText: buildSnippet(insertSnippet, `[${completion.attribute}]`),
42393 isSnippet: insertSnippet,
42394 // In the case of DOM attributes, the property binding should sort after the attribute
42395 // binding.
42396 sortText: completion.attribute + '_1',
42397 replacementSpan,
42398 });
42399 }
42400 break;
42401 }
42402 case AttributeCompletionKind.DomProperty: {
42403 if (!isAttributeContext) {
42404 entries.push({
42405 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
42406 name: completion.property,
42407 insertText: buildSnippet(insertSnippet, completion.property),
42408 isSnippet: insertSnippet,
42409 sortText: completion.property,
42410 replacementSpan,
42411 });
42412 }
42413 break;
42414 }
42415 case AttributeCompletionKind.DomEvent: {
42416 entries.push({
42417 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.EVENT),
42418 name: `(${completion.eventName})`,
42419 insertText: buildSnippet(insertSnippet, `(${completion.eventName})`),
42420 isSnippet: insertSnippet,
42421 sortText: completion.eventName,
42422 replacementSpan,
42423 });
42424 break;
42425 }
42426 }
42427 }
42428 function getAttributeCompletionSymbol(completion, checker) {
42429 switch (completion.kind) {
42430 case AttributeCompletionKind.DomAttribute:
42431 case AttributeCompletionKind.DomEvent:
42432 case AttributeCompletionKind.DomProperty:
42433 return null;
42434 case AttributeCompletionKind.DirectiveAttribute:
42435 case AttributeCompletionKind.StructuralDirectiveAttribute:
42436 return completion.directive.tsSymbol;
42437 case AttributeCompletionKind.DirectiveInput:
42438 case AttributeCompletionKind.DirectiveOutput:
42439 return checker.getDeclaredTypeOfSymbol(completion.directive.tsSymbol)
42440 .getProperty(completion.classPropertyName) ??
42441 null;
42442 }
42443 }
42444 /**
42445 * Iterates over `CssSelector` attributes, which are internally represented in a zipped array style
42446 * which is not conducive to straightforward iteration.
42447 */
42448 function* selectorAttributes(selector) {
42449 for (let i = 0; i < selector.attrs.length; i += 2) {
42450 yield [selector.attrs[0], selector.attrs[1]];
42451 }
42452 }
42453 function getStructuralAttributes(meta) {
42454 if (meta.selector === null) {
42455 return [];
42456 }
42457 const structuralAttributes = [];
42458 const selectors = CssSelector.parse(meta.selector);
42459 for (const selector of selectors) {
42460 if (selector.element !== null && selector.element !== 'ng-template') {
42461 // This particular selector does not apply under structural directive syntax.
42462 continue;
42463 }
42464 // Every attribute of this selector must be name-only - no required values.
42465 const attributeSelectors = Array.from(selectorAttributes(selector));
42466 if (!attributeSelectors.every(([_, attrValue]) => attrValue === '')) {
42467 continue;
42468 }
42469 // Get every named selector.
42470 const attributes = attributeSelectors.map(([attrName, _]) => attrName);
42471 // Find the shortest attribute. This is the structural directive "base", and all potential
42472 // input bindings must begin with the base. E.g. in `*ngFor="let a of b"`, `ngFor` is the
42473 // base attribute, and the `of` binding key corresponds to an input of `ngForOf`.
42474 const baseAttr = attributes.reduce((prev, curr) => prev === null || curr.length < prev.length ? curr : prev, null);
42475 if (baseAttr === null) {
42476 // No attributes in this selector?
42477 continue;
42478 }
42479 // Validate that the attributes are compatible with use as a structural directive.
42480 const isValid = (attr) => {
42481 // The base attribute is valid by default.
42482 if (attr === baseAttr) {
42483 return true;
42484 }
42485 // Non-base attributes must all be prefixed with the base attribute.
42486 if (!attr.startsWith(baseAttr)) {
42487 return false;
42488 }
42489 // Non-base attributes must also correspond to directive inputs.
42490 if (!meta.inputs.hasBindingPropertyName(attr)) {
42491 return false;
42492 }
42493 // This attribute is compatible.
42494 return true;
42495 };
42496 if (!attributes.every(isValid)) {
42497 continue;
42498 }
42499 // This attribute is valid as a structural attribute for this directive.
42500 structuralAttributes.push(baseAttr);
42501 }
42502 return structuralAttributes;
42503 }
42504
42505 /**
42506 * @license
42507 * Copyright Google LLC All Rights Reserved.
42508 *
42509 * Use of this source code is governed by an MIT-style license that can be
42510 * found in the LICENSE file at https://angular.io/license
42511 */
42512 /**
42513 * Differentiates the various kinds of `TargetNode`s.
42514 */
42515 var TargetNodeKind;
42516 (function (TargetNodeKind) {
42517 TargetNodeKind[TargetNodeKind["RawExpression"] = 0] = "RawExpression";
42518 TargetNodeKind[TargetNodeKind["CallExpressionInArgContext"] = 1] = "CallExpressionInArgContext";
42519 TargetNodeKind[TargetNodeKind["RawTemplateNode"] = 2] = "RawTemplateNode";
42520 TargetNodeKind[TargetNodeKind["ElementInTagContext"] = 3] = "ElementInTagContext";
42521 TargetNodeKind[TargetNodeKind["ElementInBodyContext"] = 4] = "ElementInBodyContext";
42522 TargetNodeKind[TargetNodeKind["AttributeInKeyContext"] = 5] = "AttributeInKeyContext";
42523 TargetNodeKind[TargetNodeKind["AttributeInValueContext"] = 6] = "AttributeInValueContext";
42524 TargetNodeKind[TargetNodeKind["TwoWayBindingContext"] = 7] = "TwoWayBindingContext";
42525 })(TargetNodeKind || (TargetNodeKind = {}));
42526 /**
42527 * Special marker AST that can be used when the cursor is within the `sourceSpan` but not
42528 * the key or value span of a node with key/value spans.
42529 */
42530 class OutsideKeyValueMarkerAst extends AST {
42531 visit() {
42532 return null;
42533 }
42534 }
42535 /**
42536 * This special marker is added to the path when the cursor is within the sourceSpan but not the key
42537 * or value span of a node with key/value spans.
42538 */
42539 const OUTSIDE_K_V_MARKER = new OutsideKeyValueMarkerAst(new ParseSpan(-1, -1), new AbsoluteSourceSpan$1(-1, -1));
42540 /**
42541 * Return the template AST node or expression AST node that most accurately
42542 * represents the node at the specified cursor `position`.
42543 *
42544 * @param template AST tree of the template
42545 * @param position target cursor position
42546 */
42547 function getTargetAtPosition(template, position) {
42548 const path = TemplateTargetVisitor.visitTemplate(template, position);
42549 if (path.length === 0) {
42550 return null;
42551 }
42552 const candidate = path[path.length - 1];
42553 // Walk up the result nodes to find the nearest `t.Template` which contains the targeted node.
42554 let context = null;
42555 for (let i = path.length - 2; i >= 0; i--) {
42556 const node = path[i];
42557 if (node instanceof Template) {
42558 context = node;
42559 break;
42560 }
42561 }
42562 // Given the candidate node, determine the full targeted context.
42563 let nodeInContext;
42564 if (candidate instanceof Call && isWithin(position, candidate.argumentSpan)) {
42565 nodeInContext = {
42566 kind: TargetNodeKind.CallExpressionInArgContext,
42567 node: candidate,
42568 };
42569 }
42570 else if (candidate instanceof AST) {
42571 const parents = path.filter((value) => value instanceof AST);
42572 // Remove the current node from the parents list.
42573 parents.pop();
42574 nodeInContext = {
42575 kind: TargetNodeKind.RawExpression,
42576 node: candidate,
42577 parents,
42578 };
42579 }
42580 else if (candidate instanceof Element$1) {
42581 // Elements have two contexts: the tag context (position is within the element tag) or the
42582 // element body context (position is outside of the tag name, but still in the element).
42583 // Calculate the end of the element tag name. Any position beyond this is in the element body.
42584 const tagEndPos = candidate.sourceSpan.start.offset + 1 /* '<' element open */ + candidate.name.length;
42585 if (position > tagEndPos) {
42586 // Position is within the element body
42587 nodeInContext = {
42588 kind: TargetNodeKind.ElementInBodyContext,
42589 node: candidate,
42590 };
42591 }
42592 else {
42593 nodeInContext = {
42594 kind: TargetNodeKind.ElementInTagContext,
42595 node: candidate,
42596 };
42597 }
42598 }
42599 else if ((candidate instanceof BoundAttribute || candidate instanceof BoundEvent ||
42600 candidate instanceof TextAttribute) &&
42601 candidate.keySpan !== undefined) {
42602 const previousCandidate = path[path.length - 2];
42603 if (candidate instanceof BoundEvent && previousCandidate instanceof BoundAttribute &&
42604 candidate.name === previousCandidate.name + 'Change') {
42605 const boundAttribute = previousCandidate;
42606 const boundEvent = candidate;
42607 nodeInContext = {
42608 kind: TargetNodeKind.TwoWayBindingContext,
42609 nodes: [boundAttribute, boundEvent],
42610 };
42611 }
42612 else if (isWithin(position, candidate.keySpan)) {
42613 nodeInContext = {
42614 kind: TargetNodeKind.AttributeInKeyContext,
42615 node: candidate,
42616 };
42617 }
42618 else {
42619 nodeInContext = {
42620 kind: TargetNodeKind.AttributeInValueContext,
42621 node: candidate,
42622 };
42623 }
42624 }
42625 else {
42626 nodeInContext = {
42627 kind: TargetNodeKind.RawTemplateNode,
42628 node: candidate,
42629 };
42630 }
42631 let parent = null;
42632 if (nodeInContext.kind === TargetNodeKind.TwoWayBindingContext && path.length >= 3) {
42633 parent = path[path.length - 3];
42634 }
42635 else if (path.length >= 2) {
42636 parent = path[path.length - 2];
42637 }
42638 return { position, context: nodeInContext, template: context, parent };
42639 }
42640 /**
42641 * Visitor which, given a position and a template, identifies the node within the template at that
42642 * position, as well as records the path of increasingly nested nodes that were traversed to reach
42643 * that position.
42644 */
42645 class TemplateTargetVisitor {
42646 // Position must be absolute in the source file.
42647 constructor(position) {
42648 this.position = position;
42649 // We need to keep a path instead of the last node because we might need more
42650 // context for the last node, for example what is the parent node?
42651 this.path = [];
42652 }
42653 static visitTemplate(template, position) {
42654 const visitor = new TemplateTargetVisitor(position);
42655 visitor.visitAll(template);
42656 const { path } = visitor;
42657 const strictPath = path.filter(v => v !== OUTSIDE_K_V_MARKER);
42658 const candidate = strictPath[strictPath.length - 1];
42659 const matchedASourceSpanButNotAKvSpan = path.some(v => v === OUTSIDE_K_V_MARKER);
42660 if (matchedASourceSpanButNotAKvSpan &&
42661 (candidate instanceof Template || candidate instanceof Element$1)) {
42662 // Template nodes with key and value spans are always defined on a `t.Template` or
42663 // `t.Element`. If we found a node on a template with a `sourceSpan` that includes the cursor,
42664 // it is possible that we are outside the k/v spans (i.e. in-between them). If this is the
42665 // case and we do not have any other candidate matches on the `t.Element` or `t.Template`, we
42666 // want to return no results. Otherwise, the `t.Element`/`t.Template` result is incorrect for
42667 // that cursor position.
42668 return [];
42669 }
42670 return strictPath;
42671 }
42672 visit(node) {
42673 const { start, end } = getSpanIncludingEndTag(node);
42674 if (end !== null && !isWithin(this.position, { start, end })) {
42675 return;
42676 }
42677 const last = this.path[this.path.length - 1];
42678 const withinKeySpanOfLastNode = last && isTemplateNodeWithKeyAndValue(last) && isWithin(this.position, last.keySpan);
42679 const withinKeySpanOfCurrentNode = isTemplateNodeWithKeyAndValue(node) && isWithin(this.position, node.keySpan);
42680 if (withinKeySpanOfLastNode && !withinKeySpanOfCurrentNode) {
42681 // We've already identified that we are within a `keySpan` of a node.
42682 // Unless we are _also_ in the `keySpan` of the current node (happens with two way bindings),
42683 // we should stop processing nodes at this point to prevent matching any other nodes. This can
42684 // happen when the end span of a different node touches the start of the keySpan for the
42685 // candidate node. Because our `isWithin` logic is inclusive on both ends, we can match both
42686 // nodes.
42687 return;
42688 }
42689 if (isTemplateNodeWithKeyAndValue(node) && !isWithinKeyValue(this.position, node)) {
42690 // If cursor is within source span but not within key span or value span,
42691 // do not return the node.
42692 this.path.push(OUTSIDE_K_V_MARKER);
42693 }
42694 else {
42695 this.path.push(node);
42696 node.visit(this);
42697 }
42698 }
42699 visitElement(element) {
42700 this.visitElementOrTemplate(element);
42701 }
42702 visitTemplate(template) {
42703 this.visitElementOrTemplate(template);
42704 }
42705 visitElementOrTemplate(element) {
42706 this.visitAll(element.attributes);
42707 this.visitAll(element.inputs);
42708 this.visitAll(element.outputs);
42709 if (element instanceof Template) {
42710 this.visitAll(element.templateAttrs);
42711 }
42712 this.visitAll(element.references);
42713 if (element instanceof Template) {
42714 this.visitAll(element.variables);
42715 }
42716 // If we get here and have not found a candidate node on the element itself, proceed with
42717 // looking for a more specific node on the element children.
42718 if (this.path[this.path.length - 1] !== element) {
42719 return;
42720 }
42721 this.visitAll(element.children);
42722 }
42723 visitContent(content) {
42724 visitAll$1(this, content.attributes);
42725 }
42726 visitVariable(variable) {
42727 // Variable has no template nodes or expression nodes.
42728 }
42729 visitReference(reference) {
42730 // Reference has no template nodes or expression nodes.
42731 }
42732 visitTextAttribute(attribute) {
42733 // Text attribute has no template nodes or expression nodes.
42734 }
42735 visitBoundAttribute(attribute) {
42736 const visitor = new ExpressionVisitor(this.position);
42737 visitor.visit(attribute.value, this.path);
42738 }
42739 visitBoundEvent(event) {
42740 if (!isBoundEventWithSyntheticHandler(event)) {
42741 const visitor = new ExpressionVisitor(this.position);
42742 visitor.visit(event.handler, this.path);
42743 }
42744 }
42745 visitText(text) {
42746 // Text has no template nodes or expression nodes.
42747 }
42748 visitBoundText(text) {
42749 const visitor = new ExpressionVisitor(this.position);
42750 visitor.visit(text.value, this.path);
42751 }
42752 visitIcu(icu) {
42753 for (const boundText of Object.values(icu.vars)) {
42754 this.visit(boundText);
42755 }
42756 for (const boundTextOrText of Object.values(icu.placeholders)) {
42757 this.visit(boundTextOrText);
42758 }
42759 }
42760 visitAll(nodes) {
42761 for (const node of nodes) {
42762 this.visit(node);
42763 }
42764 }
42765 }
42766 class ExpressionVisitor extends RecursiveAstVisitor {
42767 // Position must be absolute in the source file.
42768 constructor(position) {
42769 super();
42770 this.position = position;
42771 }
42772 visit(node, path) {
42773 if (node instanceof ASTWithSource) {
42774 // In order to reduce noise, do not include `ASTWithSource` in the path.
42775 // For the purpose of source spans, there is no difference between
42776 // `ASTWithSource` and and underlying node that it wraps.
42777 node = node.ast;
42778 }
42779 // The third condition is to account for the implicit receiver, which should
42780 // not be visited.
42781 if (isWithin(this.position, node.sourceSpan) && !(node instanceof ImplicitReceiver)) {
42782 path.push(node);
42783 node.visit(this, path);
42784 }
42785 }
42786 }
42787 function getSpanIncludingEndTag(ast) {
42788 const result = {
42789 start: ast.sourceSpan.start.offset,
42790 end: ast.sourceSpan.end.offset,
42791 };
42792 // For Element and Template node, sourceSpan.end is the end of the opening
42793 // tag. For the purpose of language service, we need to actually recognize
42794 // the end of the closing tag. Otherwise, for situation like
42795 // <my-component></my-comp¦onent> where the cursor is in the closing tag
42796 // we will not be able to return any information.
42797 if (ast instanceof Element$1 || ast instanceof Template) {
42798 if (ast.endSourceSpan) {
42799 result.end = ast.endSourceSpan.end.offset;
42800 }
42801 else if (ast.children.length > 0) {
42802 // If the AST has children but no end source span, then it is an unclosed element with an end
42803 // that should be the end of the last child.
42804 result.end = getSpanIncludingEndTag(ast.children[ast.children.length - 1]).end;
42805 }
42806 else ;
42807 }
42808 return result;
42809 }
42810
42811 /**
42812 * @license
42813 * Copyright Google LLC All Rights Reserved.
42814 *
42815 * Use of this source code is governed by an MIT-style license that can be
42816 * found in the LICENSE file at https://angular.io/license
42817 */
42818 var CompletionNodeContext;
42819 (function (CompletionNodeContext) {
42820 CompletionNodeContext[CompletionNodeContext["None"] = 0] = "None";
42821 CompletionNodeContext[CompletionNodeContext["ElementTag"] = 1] = "ElementTag";
42822 CompletionNodeContext[CompletionNodeContext["ElementAttributeKey"] = 2] = "ElementAttributeKey";
42823 CompletionNodeContext[CompletionNodeContext["ElementAttributeValue"] = 3] = "ElementAttributeValue";
42824 CompletionNodeContext[CompletionNodeContext["EventValue"] = 4] = "EventValue";
42825 CompletionNodeContext[CompletionNodeContext["TwoWayBinding"] = 5] = "TwoWayBinding";
42826 })(CompletionNodeContext || (CompletionNodeContext = {}));
42827 /**
42828 * Performs autocompletion operations on a given node in the template.
42829 *
42830 * This class acts as a closure around all of the context required to perform the 3 autocompletion
42831 * operations (completions, get details, and get symbol) at a specific node.
42832 *
42833 * The generic `N` type for the template node is narrowed internally for certain operations, as the
42834 * compiler operations required to implement completion may be different for different node types.
42835 *
42836 * @param N type of the template node in question, narrowed accordingly.
42837 */
42838 class CompletionBuilder {
42839 constructor(tsLS, compiler, component, node, targetDetails) {
42840 this.tsLS = tsLS;
42841 this.compiler = compiler;
42842 this.component = component;
42843 this.node = node;
42844 this.targetDetails = targetDetails;
42845 this.typeChecker = this.compiler.getCurrentProgram().getTypeChecker();
42846 this.templateTypeChecker = this.compiler.getTemplateTypeChecker();
42847 this.nodeParent = this.targetDetails.parent;
42848 this.nodeContext = nodeContextFromTarget(this.targetDetails.context);
42849 this.template = this.targetDetails.template;
42850 this.position = this.targetDetails.position;
42851 }
42852 /**
42853 * Analogue for `ts.LanguageService.getCompletionsAtPosition`.
42854 */
42855 getCompletionsAtPosition(options) {
42856 if (this.isPropertyExpressionCompletion()) {
42857 return this.getPropertyExpressionCompletion(options);
42858 }
42859 else if (this.isElementTagCompletion()) {
42860 return this.getElementTagCompletion();
42861 }
42862 else if (this.isElementAttributeCompletion()) {
42863 return this.getElementAttributeCompletions(options);
42864 }
42865 else if (this.isPipeCompletion()) {
42866 return this.getPipeCompletions();
42867 }
42868 else if (this.isLiteralCompletion()) {
42869 return this.getLiteralCompletions(options);
42870 }
42871 else {
42872 return undefined;
42873 }
42874 }
42875 isLiteralCompletion() {
42876 return this.node instanceof LiteralPrimitive ||
42877 (this.node instanceof TextAttribute &&
42878 this.nodeContext === CompletionNodeContext.ElementAttributeValue);
42879 }
42880 getLiteralCompletions(options) {
42881 const location = this.compiler.getTemplateTypeChecker().getLiteralCompletionLocation(this.node, this.component);
42882 if (location === null) {
42883 return undefined;
42884 }
42885 const tsResults = this.tsLS.getCompletionsAtPosition(location.shimPath, location.positionInShimFile, options);
42886 if (tsResults === undefined) {
42887 return undefined;
42888 }
42889 let replacementSpan;
42890 if (this.node instanceof TextAttribute && this.node.value.length > 0 && this.node.valueSpan) {
42891 replacementSpan = {
42892 start: this.node.valueSpan.start.offset,
42893 length: this.node.value.length,
42894 };
42895 }
42896 if (this.node instanceof LiteralPrimitive) {
42897 if (typeof this.node.value === 'string' && this.node.value.length > 0) {
42898 replacementSpan = {
42899 // The sourceSpan of `LiteralPrimitive` includes the open quote and the completion entries
42900 // don't, so skip the open quote here.
42901 start: this.node.sourceSpan.start + 1,
42902 length: this.node.value.length,
42903 };
42904 }
42905 else if (typeof this.node.value === 'number') {
42906 replacementSpan = {
42907 start: this.node.sourceSpan.start,
42908 length: this.node.value.toString().length,
42909 };
42910 }
42911 }
42912 let ngResults = [];
42913 for (const result of tsResults.entries) {
42914 if (this.isValidNodeContextCompletion(result)) {
42915 ngResults.push({
42916 ...result,
42917 replacementSpan,
42918 });
42919 }
42920 }
42921 return {
42922 ...tsResults,
42923 entries: ngResults,
42924 };
42925 }
42926 /**
42927 * Analogue for `ts.LanguageService.getCompletionEntryDetails`.
42928 */
42929 getCompletionEntryDetails(entryName, formatOptions, preferences, data) {
42930 if (this.isPropertyExpressionCompletion()) {
42931 return this.getPropertyExpressionCompletionDetails(entryName, formatOptions, preferences, data);
42932 }
42933 else if (this.isElementTagCompletion()) {
42934 return this.getElementTagCompletionDetails(entryName);
42935 }
42936 else if (this.isElementAttributeCompletion()) {
42937 return this.getElementAttributeCompletionDetails(entryName);
42938 }
42939 }
42940 /**
42941 * Analogue for `ts.LanguageService.getCompletionEntrySymbol`.
42942 */
42943 getCompletionEntrySymbol(name) {
42944 if (this.isPropertyExpressionCompletion()) {
42945 return this.getPropertyExpressionCompletionSymbol(name);
42946 }
42947 else if (this.isElementTagCompletion()) {
42948 return this.getElementTagCompletionSymbol(name);
42949 }
42950 else if (this.isElementAttributeCompletion()) {
42951 return this.getElementAttributeCompletionSymbol(name);
42952 }
42953 else {
42954 return undefined;
42955 }
42956 }
42957 /**
42958 * Determine if the current node is the completion of a property expression, and narrow the type
42959 * of `this.node` if so.
42960 *
42961 * This narrowing gives access to additional methods related to completion of property
42962 * expressions.
42963 */
42964 isPropertyExpressionCompletion() {
42965 return this.node instanceof PropertyRead || this.node instanceof SafePropertyRead ||
42966 this.node instanceof PropertyWrite || this.node instanceof EmptyExpr ||
42967 // BoundEvent nodes only count as property completions if in an EventValue context.
42968 (this.node instanceof BoundEvent && this.nodeContext === CompletionNodeContext.EventValue);
42969 }
42970 /**
42971 * Get completions for property expressions.
42972 */
42973 getPropertyExpressionCompletion(options) {
42974 if (this.node instanceof EmptyExpr || this.node instanceof BoundEvent ||
42975 this.node.receiver instanceof ImplicitReceiver) {
42976 return this.getGlobalPropertyExpressionCompletion(options);
42977 }
42978 else {
42979 const location = this.templateTypeChecker.getExpressionCompletionLocation(this.node, this.component);
42980 if (location === null) {
42981 return undefined;
42982 }
42983 const tsResults = this.tsLS.getCompletionsAtPosition(location.shimPath, location.positionInShimFile, options);
42984 if (tsResults === undefined) {
42985 return undefined;
42986 }
42987 const replacementSpan = makeReplacementSpanFromAst(this.node);
42988 if (!(this.node.receiver instanceof ImplicitReceiver) &&
42989 !(this.node instanceof SafePropertyRead) && options?.includeCompletionsWithInsertText &&
42990 options.includeAutomaticOptionalChainCompletions !== false) {
42991 const symbol = this.templateTypeChecker.getSymbolOfNode(this.node.receiver, this.component);
42992 if (symbol?.kind === SymbolKind.Expression) {
42993 const type = symbol.tsType;
42994 const nonNullableType = this.typeChecker.getNonNullableType(type);
42995 if (type !== nonNullableType && replacementSpan !== undefined) {
42996 // Shift the start location back one so it includes the `.` of the property access.
42997 // In combination with the options above, this will indicate to the TS LS to include
42998 // optional chaining completions `?.` for nullable types.
42999 replacementSpan.start--;
43000 replacementSpan.length++;
43001 }
43002 }
43003 }
43004 let ngResults = [];
43005 for (const result of tsResults.entries) {
43006 ngResults.push({
43007 ...result,
43008 replacementSpan,
43009 });
43010 }
43011 return {
43012 ...tsResults,
43013 entries: ngResults,
43014 };
43015 }
43016 }
43017 /**
43018 * Get the details of a specific completion for a property expression.
43019 */
43020 getPropertyExpressionCompletionDetails(entryName, formatOptions, preferences, data) {
43021 let details = undefined;
43022 if (this.node instanceof EmptyExpr || this.node instanceof BoundEvent ||
43023 this.node.receiver instanceof ImplicitReceiver) {
43024 details = this.getGlobalPropertyExpressionCompletionDetails(entryName, formatOptions, preferences, data);
43025 }
43026 else {
43027 const location = this.compiler.getTemplateTypeChecker().getExpressionCompletionLocation(this.node, this.component);
43028 if (location === null) {
43029 return undefined;
43030 }
43031 details = this.tsLS.getCompletionEntryDetails(location.shimPath, location.positionInShimFile, entryName, formatOptions,
43032 /* source */ undefined, preferences, data);
43033 }
43034 if (details !== undefined) {
43035 details.displayParts = filterAliasImports(details.displayParts);
43036 }
43037 return details;
43038 }
43039 /**
43040 * Get the `ts.Symbol` for a specific completion for a property expression.
43041 */
43042 getPropertyExpressionCompletionSymbol(name) {
43043 if (this.node instanceof EmptyExpr || this.node instanceof LiteralPrimitive ||
43044 this.node instanceof BoundEvent || this.node.receiver instanceof ImplicitReceiver) {
43045 return this.getGlobalPropertyExpressionCompletionSymbol(name);
43046 }
43047 else {
43048 const location = this.compiler.getTemplateTypeChecker().getExpressionCompletionLocation(this.node, this.component);
43049 if (location === null) {
43050 return undefined;
43051 }
43052 return this.tsLS.getCompletionEntrySymbol(location.shimPath, location.positionInShimFile, name, /* source */ undefined);
43053 }
43054 }
43055 /**
43056 * Get completions for a property expression in a global context (e.g. `{{y|}}`).
43057 */
43058 getGlobalPropertyExpressionCompletion(options) {
43059 const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component, this.node);
43060 if (completions === null) {
43061 return undefined;
43062 }
43063 const { componentContext, templateContext, nodeContext: astContext } = completions;
43064 const replacementSpan = makeReplacementSpanFromAst(this.node);
43065 // Merge TS completion results with results from the template scope.
43066 let entries = [];
43067 const componentCompletions = this.tsLS.getCompletionsAtPosition(componentContext.shimPath, componentContext.positionInShimFile, options);
43068 if (componentCompletions !== undefined) {
43069 for (const tsCompletion of componentCompletions.entries) {
43070 // Skip completions that are shadowed by a template entity definition.
43071 if (templateContext.has(tsCompletion.name)) {
43072 continue;
43073 }
43074 entries.push({
43075 ...tsCompletion,
43076 // Substitute the TS completion's `replacementSpan` (which uses offsets within the TCB)
43077 // with the `replacementSpan` within the template source.
43078 replacementSpan,
43079 });
43080 }
43081 }
43082 // Merge TS completion results with results from the ast context.
43083 if (astContext !== null) {
43084 const nodeCompletions = this.tsLS.getCompletionsAtPosition(astContext.shimPath, astContext.positionInShimFile, options);
43085 if (nodeCompletions !== undefined) {
43086 for (const tsCompletion of nodeCompletions.entries) {
43087 if (this.isValidNodeContextCompletion(tsCompletion)) {
43088 entries.push({
43089 ...tsCompletion,
43090 // Substitute the TS completion's `replacementSpan` (which uses offsets within the
43091 // TCB) with the `replacementSpan` within the template source.
43092 replacementSpan,
43093 });
43094 }
43095 }
43096 }
43097 }
43098 for (const [name, entity] of templateContext) {
43099 entries.push({
43100 name,
43101 sortText: name,
43102 replacementSpan,
43103 kindModifiers: ts__default["default"].ScriptElementKindModifier.none,
43104 kind: unsafeCastDisplayInfoKindToScriptElementKind(entity.kind === CompletionKind.Reference ? DisplayInfoKind.REFERENCE :
43105 DisplayInfoKind.VARIABLE),
43106 });
43107 }
43108 return {
43109 entries,
43110 // Although this completion is "global" in the sense of an Angular expression (there is no
43111 // explicit receiver), it is not "global" in a TypeScript sense since Angular expressions have
43112 // the component as an implicit receiver.
43113 isGlobalCompletion: false,
43114 isMemberCompletion: true,
43115 isNewIdentifierLocation: false,
43116 };
43117 }
43118 /**
43119 * Get the details of a specific completion for a property expression in a global context (e.g.
43120 * `{{y|}}`).
43121 */
43122 getGlobalPropertyExpressionCompletionDetails(entryName, formatOptions, preferences, data) {
43123 const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component, this.node);
43124 if (completions === null) {
43125 return undefined;
43126 }
43127 const { componentContext, templateContext } = completions;
43128 if (templateContext.has(entryName)) {
43129 const entry = templateContext.get(entryName);
43130 // Entries that reference a symbol in the template context refer either to local references or
43131 // variables.
43132 const symbol = this.templateTypeChecker.getSymbolOfNode(entry.node, this.component);
43133 if (symbol === null) {
43134 return undefined;
43135 }
43136 const { kind, displayParts, documentation } = getSymbolDisplayInfo(this.tsLS, this.typeChecker, symbol);
43137 return {
43138 kind: unsafeCastDisplayInfoKindToScriptElementKind(kind),
43139 name: entryName,
43140 kindModifiers: ts__default["default"].ScriptElementKindModifier.none,
43141 displayParts,
43142 documentation,
43143 };
43144 }
43145 else {
43146 return this.tsLS.getCompletionEntryDetails(componentContext.shimPath, componentContext.positionInShimFile, entryName, formatOptions,
43147 /* source */ undefined, preferences, data);
43148 }
43149 }
43150 /**
43151 * Get the `ts.Symbol` of a specific completion for a property expression in a global context
43152 * (e.g.
43153 * `{{y|}}`).
43154 */
43155 getGlobalPropertyExpressionCompletionSymbol(entryName) {
43156 const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component, this.node);
43157 if (completions === null) {
43158 return undefined;
43159 }
43160 const { componentContext, templateContext } = completions;
43161 if (templateContext.has(entryName)) {
43162 const node = templateContext.get(entryName).node;
43163 const symbol = this.templateTypeChecker.getSymbolOfNode(node, this.component);
43164 if (symbol === null || symbol.tsSymbol === null) {
43165 return undefined;
43166 }
43167 return symbol.tsSymbol;
43168 }
43169 else {
43170 return this.tsLS.getCompletionEntrySymbol(componentContext.shimPath, componentContext.positionInShimFile, entryName,
43171 /* source */ undefined);
43172 }
43173 }
43174 isElementTagCompletion() {
43175 if (this.node instanceof Text$2) {
43176 const positionInTextNode = this.position - this.node.sourceSpan.start.offset;
43177 // We only provide element completions in a text node when there is an open tag immediately to
43178 // the left of the position.
43179 return this.node.value.substring(0, positionInTextNode).endsWith('<');
43180 }
43181 else if (this.node instanceof Element$1) {
43182 return this.nodeContext === CompletionNodeContext.ElementTag;
43183 }
43184 return false;
43185 }
43186 getElementTagCompletion() {
43187 const templateTypeChecker = this.compiler.getTemplateTypeChecker();
43188 let start;
43189 let length;
43190 if (this.node instanceof Element$1) {
43191 // The replacementSpan is the tag name.
43192 start = this.node.sourceSpan.start.offset + 1; // account for leading '<'
43193 length = this.node.name.length;
43194 }
43195 else {
43196 const positionInTextNode = this.position - this.node.sourceSpan.start.offset;
43197 const textToLeftOfPosition = this.node.value.substring(0, positionInTextNode);
43198 start = this.node.sourceSpan.start.offset + textToLeftOfPosition.lastIndexOf('<') + 1;
43199 // We only autocomplete immediately after the < so we don't replace any existing text
43200 length = 0;
43201 }
43202 const replacementSpan = { start, length };
43203 let potentialTags = Array.from(templateTypeChecker.getPotentialElementTags(this.component));
43204 // Don't provide non-Angular tags (directive === null) because we expect other extensions (i.e.
43205 // Emmet) to provide those for HTML files.
43206 potentialTags = potentialTags.filter(([_, directive]) => directive !== null);
43207 const entries = potentialTags.map(([tag, directive]) => ({
43208 kind: tagCompletionKind(directive),
43209 name: tag,
43210 sortText: tag,
43211 replacementSpan,
43212 }));
43213 return {
43214 entries,
43215 isGlobalCompletion: false,
43216 isMemberCompletion: false,
43217 isNewIdentifierLocation: false,
43218 };
43219 }
43220 getElementTagCompletionDetails(entryName) {
43221 const templateTypeChecker = this.compiler.getTemplateTypeChecker();
43222 const tagMap = templateTypeChecker.getPotentialElementTags(this.component);
43223 if (!tagMap.has(entryName)) {
43224 return undefined;
43225 }
43226 const directive = tagMap.get(entryName);
43227 let displayParts;
43228 let documentation = undefined;
43229 if (directive === null) {
43230 displayParts = [];
43231 }
43232 else {
43233 const displayInfo = getDirectiveDisplayInfo(this.tsLS, directive);
43234 displayParts = displayInfo.displayParts;
43235 documentation = displayInfo.documentation;
43236 }
43237 return {
43238 kind: tagCompletionKind(directive),
43239 name: entryName,
43240 kindModifiers: ts__default["default"].ScriptElementKindModifier.none,
43241 displayParts,
43242 documentation,
43243 };
43244 }
43245 getElementTagCompletionSymbol(entryName) {
43246 const templateTypeChecker = this.compiler.getTemplateTypeChecker();
43247 const tagMap = templateTypeChecker.getPotentialElementTags(this.component);
43248 if (!tagMap.has(entryName)) {
43249 return undefined;
43250 }
43251 const directive = tagMap.get(entryName);
43252 return directive?.tsSymbol;
43253 }
43254 isElementAttributeCompletion() {
43255 return (this.nodeContext === CompletionNodeContext.ElementAttributeKey ||
43256 this.nodeContext === CompletionNodeContext.TwoWayBinding) &&
43257 (this.node instanceof Element$1 || this.node instanceof BoundAttribute ||
43258 this.node instanceof TextAttribute || this.node instanceof BoundEvent);
43259 }
43260 getElementAttributeCompletions(options) {
43261 let element;
43262 if (this.node instanceof Element$1) {
43263 element = this.node;
43264 }
43265 else if (this.nodeParent instanceof Element$1 || this.nodeParent instanceof Template) {
43266 element = this.nodeParent;
43267 }
43268 else {
43269 // Nothing to do without an element to process.
43270 return undefined;
43271 }
43272 let replacementSpan = undefined;
43273 if ((this.node instanceof BoundAttribute || this.node instanceof BoundEvent ||
43274 this.node instanceof TextAttribute) &&
43275 this.node.keySpan !== undefined) {
43276 replacementSpan = makeReplacementSpanFromParseSourceSpan(this.node.keySpan);
43277 }
43278 let insertSnippet;
43279 if (options?.includeCompletionsWithSnippetText && options.includeCompletionsWithInsertText) {
43280 if (this.node instanceof BoundEvent && isBoundEventWithSyntheticHandler(this.node)) {
43281 replacementSpan = makeReplacementSpanFromParseSourceSpan(this.node.sourceSpan);
43282 insertSnippet = true;
43283 }
43284 const isBoundAttributeValueEmpty = this.node instanceof BoundAttribute &&
43285 (this.node.valueSpan === undefined ||
43286 (this.node.value instanceof ASTWithSource && this.node.value.ast instanceof EmptyExpr));
43287 if (isBoundAttributeValueEmpty) {
43288 replacementSpan = makeReplacementSpanFromParseSourceSpan(this.node.sourceSpan);
43289 insertSnippet = true;
43290 }
43291 if (this.node instanceof TextAttribute && this.node.keySpan !== undefined) {
43292 // The `sourceSpan` only includes `ngFor` and the `valueSpan` is always empty even if there
43293 // is something there because we split this up into the desugared AST, `ngFor ngForOf=""`.
43294 const nodeStart = this.node.keySpan.start.getContext(1, 1);
43295 if (nodeStart?.before[0] === '*') {
43296 const nodeEnd = this.node.keySpan.end.getContext(1, 1);
43297 if (nodeEnd?.after[0] !== '=') {
43298 // *ngFor -> *ngFor="¦"
43299 insertSnippet = true;
43300 }
43301 }
43302 else {
43303 if (this.node.value === '') {
43304 replacementSpan = makeReplacementSpanFromParseSourceSpan(this.node.sourceSpan);
43305 insertSnippet = true;
43306 }
43307 }
43308 }
43309 if (this.node instanceof Element$1) {
43310 // <div ¦ />
43311 insertSnippet = true;
43312 }
43313 }
43314 const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
43315 let entries = [];
43316 for (const completion of attrTable.values()) {
43317 // First, filter out completions that don't make sense for the current node. For example, if
43318 // the user is completing on a property binding `[foo|]`, don't offer output event
43319 // completions.
43320 switch (completion.kind) {
43321 case AttributeCompletionKind.DomEvent:
43322 if (this.node instanceof BoundAttribute) {
43323 continue;
43324 }
43325 break;
43326 case AttributeCompletionKind.DomAttribute:
43327 case AttributeCompletionKind.DomProperty:
43328 if (this.node instanceof BoundEvent) {
43329 continue;
43330 }
43331 break;
43332 case AttributeCompletionKind.DirectiveInput:
43333 if (this.node instanceof BoundEvent) {
43334 continue;
43335 }
43336 if (!completion.twoWayBindingSupported &&
43337 this.nodeContext === CompletionNodeContext.TwoWayBinding) {
43338 continue;
43339 }
43340 break;
43341 case AttributeCompletionKind.DirectiveOutput:
43342 if (this.node instanceof BoundAttribute) {
43343 continue;
43344 }
43345 break;
43346 case AttributeCompletionKind.DirectiveAttribute:
43347 if (this.node instanceof BoundAttribute ||
43348 this.node instanceof BoundEvent) {
43349 continue;
43350 }
43351 break;
43352 }
43353 // Is the completion in an attribute context (instead of a property context)?
43354 const isAttributeContext = (this.node instanceof Element$1 || this.node instanceof TextAttribute);
43355 // Is the completion for an element (not an <ng-template>)?
43356 const isElementContext = this.node instanceof Element$1 || this.nodeParent instanceof Element$1;
43357 addAttributeCompletionEntries(entries, completion, isAttributeContext, isElementContext, replacementSpan, insertSnippet);
43358 }
43359 return {
43360 entries,
43361 isGlobalCompletion: false,
43362 isMemberCompletion: false,
43363 isNewIdentifierLocation: true,
43364 };
43365 }
43366 getElementAttributeCompletionDetails(entryName) {
43367 // `entryName` here may be `foo` or `[foo]`, depending on which suggested completion the user
43368 // chose. Strip off any binding syntax to get the real attribute name.
43369 const { name, kind } = stripBindingSugar(entryName);
43370 let element;
43371 if (this.node instanceof Element$1 || this.node instanceof Template) {
43372 element = this.node;
43373 }
43374 else if (this.nodeParent instanceof Element$1 || this.nodeParent instanceof Template) {
43375 element = this.nodeParent;
43376 }
43377 else {
43378 // Nothing to do without an element to process.
43379 return undefined;
43380 }
43381 const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
43382 if (!attrTable.has(name)) {
43383 return undefined;
43384 }
43385 const completion = attrTable.get(name);
43386 let displayParts;
43387 let documentation = undefined;
43388 let info;
43389 switch (completion.kind) {
43390 case AttributeCompletionKind.DomEvent:
43391 case AttributeCompletionKind.DomAttribute:
43392 case AttributeCompletionKind.DomProperty:
43393 // TODO(alxhub): ideally we would show the same documentation as quick info here. However,
43394 // since these bindings don't exist in the TCB, there is no straightforward way to retrieve
43395 // a `ts.Symbol` for the field in the TS DOM definition.
43396 displayParts = [];
43397 break;
43398 case AttributeCompletionKind.DirectiveAttribute:
43399 info = getDirectiveDisplayInfo(this.tsLS, completion.directive);
43400 displayParts = info.displayParts;
43401 documentation = info.documentation;
43402 break;
43403 case AttributeCompletionKind.StructuralDirectiveAttribute:
43404 case AttributeCompletionKind.DirectiveInput:
43405 case AttributeCompletionKind.DirectiveOutput:
43406 const propertySymbol = getAttributeCompletionSymbol(completion, this.typeChecker);
43407 if (propertySymbol === null) {
43408 return undefined;
43409 }
43410 let kind;
43411 if (completion.kind === AttributeCompletionKind.DirectiveInput) {
43412 kind = DisplayInfoKind.PROPERTY;
43413 }
43414 else if (completion.kind === AttributeCompletionKind.DirectiveOutput) {
43415 kind = DisplayInfoKind.EVENT;
43416 }
43417 else {
43418 kind = DisplayInfoKind.DIRECTIVE;
43419 }
43420 info = getTsSymbolDisplayInfo(this.tsLS, this.typeChecker, propertySymbol, kind, completion.directive.tsSymbol.name);
43421 if (info === null) {
43422 return undefined;
43423 }
43424 displayParts = info.displayParts;
43425 documentation = info.documentation;
43426 }
43427 return {
43428 name: entryName,
43429 kind: unsafeCastDisplayInfoKindToScriptElementKind(kind),
43430 kindModifiers: ts__default["default"].ScriptElementKindModifier.none,
43431 displayParts,
43432 documentation,
43433 };
43434 }
43435 getElementAttributeCompletionSymbol(attribute) {
43436 const { name } = stripBindingSugar(attribute);
43437 let element;
43438 if (this.node instanceof Element$1 || this.node instanceof Template) {
43439 element = this.node;
43440 }
43441 else if (this.nodeParent instanceof Element$1 || this.nodeParent instanceof Template) {
43442 element = this.nodeParent;
43443 }
43444 else {
43445 // Nothing to do without an element to process.
43446 return undefined;
43447 }
43448 const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
43449 if (!attrTable.has(name)) {
43450 return undefined;
43451 }
43452 const completion = attrTable.get(name);
43453 return getAttributeCompletionSymbol(completion, this.typeChecker) ?? undefined;
43454 }
43455 isPipeCompletion() {
43456 return this.node instanceof BindingPipe;
43457 }
43458 getPipeCompletions() {
43459 const pipes = this.templateTypeChecker.getPipesInScope(this.component);
43460 if (pipes === null) {
43461 return undefined;
43462 }
43463 const replacementSpan = makeReplacementSpanFromAst(this.node);
43464 const entries = pipes.map(pipe => ({
43465 name: pipe.name,
43466 sortText: pipe.name,
43467 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PIPE),
43468 replacementSpan,
43469 }));
43470 return {
43471 entries,
43472 isGlobalCompletion: false,
43473 isMemberCompletion: false,
43474 isNewIdentifierLocation: false,
43475 };
43476 }
43477 /**
43478 * From the AST node of the cursor position, include completion of string literals, number
43479 * literals, `true`, `false`, `null`, and `undefined`.
43480 */
43481 isValidNodeContextCompletion(completion) {
43482 if (completion.kind === ts__default["default"].ScriptElementKind.string) {
43483 // 'string' kind includes both string literals and number literals
43484 return true;
43485 }
43486 if (completion.kind === ts__default["default"].ScriptElementKind.keyword) {
43487 return completion.name === 'true' || completion.name === 'false' ||
43488 completion.name === 'null';
43489 }
43490 if (completion.kind === ts__default["default"].ScriptElementKind.variableElement) {
43491 return completion.name === 'undefined';
43492 }
43493 return false;
43494 }
43495 }
43496 function makeReplacementSpanFromParseSourceSpan(span) {
43497 return {
43498 start: span.start.offset,
43499 length: span.end.offset - span.start.offset,
43500 };
43501 }
43502 function makeReplacementSpanFromAst(node) {
43503 if ((node instanceof EmptyExpr || node instanceof LiteralPrimitive ||
43504 node instanceof BoundEvent)) {
43505 // empty nodes do not replace any existing text
43506 return undefined;
43507 }
43508 return {
43509 start: node.nameSpan.start,
43510 length: node.nameSpan.end - node.nameSpan.start,
43511 };
43512 }
43513 function tagCompletionKind(directive) {
43514 let kind;
43515 if (directive === null) {
43516 kind = DisplayInfoKind.ELEMENT;
43517 }
43518 else if (directive.isComponent) {
43519 kind = DisplayInfoKind.COMPONENT;
43520 }
43521 else {
43522 kind = DisplayInfoKind.DIRECTIVE;
43523 }
43524 return unsafeCastDisplayInfoKindToScriptElementKind(kind);
43525 }
43526 const BINDING_SUGAR = /[\[\(\)\]]/g;
43527 function stripBindingSugar(binding) {
43528 const name = binding.replace(BINDING_SUGAR, '');
43529 if (binding.startsWith('[')) {
43530 return { name, kind: DisplayInfoKind.PROPERTY };
43531 }
43532 else if (binding.startsWith('(')) {
43533 return { name, kind: DisplayInfoKind.EVENT };
43534 }
43535 else {
43536 return { name, kind: DisplayInfoKind.ATTRIBUTE };
43537 }
43538 }
43539 function nodeContextFromTarget(target) {
43540 switch (target.kind) {
43541 case TargetNodeKind.ElementInTagContext:
43542 return CompletionNodeContext.ElementTag;
43543 case TargetNodeKind.ElementInBodyContext:
43544 // Completions in element bodies are for new attributes.
43545 return CompletionNodeContext.ElementAttributeKey;
43546 case TargetNodeKind.TwoWayBindingContext:
43547 return CompletionNodeContext.TwoWayBinding;
43548 case TargetNodeKind.AttributeInKeyContext:
43549 return CompletionNodeContext.ElementAttributeKey;
43550 case TargetNodeKind.AttributeInValueContext:
43551 if (target.node instanceof BoundEvent) {
43552 return CompletionNodeContext.EventValue;
43553 }
43554 else if (target.node instanceof TextAttribute) {
43555 return CompletionNodeContext.ElementAttributeValue;
43556 }
43557 else {
43558 return CompletionNodeContext.None;
43559 }
43560 default:
43561 // No special context is available.
43562 return CompletionNodeContext.None;
43563 }
43564 }
43565
43566 /**
43567 * @license
43568 * Copyright Google LLC All Rights Reserved.
43569 *
43570 * Use of this source code is governed by an MIT-style license that can be
43571 * found in the LICENSE file at https://angular.io/license
43572 */
43573 /**
43574 * Converts a `ShimLocation` to a more genericly named `FilePosition`.
43575 */
43576 function toFilePosition(shimLocation) {
43577 return { fileName: shimLocation.shimPath, position: shimLocation.positionInShimFile };
43578 }
43579 /**
43580 * Takes a position in a template and finds equivalent targets in TS files as well as details about
43581 * the targeted template node.
43582 */
43583 function getTargetDetailsAtTemplatePosition({ template, component }, position, templateTypeChecker) {
43584 // Find the AST node in the template at the position.
43585 const positionDetails = getTargetAtPosition(template, position);
43586 if (positionDetails === null) {
43587 return null;
43588 }
43589 const nodes = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
43590 positionDetails.context.nodes :
43591 [positionDetails.context.node];
43592 const details = [];
43593 for (const node of nodes) {
43594 // Get the information about the TCB at the template position.
43595 const symbol = templateTypeChecker.getSymbolOfNode(node, component);
43596 if (symbol === null) {
43597 continue;
43598 }
43599 const templateTarget = node;
43600 switch (symbol.kind) {
43601 case SymbolKind.Directive:
43602 case SymbolKind.Template:
43603 // References to elements, templates, and directives will be through template references
43604 // (#ref). They shouldn't be used directly for a Language Service reference request.
43605 break;
43606 case SymbolKind.Element: {
43607 const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives);
43608 details.push({
43609 typescriptLocations: getPositionsForDirectives(matches),
43610 templateTarget,
43611 symbol,
43612 });
43613 break;
43614 }
43615 case SymbolKind.DomBinding: {
43616 // Dom bindings aren't currently type-checked (see `checkTypeOfDomBindings`) so they don't
43617 // have a shim location. This means we can't match dom bindings to their lib.dom
43618 // reference, but we can still see if they match to a directive.
43619 if (!(node instanceof TextAttribute) && !(node instanceof BoundAttribute)) {
43620 return null;
43621 }
43622 const directives = getDirectiveMatchesForAttribute(node.name, symbol.host.templateNode, symbol.host.directives);
43623 details.push({
43624 typescriptLocations: getPositionsForDirectives(directives),
43625 templateTarget,
43626 symbol,
43627 });
43628 break;
43629 }
43630 case SymbolKind.Reference: {
43631 details.push({
43632 typescriptLocations: [toFilePosition(symbol.referenceVarLocation)],
43633 templateTarget,
43634 symbol,
43635 });
43636 break;
43637 }
43638 case SymbolKind.Variable: {
43639 if ((templateTarget instanceof Variable)) {
43640 if (templateTarget.valueSpan !== undefined &&
43641 isWithin(position, templateTarget.valueSpan)) {
43642 // In the valueSpan of the variable, we want to get the reference of the initializer.
43643 details.push({
43644 typescriptLocations: [toFilePosition(symbol.initializerLocation)],
43645 templateTarget,
43646 symbol,
43647 });
43648 }
43649 else if (isWithin(position, templateTarget.keySpan)) {
43650 // In the keySpan of the variable, we want to get the reference of the local variable.
43651 details.push({
43652 typescriptLocations: [toFilePosition(symbol.localVarLocation)],
43653 templateTarget,
43654 symbol,
43655 });
43656 }
43657 }
43658 else {
43659 // If the templateNode is not the `TmplAstVariable`, it must be a usage of the
43660 // variable somewhere in the template.
43661 details.push({
43662 typescriptLocations: [toFilePosition(symbol.localVarLocation)],
43663 templateTarget,
43664 symbol,
43665 });
43666 }
43667 break;
43668 }
43669 case SymbolKind.Input:
43670 case SymbolKind.Output: {
43671 details.push({
43672 typescriptLocations: symbol.bindings.map(binding => toFilePosition(binding.shimLocation)),
43673 templateTarget,
43674 symbol,
43675 });
43676 break;
43677 }
43678 case SymbolKind.Pipe:
43679 case SymbolKind.Expression: {
43680 details.push({
43681 typescriptLocations: [toFilePosition(symbol.shimLocation)],
43682 templateTarget,
43683 symbol,
43684 });
43685 break;
43686 }
43687 }
43688 }
43689 return details.length > 0 ? details : null;
43690 }
43691 /**
43692 * Given a set of `DirectiveSymbol`s, finds the equivalent `FilePosition` of the class declaration.
43693 */
43694 function getPositionsForDirectives(directives) {
43695 const allDirectives = [];
43696 for (const dir of directives.values()) {
43697 const dirClass = dir.tsSymbol.valueDeclaration;
43698 if (dirClass === undefined || !ts__default["default"].isClassDeclaration(dirClass) || dirClass.name === undefined) {
43699 continue;
43700 }
43701 const { fileName } = dirClass.getSourceFile();
43702 const position = dirClass.name.getStart();
43703 allDirectives.push({ fileName, position });
43704 }
43705 return allDirectives;
43706 }
43707 /**
43708 * Creates a "key" for a rename/reference location by concatenating file name, span start, and span
43709 * length. This allows us to de-duplicate template results when an item may appear several times
43710 * in the TCB but map back to the same template location.
43711 */
43712 function createLocationKey(ds) {
43713 return ds.fileName + ds.textSpan.start + ds.textSpan.length;
43714 }
43715 /**
43716 * Converts a given `ts.DocumentSpan` in a shim file to its equivalent `ts.DocumentSpan` in the
43717 * template.
43718 *
43719 * You can optionally provide a `requiredNodeText` that ensures the equivalent template node's text
43720 * matches. If it does not, this function will return `null`.
43721 */
43722 function convertToTemplateDocumentSpan(shimDocumentSpan, templateTypeChecker, program, requiredNodeText) {
43723 const sf = program.getSourceFile(shimDocumentSpan.fileName);
43724 if (sf === undefined) {
43725 return null;
43726 }
43727 const tcbNode = findTightestNode(sf, shimDocumentSpan.textSpan.start);
43728 if (tcbNode === undefined ||
43729 hasExpressionIdentifier(sf, tcbNode, ExpressionIdentifier.EVENT_PARAMETER)) {
43730 // If the reference result is the $event parameter in the subscribe/addEventListener
43731 // function in the TCB, we want to filter this result out of the references. We really only
43732 // want to return references to the parameter in the template itself.
43733 return null;
43734 }
43735 // TODO(atscott): Determine how to consistently resolve paths. i.e. with the project
43736 // serverHost or LSParseConfigHost in the adapter. We should have a better defined way to
43737 // normalize paths.
43738 const mapping = getTemplateLocationFromShimLocation(templateTypeChecker, absoluteFrom(shimDocumentSpan.fileName), shimDocumentSpan.textSpan.start);
43739 if (mapping === null) {
43740 return null;
43741 }
43742 const { span, templateUrl } = mapping;
43743 if (requiredNodeText !== undefined && span.toString() !== requiredNodeText) {
43744 return null;
43745 }
43746 return {
43747 ...shimDocumentSpan,
43748 fileName: templateUrl,
43749 textSpan: toTextSpan(span),
43750 // Specifically clear other text span values because we do not have enough knowledge to
43751 // convert these to spans in the template.
43752 contextSpan: undefined,
43753 originalContextSpan: undefined,
43754 originalTextSpan: undefined,
43755 };
43756 }
43757 /**
43758 * Finds the text and `ts.TextSpan` for the node at a position in a template.
43759 */
43760 function getRenameTextAndSpanAtPosition(node, position) {
43761 if (node instanceof BoundAttribute || node instanceof TextAttribute ||
43762 node instanceof BoundEvent) {
43763 if (node.keySpan === undefined) {
43764 return null;
43765 }
43766 return { text: node.name, span: toTextSpan(node.keySpan) };
43767 }
43768 else if (node instanceof Variable || node instanceof Reference$1) {
43769 if (isWithin(position, node.keySpan)) {
43770 return { text: node.keySpan.toString(), span: toTextSpan(node.keySpan) };
43771 }
43772 else if (node.valueSpan && isWithin(position, node.valueSpan)) {
43773 return { text: node.valueSpan.toString(), span: toTextSpan(node.valueSpan) };
43774 }
43775 }
43776 if (node instanceof PropertyRead || node instanceof PropertyWrite ||
43777 node instanceof SafePropertyRead || node instanceof BindingPipe) {
43778 return { text: node.name, span: toTextSpan(node.nameSpan) };
43779 }
43780 else if (node instanceof LiteralPrimitive) {
43781 const span = toTextSpan(node.sourceSpan);
43782 const text = node.value;
43783 if (typeof text === 'string') {
43784 // The span of a string literal includes the quotes but they should be removed for renaming.
43785 span.start += 1;
43786 span.length -= 2;
43787 }
43788 return { text, span };
43789 }
43790 return null;
43791 }
43792 /**
43793 * Retrives the `PipeMeta` or `DirectiveMeta` of the given `ts.Node`'s parent class.
43794 *
43795 * Returns `null` if the node has no parent class or there is no meta associated with the class.
43796 */
43797 function getParentClassMeta(requestNode, compiler) {
43798 const parentClass = getParentClassDeclaration(requestNode);
43799 if (parentClass === undefined) {
43800 return null;
43801 }
43802 return compiler.getMeta(parentClass);
43803 }
43804
43805 /**
43806 * @license
43807 * Copyright Google LLC All Rights Reserved.
43808 *
43809 * Use of this source code is governed by an MIT-style license that can be
43810 * found in the LICENSE file at https://angular.io/license
43811 */
43812 class DefinitionBuilder {
43813 constructor(tsLS, compiler, driver) {
43814 this.tsLS = tsLS;
43815 this.compiler = compiler;
43816 this.driver = driver;
43817 this.ttc = this.compiler.getTemplateTypeChecker();
43818 }
43819 getDefinitionAndBoundSpan(fileName, position) {
43820 const templateInfo = getTemplateInfoAtPosition(fileName, position, this.compiler);
43821 if (templateInfo === undefined) {
43822 // We were unable to get a template at the given position. If we are in a TS file, instead
43823 // attempt to get an Angular definition at the location inside a TS file (examples of this
43824 // would be templateUrl or a url in styleUrls).
43825 if (!isTypeScriptFile(fileName)) {
43826 return;
43827 }
43828 return getDefinitionForExpressionAtPosition(fileName, position, this.compiler);
43829 }
43830 const definitionMetas = this.getDefinitionMetaAtPosition(templateInfo, position);
43831 if (definitionMetas === undefined) {
43832 return undefined;
43833 }
43834 const definitions = [];
43835 for (const definitionMeta of definitionMetas) {
43836 // The `$event` of event handlers would point to the $event parameter in the shim file, as in
43837 // `_t3["x"].subscribe(function ($event): any { $event }) ;`
43838 // If we wanted to return something for this, it would be more appropriate for something like
43839 // `getTypeDefinition`.
43840 if (isDollarEvent(definitionMeta.node)) {
43841 continue;
43842 }
43843 definitions.push(...(this.getDefinitionsForSymbol({ ...definitionMeta, ...templateInfo }) ?? []));
43844 }
43845 if (definitions.length === 0) {
43846 return undefined;
43847 }
43848 return { definitions, textSpan: getTextSpanOfNode(definitionMetas[0].node) };
43849 }
43850 getDefinitionsForSymbol({ symbol, node, parent, component }) {
43851 switch (symbol.kind) {
43852 case SymbolKind.Directive:
43853 case SymbolKind.Element:
43854 case SymbolKind.Template:
43855 case SymbolKind.DomBinding:
43856 // Though it is generally more appropriate for the above symbol definitions to be
43857 // associated with "type definitions" since the location in the template is the
43858 // actual definition location, the better user experience would be to allow
43859 // LS users to "go to definition" on an item in the template that maps to a class and be
43860 // taken to the directive or HTML class.
43861 return this.getTypeDefinitionsForTemplateInstance(symbol, node);
43862 case SymbolKind.Pipe: {
43863 if (symbol.tsSymbol !== null) {
43864 return this.getDefinitionsForSymbols(symbol);
43865 }
43866 else {
43867 // If there is no `ts.Symbol` for the pipe transform, we want to return the
43868 // type definition (the pipe class).
43869 return this.getTypeDefinitionsForSymbols(symbol.classSymbol);
43870 }
43871 }
43872 case SymbolKind.Output:
43873 case SymbolKind.Input: {
43874 const bindingDefs = this.getDefinitionsForSymbols(...symbol.bindings);
43875 // Also attempt to get directive matches for the input name. If there is a directive that
43876 // has the input name as part of the selector, we want to return that as well.
43877 const directiveDefs = this.getDirectiveTypeDefsForBindingNode(node, parent, component);
43878 return [...bindingDefs, ...directiveDefs];
43879 }
43880 case SymbolKind.Variable:
43881 case SymbolKind.Reference: {
43882 const definitions = [];
43883 if (symbol.declaration !== node) {
43884 const shimLocation = symbol.kind === SymbolKind.Variable ? symbol.localVarLocation :
43885 symbol.referenceVarLocation;
43886 const mapping = getTemplateLocationFromShimLocation(this.compiler.getTemplateTypeChecker(), shimLocation.shimPath, shimLocation.positionInShimFile);
43887 if (mapping !== null) {
43888 definitions.push({
43889 name: symbol.declaration.name,
43890 containerName: '',
43891 containerKind: ts__default["default"].ScriptElementKind.unknown,
43892 kind: ts__default["default"].ScriptElementKind.variableElement,
43893 textSpan: getTextSpanOfNode(symbol.declaration),
43894 contextSpan: toTextSpan(symbol.declaration.sourceSpan),
43895 fileName: mapping.templateUrl,
43896 });
43897 }
43898 }
43899 if (symbol.kind === SymbolKind.Variable) {
43900 definitions.push(...this.getDefinitionsForSymbols({ shimLocation: symbol.initializerLocation }));
43901 }
43902 return definitions;
43903 }
43904 case SymbolKind.Expression: {
43905 return this.getDefinitionsForSymbols(symbol);
43906 }
43907 }
43908 }
43909 getDefinitionsForSymbols(...symbols) {
43910 return flatMap(symbols, ({ shimLocation }) => {
43911 const { shimPath, positionInShimFile } = shimLocation;
43912 const definitionInfos = this.tsLS.getDefinitionAtPosition(shimPath, positionInShimFile);
43913 if (definitionInfos === undefined) {
43914 return [];
43915 }
43916 return this.mapShimResultsToTemplates(definitionInfos);
43917 });
43918 }
43919 /**
43920 * Converts and definition info result that points to a template typecheck file to a reference to
43921 * the corresponding location in the template.
43922 */
43923 mapShimResultsToTemplates(definitionInfos) {
43924 const result = [];
43925 for (const info of definitionInfos) {
43926 if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(info.fileName))) {
43927 const templateDefinitionInfo = convertToTemplateDocumentSpan(info, this.ttc, this.driver.getProgram());
43928 if (templateDefinitionInfo === null) {
43929 continue;
43930 }
43931 result.push(templateDefinitionInfo);
43932 }
43933 else {
43934 result.push(info);
43935 }
43936 }
43937 return result;
43938 }
43939 getTypeDefinitionsAtPosition(fileName, position) {
43940 const templateInfo = getTemplateInfoAtPosition(fileName, position, this.compiler);
43941 if (templateInfo === undefined) {
43942 return;
43943 }
43944 const definitionMetas = this.getDefinitionMetaAtPosition(templateInfo, position);
43945 if (definitionMetas === undefined) {
43946 return undefined;
43947 }
43948 const definitions = [];
43949 for (const { symbol, node, parent } of definitionMetas) {
43950 switch (symbol.kind) {
43951 case SymbolKind.Directive:
43952 case SymbolKind.DomBinding:
43953 case SymbolKind.Element:
43954 case SymbolKind.Template:
43955 definitions.push(...this.getTypeDefinitionsForTemplateInstance(symbol, node));
43956 break;
43957 case SymbolKind.Output:
43958 case SymbolKind.Input: {
43959 const bindingDefs = this.getTypeDefinitionsForSymbols(...symbol.bindings);
43960 definitions.push(...bindingDefs);
43961 // Also attempt to get directive matches for the input name. If there is a directive that
43962 // has the input name as part of the selector, we want to return that as well.
43963 const directiveDefs = this.getDirectiveTypeDefsForBindingNode(node, parent, templateInfo.component);
43964 definitions.push(...directiveDefs);
43965 break;
43966 }
43967 case SymbolKind.Pipe: {
43968 if (symbol.tsSymbol !== null) {
43969 definitions.push(...this.getTypeDefinitionsForSymbols(symbol));
43970 }
43971 else {
43972 // If there is no `ts.Symbol` for the pipe transform, we want to return the
43973 // type definition (the pipe class).
43974 definitions.push(...this.getTypeDefinitionsForSymbols(symbol.classSymbol));
43975 }
43976 break;
43977 }
43978 case SymbolKind.Reference:
43979 definitions.push(...this.getTypeDefinitionsForSymbols({ shimLocation: symbol.targetLocation }));
43980 break;
43981 case SymbolKind.Expression:
43982 definitions.push(...this.getTypeDefinitionsForSymbols(symbol));
43983 break;
43984 case SymbolKind.Variable: {
43985 definitions.push(...this.getTypeDefinitionsForSymbols({ shimLocation: symbol.initializerLocation }));
43986 break;
43987 }
43988 }
43989 return definitions;
43990 }
43991 }
43992 getTypeDefinitionsForTemplateInstance(symbol, node) {
43993 switch (symbol.kind) {
43994 case SymbolKind.Template: {
43995 const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives);
43996 return this.getTypeDefinitionsForSymbols(...matches);
43997 }
43998 case SymbolKind.Element: {
43999 const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives);
44000 // If one of the directive matches is a component, we should not include the native element
44001 // in the results because it is replaced by the component.
44002 return Array.from(matches).some(dir => dir.isComponent) ?
44003 this.getTypeDefinitionsForSymbols(...matches) :
44004 this.getTypeDefinitionsForSymbols(...matches, symbol);
44005 }
44006 case SymbolKind.DomBinding: {
44007 if (!(node instanceof TextAttribute)) {
44008 return [];
44009 }
44010 const dirs = getDirectiveMatchesForAttribute(node.name, symbol.host.templateNode, symbol.host.directives);
44011 return this.getTypeDefinitionsForSymbols(...dirs);
44012 }
44013 case SymbolKind.Directive:
44014 return this.getTypeDefinitionsForSymbols(symbol);
44015 }
44016 }
44017 getDirectiveTypeDefsForBindingNode(node, parent, component) {
44018 if (!(node instanceof BoundAttribute) && !(node instanceof TextAttribute) &&
44019 !(node instanceof BoundEvent)) {
44020 return [];
44021 }
44022 if (parent === null ||
44023 !(parent instanceof Template || parent instanceof Element$1)) {
44024 return [];
44025 }
44026 const templateOrElementSymbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(parent, component);
44027 if (templateOrElementSymbol === null ||
44028 (templateOrElementSymbol.kind !== SymbolKind.Template &&
44029 templateOrElementSymbol.kind !== SymbolKind.Element)) {
44030 return [];
44031 }
44032 const dirs = getDirectiveMatchesForAttribute(node.name, parent, templateOrElementSymbol.directives);
44033 return this.getTypeDefinitionsForSymbols(...dirs);
44034 }
44035 getTypeDefinitionsForSymbols(...symbols) {
44036 return flatMap(symbols, ({ shimLocation }) => {
44037 const { shimPath, positionInShimFile } = shimLocation;
44038 return this.tsLS.getTypeDefinitionAtPosition(shimPath, positionInShimFile) ?? [];
44039 });
44040 }
44041 getDefinitionMetaAtPosition({ template, component }, position) {
44042 const target = getTargetAtPosition(template, position);
44043 if (target === null) {
44044 return undefined;
44045 }
44046 const { context, parent } = target;
44047 const nodes = context.kind === TargetNodeKind.TwoWayBindingContext ? context.nodes : [context.node];
44048 const definitionMetas = [];
44049 for (const node of nodes) {
44050 const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(node, component);
44051 if (symbol === null) {
44052 continue;
44053 }
44054 definitionMetas.push({ node, parent, symbol });
44055 }
44056 return definitionMetas.length > 0 ? definitionMetas : undefined;
44057 }
44058 }
44059 /**
44060 * Gets an Angular-specific definition in a TypeScript source file.
44061 */
44062 function getDefinitionForExpressionAtPosition(fileName, position, compiler) {
44063 const sf = compiler.getCurrentProgram().getSourceFile(fileName);
44064 if (sf === undefined) {
44065 return;
44066 }
44067 const expression = findTightestNode(sf, position);
44068 if (expression === undefined) {
44069 return;
44070 }
44071 const classDeclaration = getParentClassDeclaration(expression);
44072 if (classDeclaration === undefined) {
44073 return;
44074 }
44075 const componentResources = compiler.getComponentResources(classDeclaration);
44076 if (componentResources === null) {
44077 return;
44078 }
44079 const allResources = [...componentResources.styles, componentResources.template];
44080 const resourceForExpression = allResources.find(resource => resource.expression === expression);
44081 if (resourceForExpression === undefined || !isExternalResource(resourceForExpression)) {
44082 return;
44083 }
44084 const templateDefinitions = [{
44085 kind: ts__default["default"].ScriptElementKind.externalModuleName,
44086 name: resourceForExpression.path,
44087 containerKind: ts__default["default"].ScriptElementKind.unknown,
44088 containerName: '',
44089 // Reading the template is expensive, so don't provide a preview.
44090 // TODO(ayazhafiz): Consider providing an actual span:
44091 // 1. We're likely to read the template anyway
44092 // 2. We could show just the first 100 chars or so
44093 textSpan: { start: 0, length: 0 },
44094 fileName: resourceForExpression.path,
44095 }];
44096 return {
44097 definitions: templateDefinitions,
44098 textSpan: {
44099 // Exclude opening and closing quotes in the url span.
44100 start: expression.getStart() + 1,
44101 length: expression.getWidth() - 2,
44102 },
44103 };
44104 }
44105
44106 /**
44107 * @license
44108 * Copyright Google LLC All Rights Reserved.
44109 *
44110 * Use of this source code is governed by an MIT-style license that can be
44111 * found in the LICENSE file at https://angular.io/license
44112 */
44113 class QuickInfoBuilder {
44114 constructor(tsLS, compiler, component, node, parent) {
44115 this.tsLS = tsLS;
44116 this.compiler = compiler;
44117 this.component = component;
44118 this.node = node;
44119 this.parent = parent;
44120 this.typeChecker = this.compiler.getCurrentProgram().getTypeChecker();
44121 }
44122 get() {
44123 const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(this.node, this.component);
44124 if (symbol !== null) {
44125 return this.getQuickInfoForSymbol(symbol);
44126 }
44127 if (isDollarAny(this.node)) {
44128 return createDollarAnyQuickInfo(this.node);
44129 }
44130 // If the cursor lands on the receiver of a method call, we have to look
44131 // at the entire call in order to figure out if it's a call to `$any`.
44132 if (this.parent !== null && isDollarAny(this.parent) && this.parent.receiver === this.node) {
44133 return createDollarAnyQuickInfo(this.parent);
44134 }
44135 }
44136 getQuickInfoForSymbol(symbol) {
44137 switch (symbol.kind) {
44138 case SymbolKind.Input:
44139 case SymbolKind.Output:
44140 return this.getQuickInfoForBindingSymbol(symbol);
44141 case SymbolKind.Template:
44142 return createNgTemplateQuickInfo(this.node);
44143 case SymbolKind.Element:
44144 return this.getQuickInfoForElementSymbol(symbol);
44145 case SymbolKind.Variable:
44146 return this.getQuickInfoForVariableSymbol(symbol);
44147 case SymbolKind.Reference:
44148 return this.getQuickInfoForReferenceSymbol(symbol);
44149 case SymbolKind.DomBinding:
44150 return this.getQuickInfoForDomBinding(symbol);
44151 case SymbolKind.Directive:
44152 return this.getQuickInfoAtShimLocation(symbol.shimLocation);
44153 case SymbolKind.Pipe:
44154 return this.getQuickInfoForPipeSymbol(symbol);
44155 case SymbolKind.Expression:
44156 return this.getQuickInfoAtShimLocation(symbol.shimLocation);
44157 }
44158 }
44159 getQuickInfoForBindingSymbol(symbol) {
44160 if (symbol.bindings.length === 0) {
44161 return undefined;
44162 }
44163 const kind = symbol.kind === SymbolKind.Input ? DisplayInfoKind.PROPERTY : DisplayInfoKind.EVENT;
44164 const quickInfo = this.getQuickInfoAtShimLocation(symbol.bindings[0].shimLocation);
44165 return quickInfo === undefined ? undefined : updateQuickInfoKind(quickInfo, kind);
44166 }
44167 getQuickInfoForElementSymbol(symbol) {
44168 const { templateNode } = symbol;
44169 const matches = getDirectiveMatchesForElementTag(templateNode, symbol.directives);
44170 if (matches.size > 0) {
44171 return this.getQuickInfoForDirectiveSymbol(matches.values().next().value, templateNode);
44172 }
44173 return createQuickInfo(templateNode.name, DisplayInfoKind.ELEMENT, getTextSpanOfNode(templateNode), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType));
44174 }
44175 getQuickInfoForVariableSymbol(symbol) {
44176 const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.initializerLocation);
44177 return createQuickInfo(symbol.declaration.name, DisplayInfoKind.VARIABLE, getTextSpanOfNode(this.node), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType), documentation);
44178 }
44179 getQuickInfoForReferenceSymbol(symbol) {
44180 const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.targetLocation);
44181 return createQuickInfo(symbol.declaration.name, DisplayInfoKind.REFERENCE, getTextSpanOfNode(this.node), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType), documentation);
44182 }
44183 getQuickInfoForPipeSymbol(symbol) {
44184 if (symbol.tsSymbol !== null) {
44185 const quickInfo = this.getQuickInfoAtShimLocation(symbol.shimLocation);
44186 return quickInfo === undefined ? undefined :
44187 updateQuickInfoKind(quickInfo, DisplayInfoKind.PIPE);
44188 }
44189 else {
44190 return createQuickInfo(this.typeChecker.typeToString(symbol.classSymbol.tsType), DisplayInfoKind.PIPE, getTextSpanOfNode(this.node));
44191 }
44192 }
44193 getQuickInfoForDomBinding(symbol) {
44194 if (!(this.node instanceof TextAttribute) &&
44195 !(this.node instanceof BoundAttribute)) {
44196 return undefined;
44197 }
44198 const directives = getDirectiveMatchesForAttribute(this.node.name, symbol.host.templateNode, symbol.host.directives);
44199 if (directives.size === 0) {
44200 return undefined;
44201 }
44202 return this.getQuickInfoForDirectiveSymbol(directives.values().next().value);
44203 }
44204 getQuickInfoForDirectiveSymbol(dir, node = this.node) {
44205 const kind = dir.isComponent ? DisplayInfoKind.COMPONENT : DisplayInfoKind.DIRECTIVE;
44206 const documentation = this.getDocumentationFromTypeDefAtLocation(dir.shimLocation);
44207 let containerName;
44208 if (ts__default["default"].isClassDeclaration(dir.tsSymbol.valueDeclaration) && dir.ngModule !== null) {
44209 containerName = dir.ngModule.name.getText();
44210 }
44211 return createQuickInfo(this.typeChecker.typeToString(dir.tsType), kind, getTextSpanOfNode(this.node), containerName, undefined, documentation);
44212 }
44213 getDocumentationFromTypeDefAtLocation(shimLocation) {
44214 const typeDefs = this.tsLS.getTypeDefinitionAtPosition(shimLocation.shimPath, shimLocation.positionInShimFile);
44215 if (typeDefs === undefined || typeDefs.length === 0) {
44216 return undefined;
44217 }
44218 return this.tsLS.getQuickInfoAtPosition(typeDefs[0].fileName, typeDefs[0].textSpan.start)
44219 ?.documentation;
44220 }
44221 getQuickInfoAtShimLocation(location) {
44222 const quickInfo = this.tsLS.getQuickInfoAtPosition(location.shimPath, location.positionInShimFile);
44223 if (quickInfo === undefined || quickInfo.displayParts === undefined) {
44224 return quickInfo;
44225 }
44226 quickInfo.displayParts = filterAliasImports(quickInfo.displayParts);
44227 const textSpan = getTextSpanOfNode(this.node);
44228 return { ...quickInfo, textSpan };
44229 }
44230 }
44231 function updateQuickInfoKind(quickInfo, kind) {
44232 if (quickInfo.displayParts === undefined) {
44233 return quickInfo;
44234 }
44235 const startsWithKind = quickInfo.displayParts.length >= 3 &&
44236 displayPartsEqual(quickInfo.displayParts[0], { text: '(', kind: SYMBOL_PUNC }) &&
44237 quickInfo.displayParts[1].kind === SYMBOL_TEXT &&
44238 displayPartsEqual(quickInfo.displayParts[2], { text: ')', kind: SYMBOL_PUNC });
44239 if (startsWithKind) {
44240 quickInfo.displayParts[1].text = kind;
44241 }
44242 else {
44243 quickInfo.displayParts = [
44244 { text: '(', kind: SYMBOL_PUNC },
44245 { text: kind, kind: SYMBOL_TEXT },
44246 { text: ')', kind: SYMBOL_PUNC },
44247 { text: ' ', kind: SYMBOL_SPACE },
44248 ...quickInfo.displayParts,
44249 ];
44250 }
44251 return quickInfo;
44252 }
44253 function displayPartsEqual(a, b) {
44254 return a.text === b.text && a.kind === b.kind;
44255 }
44256 function isDollarAny(node) {
44257 return node instanceof Call && node.receiver instanceof PropertyRead &&
44258 node.receiver.receiver instanceof ImplicitReceiver &&
44259 !(node.receiver.receiver instanceof ThisReceiver) && node.receiver.name === '$any' &&
44260 node.args.length === 1;
44261 }
44262 function createDollarAnyQuickInfo(node) {
44263 return createQuickInfo('$any', DisplayInfoKind.METHOD, getTextSpanOfNode(node.receiver),
44264 /** containerName */ undefined, 'any', [{
44265 kind: SYMBOL_TEXT,
44266 text: 'function to cast an expression to the `any` type',
44267 }]);
44268 }
44269 // TODO(atscott): Create special `ts.QuickInfo` for `ng-template` and `ng-container` as well.
44270 function createNgTemplateQuickInfo(node) {
44271 return createQuickInfo('ng-template', DisplayInfoKind.TEMPLATE, getTextSpanOfNode(node),
44272 /** containerName */ undefined,
44273 /** type */ undefined, [{
44274 kind: SYMBOL_TEXT,
44275 text: 'The `<ng-template>` is an Angular element for rendering HTML. It is never displayed directly.',
44276 }]);
44277 }
44278 /**
44279 * Construct a QuickInfo object taking into account its container and type.
44280 * @param name Name of the QuickInfo target
44281 * @param kind component, directive, pipe, etc.
44282 * @param textSpan span of the target
44283 * @param containerName either the Symbol's container or the NgModule that contains the directive
44284 * @param type user-friendly name of the type
44285 * @param documentation docstring or comment
44286 */
44287 function createQuickInfo(name, kind, textSpan, containerName, type, documentation) {
44288 const displayParts = createDisplayParts(name, kind, containerName, type);
44289 return {
44290 kind: unsafeCastDisplayInfoKindToScriptElementKind(kind),
44291 kindModifiers: ts__default["default"].ScriptElementKindModifier.none,
44292 textSpan: textSpan,
44293 displayParts,
44294 documentation,
44295 };
44296 }
44297
44298 class ReferencesBuilder {
44299 constructor(driver, tsLS, compiler) {
44300 this.driver = driver;
44301 this.tsLS = tsLS;
44302 this.compiler = compiler;
44303 this.ttc = this.compiler.getTemplateTypeChecker();
44304 }
44305 getReferencesAtPosition(filePath, position) {
44306 this.ttc.generateAllTypeCheckBlocks();
44307 const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
44308 if (templateInfo === undefined) {
44309 return this.getReferencesAtTypescriptPosition(filePath, position);
44310 }
44311 return this.getReferencesAtTemplatePosition(templateInfo, position);
44312 }
44313 getReferencesAtTemplatePosition(templateInfo, position) {
44314 const allTargetDetails = getTargetDetailsAtTemplatePosition(templateInfo, position, this.ttc);
44315 if (allTargetDetails === null) {
44316 return undefined;
44317 }
44318 const allReferences = [];
44319 for (const targetDetails of allTargetDetails) {
44320 for (const location of targetDetails.typescriptLocations) {
44321 const refs = this.getReferencesAtTypescriptPosition(location.fileName, location.position);
44322 if (refs !== undefined) {
44323 allReferences.push(...refs);
44324 }
44325 }
44326 }
44327 return allReferences.length > 0 ? allReferences : undefined;
44328 }
44329 getReferencesAtTypescriptPosition(fileName, position) {
44330 const refs = this.tsLS.getReferencesAtPosition(fileName, position);
44331 if (refs === undefined) {
44332 return undefined;
44333 }
44334 const entries = [];
44335 for (const ref of refs) {
44336 if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(ref.fileName))) {
44337 const entry = convertToTemplateDocumentSpan(ref, this.ttc, this.driver.getProgram());
44338 if (entry !== null) {
44339 entries.push(entry);
44340 }
44341 }
44342 else {
44343 entries.push(ref);
44344 }
44345 }
44346 return entries;
44347 }
44348 }
44349 var RequestKind;
44350 (function (RequestKind) {
44351 RequestKind[RequestKind["DirectFromTemplate"] = 0] = "DirectFromTemplate";
44352 RequestKind[RequestKind["DirectFromTypeScript"] = 1] = "DirectFromTypeScript";
44353 RequestKind[RequestKind["PipeName"] = 2] = "PipeName";
44354 RequestKind[RequestKind["Selector"] = 3] = "Selector";
44355 })(RequestKind || (RequestKind = {}));
44356 function isDirectRenameContext(context) {
44357 return context.type === RequestKind.DirectFromTemplate ||
44358 context.type === RequestKind.DirectFromTypeScript;
44359 }
44360 class RenameBuilder {
44361 constructor(driver, tsLS, compiler) {
44362 this.driver = driver;
44363 this.tsLS = tsLS;
44364 this.compiler = compiler;
44365 this.ttc = this.compiler.getTemplateTypeChecker();
44366 }
44367 getRenameInfo(filePath, position) {
44368 return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
44369 const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
44370 // We could not get a template at position so we assume the request came from outside the
44371 // template.
44372 if (templateInfo === undefined) {
44373 const renameRequest = this.buildRenameRequestAtTypescriptPosition(filePath, position);
44374 if (renameRequest === null) {
44375 return {
44376 canRename: false,
44377 localizedErrorMessage: 'Could not determine rename info at typescript position.',
44378 };
44379 }
44380 if (renameRequest.type === RequestKind.PipeName) {
44381 const pipeName = renameRequest.pipeNameExpr.text;
44382 return {
44383 canRename: true,
44384 displayName: pipeName,
44385 fullDisplayName: pipeName,
44386 triggerSpan: {
44387 length: pipeName.length,
44388 // Offset the pipe name by 1 to account for start of string '/`/"
44389 start: renameRequest.pipeNameExpr.getStart() + 1,
44390 },
44391 };
44392 }
44393 else {
44394 // TODO(atscott): Add support for other special indirect renames from typescript files.
44395 return this.tsLS.getRenameInfo(filePath, position);
44396 }
44397 }
44398 const allTargetDetails = getTargetDetailsAtTemplatePosition(templateInfo, position, this.ttc);
44399 if (allTargetDetails === null) {
44400 return {
44401 canRename: false,
44402 localizedErrorMessage: 'Could not find template node at position.'
44403 };
44404 }
44405 const { templateTarget } = allTargetDetails[0];
44406 const templateTextAndSpan = getRenameTextAndSpanAtPosition(templateTarget, position);
44407 if (templateTextAndSpan === null) {
44408 return { canRename: false, localizedErrorMessage: 'Could not determine template node text.' };
44409 }
44410 const { text, span } = templateTextAndSpan;
44411 return {
44412 canRename: true,
44413 displayName: text,
44414 fullDisplayName: text,
44415 triggerSpan: span,
44416 };
44417 });
44418 }
44419 findRenameLocations(filePath, position) {
44420 this.ttc.generateAllTypeCheckBlocks();
44421 return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
44422 const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
44423 // We could not get a template at position so we assume the request came from outside the
44424 // template.
44425 if (templateInfo === undefined) {
44426 const renameRequest = this.buildRenameRequestAtTypescriptPosition(filePath, position);
44427 if (renameRequest === null) {
44428 return null;
44429 }
44430 return this.findRenameLocationsAtTypescriptPosition(renameRequest);
44431 }
44432 return this.findRenameLocationsAtTemplatePosition(templateInfo, position);
44433 });
44434 }
44435 findRenameLocationsAtTemplatePosition(templateInfo, position) {
44436 const allTargetDetails = getTargetDetailsAtTemplatePosition(templateInfo, position, this.ttc);
44437 if (allTargetDetails === null) {
44438 return null;
44439 }
44440 const renameRequests = this.buildRenameRequestsFromTemplateDetails(allTargetDetails, position);
44441 if (renameRequests === null) {
44442 return null;
44443 }
44444 const allRenameLocations = [];
44445 for (const renameRequest of renameRequests) {
44446 const locations = this.findRenameLocationsAtTypescriptPosition(renameRequest);
44447 // If we couldn't find rename locations for _any_ result, we should not allow renaming to
44448 // proceed instead of having a partially complete rename.
44449 if (locations === null) {
44450 return null;
44451 }
44452 allRenameLocations.push(...locations);
44453 }
44454 return allRenameLocations.length > 0 ? allRenameLocations : null;
44455 }
44456 findRenameLocationsAtTypescriptPosition(renameRequest) {
44457 return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
44458 const renameInfo = getExpectedRenameTextAndInitalRenameEntries(renameRequest);
44459 if (renameInfo === null) {
44460 return null;
44461 }
44462 const { entries, expectedRenameText } = renameInfo;
44463 const { fileName, position } = getRenameRequestPosition(renameRequest);
44464 const findInStrings = false;
44465 const findInComments = false;
44466 const locations = this.tsLS.findRenameLocations(fileName, position, findInStrings, findInComments);
44467 if (locations === undefined) {
44468 return null;
44469 }
44470 for (const location of locations) {
44471 if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(location.fileName))) {
44472 const entry = convertToTemplateDocumentSpan(location, this.ttc, this.driver.getProgram(), expectedRenameText);
44473 // There is no template node whose text matches the original rename request. Bail on
44474 // renaming completely rather than providing incomplete results.
44475 if (entry === null) {
44476 return null;
44477 }
44478 entries.push(entry);
44479 }
44480 else {
44481 if (!isDirectRenameContext(renameRequest)) {
44482 // Discard any non-template results for non-direct renames. We should only rename
44483 // template results + the name/selector/alias `ts.Expression`. The other results
44484 // will be the the `ts.Identifier` of the transform method (pipe rename) or the
44485 // directive class (selector rename).
44486 continue;
44487 }
44488 // Ensure we only allow renaming a TS result with matching text
44489 const refNode = this.getTsNodeAtPosition(location.fileName, location.textSpan.start);
44490 if (refNode === null || refNode.getText() !== expectedRenameText) {
44491 return null;
44492 }
44493 entries.push(location);
44494 }
44495 }
44496 return entries;
44497 });
44498 }
44499 getTsNodeAtPosition(filePath, position) {
44500 const sf = this.driver.getProgram().getSourceFile(filePath);
44501 if (!sf) {
44502 return null;
44503 }
44504 return findTightestNode(sf, position) ?? null;
44505 }
44506 buildRenameRequestsFromTemplateDetails(allTargetDetails, templatePosition) {
44507 const renameRequests = [];
44508 for (const targetDetails of allTargetDetails) {
44509 for (const location of targetDetails.typescriptLocations) {
44510 if (targetDetails.symbol.kind === SymbolKind.Pipe) {
44511 const meta = this.compiler.getMeta(targetDetails.symbol.classSymbol.tsSymbol.valueDeclaration);
44512 if (meta === null || meta.type !== MetaType.Pipe) {
44513 return null;
44514 }
44515 const renameRequest = this.buildPipeRenameRequest(meta);
44516 if (renameRequest === null) {
44517 return null;
44518 }
44519 renameRequests.push(renameRequest);
44520 }
44521 else {
44522 const renameRequest = {
44523 type: RequestKind.DirectFromTemplate,
44524 templatePosition,
44525 requestNode: targetDetails.templateTarget,
44526 renamePosition: location
44527 };
44528 renameRequests.push(renameRequest);
44529 }
44530 }
44531 }
44532 return renameRequests;
44533 }
44534 buildRenameRequestAtTypescriptPosition(filePath, position) {
44535 const requestNode = this.getTsNodeAtPosition(filePath, position);
44536 if (requestNode === null) {
44537 return null;
44538 }
44539 const meta = getParentClassMeta(requestNode, this.compiler);
44540 if (meta !== null && meta.type === MetaType.Pipe && meta.nameExpr === requestNode) {
44541 return this.buildPipeRenameRequest(meta);
44542 }
44543 else {
44544 return { type: RequestKind.DirectFromTypeScript, requestNode };
44545 }
44546 }
44547 buildPipeRenameRequest(meta) {
44548 if (!ts__default["default"].isClassDeclaration(meta.ref.node) || meta.nameExpr === null ||
44549 !ts__default["default"].isStringLiteral(meta.nameExpr)) {
44550 return null;
44551 }
44552 const typeChecker = this.driver.getProgram().getTypeChecker();
44553 const memberMethods = collectMemberMethods(meta.ref.node, typeChecker) ?? [];
44554 const pipeTransformNode = memberMethods.find(m => m.name.getText() === 'transform');
44555 if (pipeTransformNode === undefined) {
44556 return null;
44557 }
44558 return {
44559 type: RequestKind.PipeName,
44560 pipeNameExpr: meta.nameExpr,
44561 renamePosition: {
44562 fileName: pipeTransformNode.getSourceFile().fileName,
44563 position: pipeTransformNode.getStart(),
44564 }
44565 };
44566 }
44567 }
44568 /**
44569 * From the provided `RenameRequest`, determines what text we should expect all produced
44570 * `ts.RenameLocation`s to have and creates an initial entry for indirect renames (one which is
44571 * required for the rename operation, but cannot be found by the native TS LS).
44572 */
44573 function getExpectedRenameTextAndInitalRenameEntries(renameRequest) {
44574 let expectedRenameText;
44575 const entries = [];
44576 if (renameRequest.type === RequestKind.DirectFromTypeScript) {
44577 expectedRenameText = renameRequest.requestNode.getText();
44578 }
44579 else if (renameRequest.type === RequestKind.DirectFromTemplate) {
44580 const templateNodeText = getRenameTextAndSpanAtPosition(renameRequest.requestNode, renameRequest.templatePosition);
44581 if (templateNodeText === null) {
44582 return null;
44583 }
44584 expectedRenameText = templateNodeText.text;
44585 }
44586 else if (renameRequest.type === RequestKind.PipeName) {
44587 const { pipeNameExpr } = renameRequest;
44588 expectedRenameText = pipeNameExpr.text;
44589 const entry = {
44590 fileName: renameRequest.pipeNameExpr.getSourceFile().fileName,
44591 textSpan: { start: pipeNameExpr.getStart() + 1, length: pipeNameExpr.getText().length - 2 },
44592 };
44593 entries.push(entry);
44594 }
44595 else {
44596 // TODO(atscott): Implement other types of special renames
44597 return null;
44598 }
44599 return { entries, expectedRenameText };
44600 }
44601 /**
44602 * Given a `RenameRequest`, determines the `FilePosition` to use asking the native TS LS for rename
44603 * locations.
44604 */
44605 function getRenameRequestPosition(renameRequest) {
44606 const fileName = renameRequest.type === RequestKind.DirectFromTypeScript ?
44607 renameRequest.requestNode.getSourceFile().fileName :
44608 renameRequest.renamePosition.fileName;
44609 const position = renameRequest.type === RequestKind.DirectFromTypeScript ?
44610 renameRequest.requestNode.getStart() :
44611 renameRequest.renamePosition.position;
44612 return { fileName, position };
44613 }
44614
44615 /**
44616 * @license
44617 * Copyright Google LLC All Rights Reserved.
44618 *
44619 * Use of this source code is governed by an MIT-style license that can be
44620 * found in the LICENSE file at https://angular.io/license
44621 */
44622 /**
44623 * Queries the TypeScript Language Service to get signature help for a template position.
44624 */
44625 function getSignatureHelp(compiler, tsLS, fileName, position, options) {
44626 const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
44627 if (templateInfo === undefined) {
44628 return undefined;
44629 }
44630 const targetInfo = getTargetAtPosition(templateInfo.template, position);
44631 if (targetInfo === null) {
44632 return undefined;
44633 }
44634 if (targetInfo.context.kind !== TargetNodeKind.RawExpression &&
44635 targetInfo.context.kind !== TargetNodeKind.CallExpressionInArgContext) {
44636 // Signature completions are only available in expressions.
44637 return undefined;
44638 }
44639 const symbol = compiler.getTemplateTypeChecker().getSymbolOfNode(targetInfo.context.node, templateInfo.component);
44640 if (symbol === null || symbol.kind !== SymbolKind.Expression) {
44641 return undefined;
44642 }
44643 // Determine a shim position to use in the request to the TypeScript Language Service.
44644 // Additionally, extract the `Call` node for which signature help is being queried, as this
44645 // is needed to construct the correct span for the results later.
44646 let shimPosition;
44647 let expr;
44648 switch (targetInfo.context.kind) {
44649 case TargetNodeKind.RawExpression:
44650 // For normal expressions, just use the primary TCB position of the expression.
44651 shimPosition = symbol.shimLocation.positionInShimFile;
44652 // Walk up the parents of this expression and try to find a
44653 // `Call` for which signature information is being fetched.
44654 let callExpr = null;
44655 const parents = targetInfo.context.parents;
44656 for (let i = parents.length - 1; i >= 0; i--) {
44657 const parent = parents[i];
44658 if (parent instanceof Call) {
44659 callExpr = parent;
44660 break;
44661 }
44662 }
44663 // If no Call node could be found, then this query cannot be safely
44664 // answered as a correct span for the results will not be obtainable.
44665 if (callExpr === null) {
44666 return undefined;
44667 }
44668 expr = callExpr;
44669 break;
44670 case TargetNodeKind.CallExpressionInArgContext:
44671 // The `Symbol` points to a `Call` expression in the TCB (where it will be represented as a
44672 // `ts.CallExpression`) *and* the template position was within the argument list of the method
44673 // call. This happens when there was no narrower expression inside the argument list that
44674 // matched the template position, such as when the call has no arguments: `foo(|)`.
44675 //
44676 // The `Symbol`'s shim position is to the start of the call expression (`|foo()`) and
44677 // therefore wouldn't return accurate signature help from the TS language service. For that, a
44678 // position within the argument list for the `ts.CallExpression` in the TCB will need to be
44679 // determined. This is done by finding that call expression and extracting a viable position
44680 // from it directly.
44681 //
44682 // First, use `findTightestNode` to locate the `ts.Node` at `symbol`'s location.
44683 const shimSf = getSourceFileOrError(compiler.getCurrentProgram(), symbol.shimLocation.shimPath);
44684 let shimNode = findTightestNode(shimSf, symbol.shimLocation.positionInShimFile) ?? null;
44685 // This node should be somewhere inside a `ts.CallExpression`. Walk up the AST to find it.
44686 while (shimNode !== null) {
44687 if (ts__namespace.isCallExpression(shimNode)) {
44688 break;
44689 }
44690 shimNode = shimNode.parent ?? null;
44691 }
44692 // If one couldn't be found, something is wrong, so bail rather than report incorrect results.
44693 if (shimNode === null || !ts__namespace.isCallExpression(shimNode)) {
44694 return undefined;
44695 }
44696 // Position the cursor in the TCB at the start of the argument list for the
44697 // `ts.CallExpression`. This will allow us to get the correct signature help, even though the
44698 // template itself doesn't have an expression inside the argument list.
44699 shimPosition = shimNode.arguments.pos;
44700 // In this case, getting the right call AST node is easy.
44701 expr = targetInfo.context.node;
44702 break;
44703 }
44704 const res = tsLS.getSignatureHelpItems(symbol.shimLocation.shimPath, shimPosition, options);
44705 if (res === undefined) {
44706 return undefined;
44707 }
44708 // The TS language service results are almost returnable as-is. However, they contain an
44709 // `applicableSpan` which marks the entire argument list, and that span is in the context of the
44710 // TCB's `ts.CallExpression`. It needs to be replaced with the span for the `Call` argument list.
44711 return {
44712 ...res,
44713 applicableSpan: {
44714 start: expr.argumentSpan.start,
44715 length: expr.argumentSpan.end - expr.argumentSpan.start,
44716 },
44717 };
44718 }
44719
44720 /**
44721 * @license
44722 * Copyright Google LLC All Rights Reserved.
44723 *
44724 * Use of this source code is governed by an MIT-style license that can be
44725 * found in the LICENSE file at https://angular.io/license
44726 */
44727 class LanguageService {
44728 constructor(project, tsLS, config) {
44729 this.project = project;
44730 this.tsLS = tsLS;
44731 this.config = config;
44732 this.parseConfigHost = new LSParseConfigHost(project.projectService.host);
44733 this.options = parseNgCompilerOptions(project, this.parseConfigHost, config);
44734 logCompilerOptions(project, this.options);
44735 this.programDriver = createProgramDriver(project);
44736 this.adapter = new LanguageServiceAdapter(project);
44737 this.compilerFactory = new CompilerFactory(this.adapter, this.programDriver, this.options);
44738 this.watchConfigFile(project);
44739 }
44740 getCompilerOptions() {
44741 return this.options;
44742 }
44743 getSemanticDiagnostics(fileName) {
44744 return this.withCompilerAndPerfTracing(PerfPhase.LsDiagnostics, (compiler) => {
44745 const diagnostics = [];
44746 if (isTypeScriptFile(fileName)) {
44747 const program = compiler.getCurrentProgram();
44748 const sourceFile = program.getSourceFile(fileName);
44749 if (sourceFile) {
44750 const ngDiagnostics = compiler.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile);
44751 // There are several kinds of diagnostics returned by `NgCompiler` for a source file:
44752 //
44753 // 1. Angular-related non-template diagnostics from decorated classes within that
44754 // file.
44755 // 2. Template diagnostics for components with direct inline templates (a string
44756 // literal).
44757 // 3. Template diagnostics for components with indirect inline templates (templates
44758 // computed
44759 // by expression).
44760 // 4. Template diagnostics for components with external templates.
44761 //
44762 // When showing diagnostics for a TS source file, we want to only include kinds 1 and
44763 // 2 - those diagnostics which are reported at a location within the TS file itself.
44764 // Diagnostics for external templates will be shown when editing that template file
44765 // (the `else` block) below.
44766 //
44767 // Currently, indirect inline template diagnostics (kind 3) are not shown at all by
44768 // the Language Service, because there is no sensible location in the user's code for
44769 // them. Such templates are an edge case, though, and should not be common.
44770 //
44771 // TODO(alxhub): figure out a good user experience for indirect template diagnostics
44772 // and show them from within the Language Service.
44773 diagnostics.push(...ngDiagnostics.filter(diag => diag.file !== undefined && diag.file.fileName === sourceFile.fileName));
44774 }
44775 }
44776 else {
44777 const components = compiler.getComponentsWithTemplateFile(fileName);
44778 for (const component of components) {
44779 if (ts__namespace.isClassDeclaration(component)) {
44780 diagnostics.push(...compiler.getDiagnosticsForComponent(component));
44781 }
44782 }
44783 }
44784 return diagnostics;
44785 });
44786 }
44787 getDefinitionAndBoundSpan(fileName, position) {
44788 return this.withCompilerAndPerfTracing(PerfPhase.LsDefinition, (compiler) => {
44789 if (!isInAngularContext(compiler.getCurrentProgram(), fileName, position)) {
44790 return undefined;
44791 }
44792 return new DefinitionBuilder(this.tsLS, compiler, this.programDriver)
44793 .getDefinitionAndBoundSpan(fileName, position);
44794 });
44795 }
44796 getTypeDefinitionAtPosition(fileName, position) {
44797 return this.withCompilerAndPerfTracing(PerfPhase.LsDefinition, (compiler) => {
44798 if (!isTemplateContext(compiler.getCurrentProgram(), fileName, position)) {
44799 return undefined;
44800 }
44801 return new DefinitionBuilder(this.tsLS, compiler, this.programDriver)
44802 .getTypeDefinitionsAtPosition(fileName, position);
44803 });
44804 }
44805 getQuickInfoAtPosition(fileName, position) {
44806 return this.withCompilerAndPerfTracing(PerfPhase.LsQuickInfo, (compiler) => {
44807 return this.getQuickInfoAtPositionImpl(fileName, position, compiler);
44808 });
44809 }
44810 getQuickInfoAtPositionImpl(fileName, position, compiler) {
44811 if (!isTemplateContext(compiler.getCurrentProgram(), fileName, position)) {
44812 return undefined;
44813 }
44814 const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
44815 if (templateInfo === undefined) {
44816 return undefined;
44817 }
44818 const positionDetails = getTargetAtPosition(templateInfo.template, position);
44819 if (positionDetails === null) {
44820 return undefined;
44821 }
44822 // Because we can only show 1 quick info, just use the bound attribute if the target is a two
44823 // way binding. We may consider concatenating additional display parts from the other target
44824 // nodes or representing the two way binding in some other manner in the future.
44825 const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
44826 positionDetails.context.nodes[0] :
44827 positionDetails.context.node;
44828 return new QuickInfoBuilder(this.tsLS, compiler, templateInfo.component, node, positionDetails.parent)
44829 .get();
44830 }
44831 getReferencesAtPosition(fileName, position) {
44832 return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
44833 const results = new ReferencesBuilder(this.programDriver, this.tsLS, compiler)
44834 .getReferencesAtPosition(fileName, position);
44835 return results === undefined ? undefined : getUniqueLocations(results);
44836 });
44837 }
44838 getRenameInfo(fileName, position) {
44839 return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
44840 const renameInfo = new RenameBuilder(this.programDriver, this.tsLS, compiler)
44841 .getRenameInfo(absoluteFrom(fileName), position);
44842 if (!renameInfo.canRename) {
44843 return renameInfo;
44844 }
44845 const quickInfo = this.getQuickInfoAtPositionImpl(fileName, position, compiler) ??
44846 this.tsLS.getQuickInfoAtPosition(fileName, position);
44847 const kind = quickInfo?.kind ?? ts__namespace.ScriptElementKind.unknown;
44848 const kindModifiers = quickInfo?.kindModifiers ?? ts__namespace.ScriptElementKind.unknown;
44849 return { ...renameInfo, kind, kindModifiers };
44850 });
44851 }
44852 findRenameLocations(fileName, position) {
44853 return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
44854 const results = new RenameBuilder(this.programDriver, this.tsLS, compiler)
44855 .findRenameLocations(fileName, position);
44856 return results === null ? undefined : getUniqueLocations(results);
44857 });
44858 }
44859 getCompletionBuilder(fileName, position, compiler) {
44860 const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
44861 if (templateInfo === undefined) {
44862 return null;
44863 }
44864 const positionDetails = getTargetAtPosition(templateInfo.template, position);
44865 if (positionDetails === null) {
44866 return null;
44867 }
44868 // For two-way bindings, we actually only need to be concerned with the bound attribute because
44869 // the bindings in the template are written with the attribute name, not the event name.
44870 const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
44871 positionDetails.context.nodes[0] :
44872 positionDetails.context.node;
44873 return new CompletionBuilder(this.tsLS, compiler, templateInfo.component, node, positionDetails);
44874 }
44875 getCompletionsAtPosition(fileName, position, options) {
44876 return this.withCompilerAndPerfTracing(PerfPhase.LsCompletions, (compiler) => {
44877 return this.getCompletionsAtPositionImpl(fileName, position, options, compiler);
44878 });
44879 }
44880 getCompletionsAtPositionImpl(fileName, position, options, compiler) {
44881 if (!isTemplateContext(compiler.getCurrentProgram(), fileName, position)) {
44882 return undefined;
44883 }
44884 const builder = this.getCompletionBuilder(fileName, position, compiler);
44885 if (builder === null) {
44886 return undefined;
44887 }
44888 return builder.getCompletionsAtPosition(options);
44889 }
44890 getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences, data) {
44891 return this.withCompilerAndPerfTracing(PerfPhase.LsCompletions, (compiler) => {
44892 if (!isTemplateContext(compiler.getCurrentProgram(), fileName, position)) {
44893 return undefined;
44894 }
44895 const builder = this.getCompletionBuilder(fileName, position, compiler);
44896 if (builder === null) {
44897 return undefined;
44898 }
44899 return builder.getCompletionEntryDetails(entryName, formatOptions, preferences, data);
44900 });
44901 }
44902 getSignatureHelpItems(fileName, position, options) {
44903 return this.withCompilerAndPerfTracing(PerfPhase.LsSignatureHelp, compiler => {
44904 if (!isTemplateContext(compiler.getCurrentProgram(), fileName, position)) {
44905 return undefined;
44906 }
44907 return getSignatureHelp(compiler, this.tsLS, fileName, position, options);
44908 });
44909 }
44910 getCompletionEntrySymbol(fileName, position, entryName) {
44911 return this.withCompilerAndPerfTracing(PerfPhase.LsCompletions, (compiler) => {
44912 if (!isTemplateContext(compiler.getCurrentProgram(), fileName, position)) {
44913 return undefined;
44914 }
44915 const builder = this.getCompletionBuilder(fileName, position, compiler);
44916 if (builder === null) {
44917 return undefined;
44918 }
44919 const result = builder.getCompletionEntrySymbol(entryName);
44920 return result;
44921 });
44922 }
44923 getComponentLocationsForTemplate(fileName) {
44924 return this.withCompilerAndPerfTracing(PerfPhase.LsComponentLocations, (compiler) => {
44925 const components = compiler.getComponentsWithTemplateFile(fileName);
44926 const componentDeclarationLocations = Array.from(components.values()).map(c => {
44927 let contextSpan = undefined;
44928 let textSpan;
44929 if (isNamedClassDeclaration(c)) {
44930 textSpan = ts__namespace.createTextSpanFromBounds(c.name.getStart(), c.name.getEnd());
44931 contextSpan = ts__namespace.createTextSpanFromBounds(c.getStart(), c.getEnd());
44932 }
44933 else {
44934 textSpan = ts__namespace.createTextSpanFromBounds(c.getStart(), c.getEnd());
44935 }
44936 return {
44937 fileName: c.getSourceFile().fileName,
44938 textSpan,
44939 contextSpan,
44940 };
44941 });
44942 return componentDeclarationLocations;
44943 });
44944 }
44945 getTemplateLocationForComponent(fileName, position) {
44946 return this.withCompilerAndPerfTracing(PerfPhase.LsComponentLocations, (compiler) => {
44947 const nearestNode = findTightestNodeAtPosition(this.programDriver.getProgram(), fileName, position);
44948 if (nearestNode === undefined) {
44949 return undefined;
44950 }
44951 const classDeclaration = getParentClassDeclaration(nearestNode);
44952 if (classDeclaration === undefined) {
44953 return undefined;
44954 }
44955 const resources = compiler.getComponentResources(classDeclaration);
44956 if (resources === null) {
44957 return undefined;
44958 }
44959 const { template } = resources;
44960 let templateFileName;
44961 let span;
44962 if (template.path !== null) {
44963 span = ts__namespace.createTextSpanFromBounds(0, 0);
44964 templateFileName = template.path;
44965 }
44966 else {
44967 span = ts__namespace.createTextSpanFromBounds(template.expression.getStart(), template.expression.getEnd());
44968 templateFileName = template.expression.getSourceFile().fileName;
44969 }
44970 return { fileName: templateFileName, textSpan: span, contextSpan: span };
44971 });
44972 }
44973 getTcb(fileName, position) {
44974 return this.withCompilerAndPerfTracing(PerfPhase.LsTcb, compiler => {
44975 const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
44976 if (templateInfo === undefined) {
44977 return undefined;
44978 }
44979 const tcb = compiler.getTemplateTypeChecker().getTypeCheckBlock(templateInfo.component);
44980 if (tcb === null) {
44981 return undefined;
44982 }
44983 const sf = tcb.getSourceFile();
44984 let selections = [];
44985 const target = getTargetAtPosition(templateInfo.template, position);
44986 if (target !== null) {
44987 let selectionSpans;
44988 if ('nodes' in target.context) {
44989 selectionSpans = target.context.nodes.map(n => n.sourceSpan);
44990 }
44991 else {
44992 selectionSpans = [target.context.node.sourceSpan];
44993 }
44994 const selectionNodes = selectionSpans
44995 .map(s => findFirstMatchingNode(tcb, {
44996 withSpan: s,
44997 filter: (node) => true,
44998 }))
44999 .filter((n) => n !== null);
45000 selections = selectionNodes.map(n => {
45001 return {
45002 start: n.getStart(sf),
45003 length: n.getEnd() - n.getStart(sf),
45004 };
45005 });
45006 }
45007 return {
45008 fileName: sf.fileName,
45009 content: sf.getFullText(),
45010 selections,
45011 };
45012 });
45013 }
45014 /**
45015 * Provides an instance of the `NgCompiler` and traces perf results. Perf results are logged only
45016 * if the log level is verbose or higher. This method is intended to be called once per public
45017 * method call.
45018 *
45019 * Here is an example of the log output.
45020 *
45021 * Perf 245 [16:16:39.353] LanguageService#getQuickInfoAtPosition(): {"events":{},"phases":{
45022 * "Unaccounted":379,"TtcSymbol":4},"memory":{}}
45023 *
45024 * Passing name of caller instead of using `arguments.caller` because 'caller', 'callee', and
45025 * 'arguments' properties may not be accessed in strict mode.
45026 *
45027 * @param phase the `PerfPhase` to execute the `p` callback in
45028 * @param p callback to be run synchronously with an instance of the `NgCompiler` as argument
45029 * @return the result of running the `p` callback
45030 */
45031 withCompilerAndPerfTracing(phase, p) {
45032 const compiler = this.compilerFactory.getOrCreate();
45033 const result = compiler.perfRecorder.inPhase(phase, () => p(compiler));
45034 const logger = this.project.projectService.logger;
45035 if (logger.hasLevel(ts__namespace.server.LogLevel.verbose)) {
45036 logger.perftrc(`LanguageService#${PerfPhase[phase]}: ${JSON.stringify(compiler.perfRecorder.finalize())}`);
45037 }
45038 return result;
45039 }
45040 getCompilerOptionsDiagnostics() {
45041 const project = this.project;
45042 if (!(project instanceof ts__namespace.server.ConfiguredProject)) {
45043 return [];
45044 }
45045 return this.withCompilerAndPerfTracing(PerfPhase.LsDiagnostics, (compiler) => {
45046 const diagnostics = [];
45047 const configSourceFile = ts__namespace.readJsonConfigFile(project.getConfigFilePath(), (path) => project.readFile(path));
45048 if (!this.options.strictTemplates && !this.options.fullTemplateTypeCheck) {
45049 diagnostics.push({
45050 messageText: 'Some language features are not available. ' +
45051 'To access all features, enable `strictTemplates` in `angularCompilerOptions`.',
45052 category: ts__namespace.DiagnosticCategory.Suggestion,
45053 code: ngErrorCode(ErrorCode.SUGGEST_STRICT_TEMPLATES),
45054 file: configSourceFile,
45055 start: undefined,
45056 length: undefined,
45057 });
45058 }
45059 diagnostics.push(...compiler.getOptionDiagnostics());
45060 return diagnostics;
45061 });
45062 }
45063 watchConfigFile(project) {
45064 // TODO: Check the case when the project is disposed. An InferredProject
45065 // could be disposed when a tsconfig.json is added to the workspace,
45066 // in which case it becomes a ConfiguredProject (or vice-versa).
45067 // We need to make sure that the FileWatcher is closed.
45068 if (!(project instanceof ts__namespace.server.ConfiguredProject)) {
45069 return;
45070 }
45071 const { host } = project.projectService;
45072 host.watchFile(project.getConfigFilePath(), (fileName, eventKind) => {
45073 project.log(`Config file changed: ${fileName}`);
45074 if (eventKind === ts__namespace.FileWatcherEventKind.Changed) {
45075 this.options = parseNgCompilerOptions(project, this.parseConfigHost, this.config);
45076 logCompilerOptions(project, this.options);
45077 }
45078 });
45079 }
45080 }
45081 function logCompilerOptions(project, options) {
45082 const { logger } = project.projectService;
45083 const projectName = project.getProjectName();
45084 logger.info(`Angular compiler options for ${projectName}: ` + JSON.stringify(options, null, 2));
45085 }
45086 function parseNgCompilerOptions(project, host, config) {
45087 if (!(project instanceof ts__namespace.server.ConfiguredProject)) {
45088 return {};
45089 }
45090 const { options, errors } = readConfiguration(project.getConfigFilePath(), /* existingOptions */ undefined, host);
45091 if (errors.length > 0) {
45092 project.setProjectErrors(errors);
45093 }
45094 // Projects loaded into the Language Service often include test files which are not part of the
45095 // app's main compilation unit, and these test files often include inline NgModules that declare
45096 // components from the app. These declarations conflict with the main declarations of such
45097 // components in the app's NgModules. This conflict is not normally present during regular
45098 // compilation because the app and the tests are part of separate compilation units.
45099 //
45100 // As a temporary mitigation of this problem, we instruct the compiler to ignore classes which
45101 // are not exported. In many cases, this ensures the test NgModules are ignored by the compiler
45102 // and only the real component declaration is used.
45103 options.compileNonExportedClasses = false;
45104 // If `forceStrictTemplates` is true, always enable `strictTemplates`
45105 // regardless of its value in tsconfig.json.
45106 if (config.forceStrictTemplates === true) {
45107 options.strictTemplates = true;
45108 }
45109 return options;
45110 }
45111 function createProgramDriver(project) {
45112 return {
45113 supportsInlineOperations: false,
45114 getProgram() {
45115 const program = project.getLanguageService().getProgram();
45116 if (!program) {
45117 throw new Error('Language service does not have a program!');
45118 }
45119 return program;
45120 },
45121 updateFiles(contents) {
45122 for (const [fileName, { newText }] of contents) {
45123 const scriptInfo = getOrCreateTypeCheckScriptInfo(project, fileName);
45124 const snapshot = scriptInfo.getSnapshot();
45125 const length = snapshot.getLength();
45126 scriptInfo.editContent(0, length, newText);
45127 }
45128 },
45129 getSourceFileVersion(sf) {
45130 return project.getScriptVersion(sf.fileName);
45131 }
45132 };
45133 }
45134 function getOrCreateTypeCheckScriptInfo(project, tcf) {
45135 // First check if there is already a ScriptInfo for the tcf
45136 const { projectService } = project;
45137 let scriptInfo = projectService.getScriptInfo(tcf);
45138 if (!scriptInfo) {
45139 // ScriptInfo needs to be opened by client to be able to set its user-defined
45140 // content. We must also provide file content, otherwise the service will
45141 // attempt to fetch the content from disk and fail.
45142 scriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath(ts__namespace.server.toNormalizedPath(tcf), true, // openedByClient
45143 '', // fileContent
45144 // script info added by plugins should be marked as external, see
45145 // https://github.com/microsoft/TypeScript/blob/b217f22e798c781f55d17da72ed099a9dee5c650/src/compiler/program.ts#L1897-L1899
45146 ts__namespace.ScriptKind.External);
45147 if (!scriptInfo) {
45148 throw new Error(`Failed to create script info for ${tcf}`);
45149 }
45150 }
45151 // Add ScriptInfo to project if it's missing. A ScriptInfo needs to be part of
45152 // the project so that it becomes part of the program.
45153 if (!project.containsScriptInfo(scriptInfo)) {
45154 project.addRoot(scriptInfo);
45155 }
45156 return scriptInfo;
45157 }
45158 function isTemplateContext(program, fileName, position) {
45159 if (!isTypeScriptFile(fileName)) {
45160 // If we aren't in a TS file, we must be in an HTML file, which we treat as template context
45161 return true;
45162 }
45163 const node = findTightestNodeAtPosition(program, fileName, position);
45164 if (node === undefined) {
45165 return false;
45166 }
45167 let asgn = getPropertyAssignmentFromValue(node, 'template');
45168 if (asgn === null) {
45169 return false;
45170 }
45171 return getClassDeclFromDecoratorProp(asgn) !== null;
45172 }
45173 function isInAngularContext(program, fileName, position) {
45174 if (!isTypeScriptFile(fileName)) {
45175 return true;
45176 }
45177 const node = findTightestNodeAtPosition(program, fileName, position);
45178 if (node === undefined) {
45179 return false;
45180 }
45181 const asgn = getPropertyAssignmentFromValue(node, 'template') ??
45182 getPropertyAssignmentFromValue(node, 'templateUrl') ??
45183 getPropertyAssignmentFromValue(node.parent, 'styleUrls');
45184 return asgn !== null && getClassDeclFromDecoratorProp(asgn) !== null;
45185 }
45186 function findTightestNodeAtPosition(program, fileName, position) {
45187 const sourceFile = program.getSourceFile(fileName);
45188 if (sourceFile === undefined) {
45189 return undefined;
45190 }
45191 return findTightestNode(sourceFile, position);
45192 }
45193 function getUniqueLocations(locations) {
45194 const uniqueLocations = new Map();
45195 for (const location of locations) {
45196 uniqueLocations.set(createLocationKey(location), location);
45197 }
45198 return Array.from(uniqueLocations.values());
45199 }
45200
45201 /**
45202 * @license
45203 * Copyright Google LLC All Rights Reserved.
45204 *
45205 * Use of this source code is governed by an MIT-style license that can be
45206 * found in the LICENSE file at https://angular.io/license
45207 */
45208 function create(info) {
45209 const { project, languageService: tsLS, config } = info;
45210 const angularOnly = config?.angularOnly === true;
45211 const ngLS = new LanguageService(project, tsLS, config);
45212 function getSemanticDiagnostics(fileName) {
45213 const diagnostics = [];
45214 if (!angularOnly) {
45215 diagnostics.push(...tsLS.getSemanticDiagnostics(fileName));
45216 }
45217 diagnostics.push(...ngLS.getSemanticDiagnostics(fileName));
45218 return diagnostics;
45219 }
45220 function getQuickInfoAtPosition(fileName, position) {
45221 if (angularOnly) {
45222 return ngLS.getQuickInfoAtPosition(fileName, position);
45223 }
45224 else {
45225 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
45226 return tsLS.getQuickInfoAtPosition(fileName, position) ??
45227 ngLS.getQuickInfoAtPosition(fileName, position);
45228 }
45229 }
45230 function getTypeDefinitionAtPosition(fileName, position) {
45231 if (angularOnly) {
45232 return ngLS.getTypeDefinitionAtPosition(fileName, position);
45233 }
45234 else {
45235 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
45236 return tsLS.getTypeDefinitionAtPosition(fileName, position) ??
45237 ngLS.getTypeDefinitionAtPosition(fileName, position);
45238 }
45239 }
45240 function getDefinitionAndBoundSpan(fileName, position) {
45241 if (angularOnly) {
45242 return ngLS.getDefinitionAndBoundSpan(fileName, position);
45243 }
45244 else {
45245 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
45246 return tsLS.getDefinitionAndBoundSpan(fileName, position) ??
45247 ngLS.getDefinitionAndBoundSpan(fileName, position);
45248 }
45249 }
45250 function getReferencesAtPosition(fileName, position) {
45251 return ngLS.getReferencesAtPosition(fileName, position);
45252 }
45253 function findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename) {
45254 // Most operations combine results from all extensions. However, rename locations are exclusive
45255 // (results from only one extension are used) so our rename locations are a superset of the TS
45256 // rename locations. As a result, we do not check the `angularOnly` flag here because we always
45257 // want to include results at TS locations as well as locations in templates.
45258 return ngLS.findRenameLocations(fileName, position);
45259 }
45260 function getRenameInfo(fileName, position) {
45261 // See the comment in `findRenameLocations` explaining why we don't check the `angularOnly`
45262 // flag.
45263 return ngLS.getRenameInfo(fileName, position);
45264 }
45265 function getCompletionsAtPosition(fileName, position, options) {
45266 if (angularOnly) {
45267 return ngLS.getCompletionsAtPosition(fileName, position, options);
45268 }
45269 else {
45270 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
45271 return tsLS.getCompletionsAtPosition(fileName, position, options) ??
45272 ngLS.getCompletionsAtPosition(fileName, position, options);
45273 }
45274 }
45275 function getCompletionEntryDetails(fileName, position, entryName, formatOptions, source, preferences, data) {
45276 if (angularOnly) {
45277 return ngLS.getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences, data);
45278 }
45279 else {
45280 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
45281 return tsLS.getCompletionEntryDetails(fileName, position, entryName, formatOptions, source, preferences, data) ??
45282 ngLS.getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences, data);
45283 }
45284 }
45285 function getCompletionEntrySymbol(fileName, position, name, source) {
45286 if (angularOnly) {
45287 return ngLS.getCompletionEntrySymbol(fileName, position, name);
45288 }
45289 else {
45290 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
45291 return tsLS.getCompletionEntrySymbol(fileName, position, name, source) ??
45292 ngLS.getCompletionEntrySymbol(fileName, position, name);
45293 }
45294 }
45295 /**
45296 * Gets global diagnostics related to the program configuration and compiler options.
45297 */
45298 function getCompilerOptionsDiagnostics() {
45299 const diagnostics = [];
45300 if (!angularOnly) {
45301 diagnostics.push(...tsLS.getCompilerOptionsDiagnostics());
45302 }
45303 diagnostics.push(...ngLS.getCompilerOptionsDiagnostics());
45304 return diagnostics;
45305 }
45306 function getSignatureHelpItems(fileName, position, options) {
45307 if (angularOnly) {
45308 return ngLS.getSignatureHelpItems(fileName, position, options);
45309 }
45310 else {
45311 return tsLS.getSignatureHelpItems(fileName, position, options) ??
45312 ngLS.getSignatureHelpItems(fileName, position, options);
45313 }
45314 }
45315 function getTcb(fileName, position) {
45316 return ngLS.getTcb(fileName, position);
45317 }
45318 /**
45319 * Given an external template, finds the associated components that use it as a `templateUrl`.
45320 */
45321 function getComponentLocationsForTemplate(fileName) {
45322 return ngLS.getComponentLocationsForTemplate(fileName);
45323 }
45324 /**
45325 * Given a location inside a component, finds the location of the inline template or the file for
45326 * the `templateUrl`.
45327 */
45328 function getTemplateLocationForComponent(fileName, position) {
45329 return ngLS.getTemplateLocationForComponent(fileName, position);
45330 }
45331 return {
45332 ...tsLS,
45333 getSemanticDiagnostics,
45334 getTypeDefinitionAtPosition,
45335 getQuickInfoAtPosition,
45336 getDefinitionAndBoundSpan,
45337 getReferencesAtPosition,
45338 findRenameLocations,
45339 getRenameInfo,
45340 getCompletionsAtPosition,
45341 getCompletionEntryDetails,
45342 getCompletionEntrySymbol,
45343 getTcb,
45344 getCompilerOptionsDiagnostics,
45345 getComponentLocationsForTemplate,
45346 getSignatureHelpItems,
45347 getTemplateLocationForComponent,
45348 };
45349 }
45350 function getExternalFiles(project) {
45351 if (!project.hasRoots()) {
45352 return []; // project has not been initialized
45353 }
45354 const typecheckFiles = [];
45355 for (const scriptInfo of project.getScriptInfos()) {
45356 if (scriptInfo.scriptKind === ts__namespace.ScriptKind.External) {
45357 // script info for typecheck file is marked as external, see
45358 // getOrCreateTypeCheckScriptInfo() in
45359 // packages/language-service/src/language_service.ts
45360 typecheckFiles.push(scriptInfo.fileName);
45361 }
45362 }
45363 return typecheckFiles;
45364 }
45365
45366 exports.create = create;
45367 exports.getExternalFiles = getExternalFiles;
45368
45369 Object.defineProperty(exports, '__esModule', { value: true });
45370
45371}));
45372//# sourceMappingURL=language-service.js.map