UNPKG

1.92 MBJavaScriptView Raw
1/**
2 * @license Angular v11.2.0
3 * Copyright Google LLC All Rights Reserved.
4 * License: MIT
5 */
6
7let $deferred;
8function define(modules, callback) {
9 $deferred = {modules, callback};
10}
11module.exports = function(provided) {
12 const ts = provided['typescript'];
13 if (!ts) {
14 throw new Error('Caller does not provide typescript module');
15 }
16 const results = {};
17 const resolvedModules = $deferred.modules.map(m => {
18 if (m === 'exports') {
19 return results;
20 }
21 if (m === 'typescript' || m === 'typescript/lib/tsserverlibrary') {
22 return ts;
23 }
24 return require(m);
25 });
26 $deferred.callback(...resolvedModules);
27 return results;
28};
29
30define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', 'constants', 'stream', 'util', 'assert', 'path'], function (exports, ts, os, ts$1, fs$2, constants, stream, util, assert, path) { 'use strict';
31
32 var os__default = 'default' in os ? os['default'] : os;
33 var fs$2__default = 'default' in fs$2 ? fs$2['default'] : fs$2;
34 constants = constants && Object.prototype.hasOwnProperty.call(constants, 'default') ? constants['default'] : constants;
35 stream = stream && Object.prototype.hasOwnProperty.call(stream, 'default') ? stream['default'] : stream;
36 util = util && Object.prototype.hasOwnProperty.call(util, 'default') ? util['default'] : util;
37 assert = assert && Object.prototype.hasOwnProperty.call(assert, 'default') ? assert['default'] : assert;
38 var path__default = 'default' in path ? path['default'] : path;
39
40 /**
41 * @license
42 * Copyright Google LLC All Rights Reserved.
43 *
44 * Use of this source code is governed by an MIT-style license that can be
45 * found in the LICENSE file at https://angular.io/license
46 */
47 var TagContentType;
48 (function (TagContentType) {
49 TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
50 TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
51 TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
52 })(TagContentType || (TagContentType = {}));
53 function splitNsName(elementName) {
54 if (elementName[0] != ':') {
55 return [null, elementName];
56 }
57 const colonIndex = elementName.indexOf(':', 1);
58 if (colonIndex == -1) {
59 throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
60 }
61 return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
62 }
63 // `<ng-container>` tags work the same regardless the namespace
64 function isNgContainer(tagName) {
65 return splitNsName(tagName)[1] === 'ng-container';
66 }
67 // `<ng-content>` tags work the same regardless the namespace
68 function isNgContent(tagName) {
69 return splitNsName(tagName)[1] === 'ng-content';
70 }
71 // `<ng-template>` tags work the same regardless the namespace
72 function isNgTemplate(tagName) {
73 return splitNsName(tagName)[1] === 'ng-template';
74 }
75 function getNsPrefix(fullName) {
76 return fullName === null ? null : splitNsName(fullName)[0];
77 }
78 function mergeNsAndName(prefix, localName) {
79 return prefix ? `:${prefix}:${localName}` : localName;
80 }
81 // see https://www.w3.org/TR/html51/syntax.html#named-character-references
82 // see https://html.spec.whatwg.org/multipage/entities.json
83 // This list is not exhaustive to keep the compiler footprint low.
84 // The `&#123;` / `&#x1ab;` syntax should be used when the named character reference does not
85 // exist.
86 const NAMED_ENTITIES = {
87 'Aacute': '\u00C1',
88 'aacute': '\u00E1',
89 'Acirc': '\u00C2',
90 'acirc': '\u00E2',
91 'acute': '\u00B4',
92 'AElig': '\u00C6',
93 'aelig': '\u00E6',
94 'Agrave': '\u00C0',
95 'agrave': '\u00E0',
96 'alefsym': '\u2135',
97 'Alpha': '\u0391',
98 'alpha': '\u03B1',
99 'amp': '&',
100 'and': '\u2227',
101 'ang': '\u2220',
102 'apos': '\u0027',
103 'Aring': '\u00C5',
104 'aring': '\u00E5',
105 'asymp': '\u2248',
106 'Atilde': '\u00C3',
107 'atilde': '\u00E3',
108 'Auml': '\u00C4',
109 'auml': '\u00E4',
110 'bdquo': '\u201E',
111 'Beta': '\u0392',
112 'beta': '\u03B2',
113 'brvbar': '\u00A6',
114 'bull': '\u2022',
115 'cap': '\u2229',
116 'Ccedil': '\u00C7',
117 'ccedil': '\u00E7',
118 'cedil': '\u00B8',
119 'cent': '\u00A2',
120 'Chi': '\u03A7',
121 'chi': '\u03C7',
122 'circ': '\u02C6',
123 'clubs': '\u2663',
124 'cong': '\u2245',
125 'copy': '\u00A9',
126 'crarr': '\u21B5',
127 'cup': '\u222A',
128 'curren': '\u00A4',
129 'dagger': '\u2020',
130 'Dagger': '\u2021',
131 'darr': '\u2193',
132 'dArr': '\u21D3',
133 'deg': '\u00B0',
134 'Delta': '\u0394',
135 'delta': '\u03B4',
136 'diams': '\u2666',
137 'divide': '\u00F7',
138 'Eacute': '\u00C9',
139 'eacute': '\u00E9',
140 'Ecirc': '\u00CA',
141 'ecirc': '\u00EA',
142 'Egrave': '\u00C8',
143 'egrave': '\u00E8',
144 'empty': '\u2205',
145 'emsp': '\u2003',
146 'ensp': '\u2002',
147 'Epsilon': '\u0395',
148 'epsilon': '\u03B5',
149 'equiv': '\u2261',
150 'Eta': '\u0397',
151 'eta': '\u03B7',
152 'ETH': '\u00D0',
153 'eth': '\u00F0',
154 'Euml': '\u00CB',
155 'euml': '\u00EB',
156 'euro': '\u20AC',
157 'exist': '\u2203',
158 'fnof': '\u0192',
159 'forall': '\u2200',
160 'frac12': '\u00BD',
161 'frac14': '\u00BC',
162 'frac34': '\u00BE',
163 'frasl': '\u2044',
164 'Gamma': '\u0393',
165 'gamma': '\u03B3',
166 'ge': '\u2265',
167 'gt': '>',
168 'harr': '\u2194',
169 'hArr': '\u21D4',
170 'hearts': '\u2665',
171 'hellip': '\u2026',
172 'Iacute': '\u00CD',
173 'iacute': '\u00ED',
174 'Icirc': '\u00CE',
175 'icirc': '\u00EE',
176 'iexcl': '\u00A1',
177 'Igrave': '\u00CC',
178 'igrave': '\u00EC',
179 'image': '\u2111',
180 'infin': '\u221E',
181 'int': '\u222B',
182 'Iota': '\u0399',
183 'iota': '\u03B9',
184 'iquest': '\u00BF',
185 'isin': '\u2208',
186 'Iuml': '\u00CF',
187 'iuml': '\u00EF',
188 'Kappa': '\u039A',
189 'kappa': '\u03BA',
190 'Lambda': '\u039B',
191 'lambda': '\u03BB',
192 'lang': '\u27E8',
193 'laquo': '\u00AB',
194 'larr': '\u2190',
195 'lArr': '\u21D0',
196 'lceil': '\u2308',
197 'ldquo': '\u201C',
198 'le': '\u2264',
199 'lfloor': '\u230A',
200 'lowast': '\u2217',
201 'loz': '\u25CA',
202 'lrm': '\u200E',
203 'lsaquo': '\u2039',
204 'lsquo': '\u2018',
205 'lt': '<',
206 'macr': '\u00AF',
207 'mdash': '\u2014',
208 'micro': '\u00B5',
209 'middot': '\u00B7',
210 'minus': '\u2212',
211 'Mu': '\u039C',
212 'mu': '\u03BC',
213 'nabla': '\u2207',
214 'nbsp': '\u00A0',
215 'ndash': '\u2013',
216 'ne': '\u2260',
217 'ni': '\u220B',
218 'not': '\u00AC',
219 'notin': '\u2209',
220 'nsub': '\u2284',
221 'Ntilde': '\u00D1',
222 'ntilde': '\u00F1',
223 'Nu': '\u039D',
224 'nu': '\u03BD',
225 'Oacute': '\u00D3',
226 'oacute': '\u00F3',
227 'Ocirc': '\u00D4',
228 'ocirc': '\u00F4',
229 'OElig': '\u0152',
230 'oelig': '\u0153',
231 'Ograve': '\u00D2',
232 'ograve': '\u00F2',
233 'oline': '\u203E',
234 'Omega': '\u03A9',
235 'omega': '\u03C9',
236 'Omicron': '\u039F',
237 'omicron': '\u03BF',
238 'oplus': '\u2295',
239 'or': '\u2228',
240 'ordf': '\u00AA',
241 'ordm': '\u00BA',
242 'Oslash': '\u00D8',
243 'oslash': '\u00F8',
244 'Otilde': '\u00D5',
245 'otilde': '\u00F5',
246 'otimes': '\u2297',
247 'Ouml': '\u00D6',
248 'ouml': '\u00F6',
249 'para': '\u00B6',
250 'permil': '\u2030',
251 'perp': '\u22A5',
252 'Phi': '\u03A6',
253 'phi': '\u03C6',
254 'Pi': '\u03A0',
255 'pi': '\u03C0',
256 'piv': '\u03D6',
257 'plusmn': '\u00B1',
258 'pound': '\u00A3',
259 'prime': '\u2032',
260 'Prime': '\u2033',
261 'prod': '\u220F',
262 'prop': '\u221D',
263 'Psi': '\u03A8',
264 'psi': '\u03C8',
265 'quot': '\u0022',
266 'radic': '\u221A',
267 'rang': '\u27E9',
268 'raquo': '\u00BB',
269 'rarr': '\u2192',
270 'rArr': '\u21D2',
271 'rceil': '\u2309',
272 'rdquo': '\u201D',
273 'real': '\u211C',
274 'reg': '\u00AE',
275 'rfloor': '\u230B',
276 'Rho': '\u03A1',
277 'rho': '\u03C1',
278 'rlm': '\u200F',
279 'rsaquo': '\u203A',
280 'rsquo': '\u2019',
281 'sbquo': '\u201A',
282 'Scaron': '\u0160',
283 'scaron': '\u0161',
284 'sdot': '\u22C5',
285 'sect': '\u00A7',
286 'shy': '\u00AD',
287 'Sigma': '\u03A3',
288 'sigma': '\u03C3',
289 'sigmaf': '\u03C2',
290 'sim': '\u223C',
291 'spades': '\u2660',
292 'sub': '\u2282',
293 'sube': '\u2286',
294 'sum': '\u2211',
295 'sup': '\u2283',
296 'sup1': '\u00B9',
297 'sup2': '\u00B2',
298 'sup3': '\u00B3',
299 'supe': '\u2287',
300 'szlig': '\u00DF',
301 'Tau': '\u03A4',
302 'tau': '\u03C4',
303 'there4': '\u2234',
304 'Theta': '\u0398',
305 'theta': '\u03B8',
306 'thetasym': '\u03D1',
307 'thinsp': '\u2009',
308 'THORN': '\u00DE',
309 'thorn': '\u00FE',
310 'tilde': '\u02DC',
311 'times': '\u00D7',
312 'trade': '\u2122',
313 'Uacute': '\u00DA',
314 'uacute': '\u00FA',
315 'uarr': '\u2191',
316 'uArr': '\u21D1',
317 'Ucirc': '\u00DB',
318 'ucirc': '\u00FB',
319 'Ugrave': '\u00D9',
320 'ugrave': '\u00F9',
321 'uml': '\u00A8',
322 'upsih': '\u03D2',
323 'Upsilon': '\u03A5',
324 'upsilon': '\u03C5',
325 'Uuml': '\u00DC',
326 'uuml': '\u00FC',
327 'weierp': '\u2118',
328 'Xi': '\u039E',
329 'xi': '\u03BE',
330 'Yacute': '\u00DD',
331 'yacute': '\u00FD',
332 'yen': '\u00A5',
333 'yuml': '\u00FF',
334 'Yuml': '\u0178',
335 'Zeta': '\u0396',
336 'zeta': '\u03B6',
337 'zwj': '\u200D',
338 'zwnj': '\u200C',
339 };
340 // The &ngsp; pseudo-entity is denoting a space. see:
341 // https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart
342 const NGSP_UNICODE = '\uE500';
343 NAMED_ENTITIES['ngsp'] = NGSP_UNICODE;
344
345 /**
346 * @license
347 * Copyright Google LLC All Rights Reserved.
348 *
349 * Use of this source code is governed by an MIT-style license that can be
350 * found in the LICENSE file at https://angular.io/license
351 */
352 class HtmlTagDefinition {
353 constructor({ closedByChildren, implicitNamespacePrefix, contentType = TagContentType.PARSABLE_DATA, closedByParent = false, isVoid = false, ignoreFirstLf = false, preventNamespaceInheritance = false } = {}) {
354 this.closedByChildren = {};
355 this.closedByParent = false;
356 this.canSelfClose = false;
357 if (closedByChildren && closedByChildren.length > 0) {
358 closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true);
359 }
360 this.isVoid = isVoid;
361 this.closedByParent = closedByParent || isVoid;
362 this.implicitNamespacePrefix = implicitNamespacePrefix || null;
363 this.contentType = contentType;
364 this.ignoreFirstLf = ignoreFirstLf;
365 this.preventNamespaceInheritance = preventNamespaceInheritance;
366 }
367 isClosedByChild(name) {
368 return this.isVoid || name.toLowerCase() in this.closedByChildren;
369 }
370 getContentType(prefix) {
371 if (typeof this.contentType === 'object') {
372 const overrideType = prefix == null ? undefined : this.contentType[prefix];
373 return overrideType !== null && overrideType !== void 0 ? overrideType : this.contentType.default;
374 }
375 return this.contentType;
376 }
377 }
378 let _DEFAULT_TAG_DEFINITION;
379 // see https://www.w3.org/TR/html51/syntax.html#optional-tags
380 // This implementation does not fully conform to the HTML5 spec.
381 let TAG_DEFINITIONS;
382 function getHtmlTagDefinition(tagName) {
383 var _a, _b;
384 if (!TAG_DEFINITIONS) {
385 _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition();
386 TAG_DEFINITIONS = {
387 'base': new HtmlTagDefinition({ isVoid: true }),
388 'meta': new HtmlTagDefinition({ isVoid: true }),
389 'area': new HtmlTagDefinition({ isVoid: true }),
390 'embed': new HtmlTagDefinition({ isVoid: true }),
391 'link': new HtmlTagDefinition({ isVoid: true }),
392 'img': new HtmlTagDefinition({ isVoid: true }),
393 'input': new HtmlTagDefinition({ isVoid: true }),
394 'param': new HtmlTagDefinition({ isVoid: true }),
395 'hr': new HtmlTagDefinition({ isVoid: true }),
396 'br': new HtmlTagDefinition({ isVoid: true }),
397 'source': new HtmlTagDefinition({ isVoid: true }),
398 'track': new HtmlTagDefinition({ isVoid: true }),
399 'wbr': new HtmlTagDefinition({ isVoid: true }),
400 'p': new HtmlTagDefinition({
401 closedByChildren: [
402 'address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset',
403 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',
404 'h6', 'header', 'hgroup', 'hr', 'main', 'nav', 'ol',
405 'p', 'pre', 'section', 'table', 'ul'
406 ],
407 closedByParent: true
408 }),
409 'thead': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'] }),
410 'tbody': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'], closedByParent: true }),
411 'tfoot': new HtmlTagDefinition({ closedByChildren: ['tbody'], closedByParent: true }),
412 'tr': new HtmlTagDefinition({ closedByChildren: ['tr'], closedByParent: true }),
413 'td': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }),
414 'th': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }),
415 'col': new HtmlTagDefinition({ isVoid: true }),
416 'svg': new HtmlTagDefinition({ implicitNamespacePrefix: 'svg' }),
417 'foreignObject': new HtmlTagDefinition({
418 // Usually the implicit namespace here would be redundant since it will be inherited from
419 // the parent `svg`, but we have to do it for `foreignObject`, because the way the parser
420 // works is that the parent node of an end tag is its own start tag which means that
421 // the `preventNamespaceInheritance` on `foreignObject` would have it default to the
422 // implicit namespace which is `html`, unless specified otherwise.
423 implicitNamespacePrefix: 'svg',
424 // We want to prevent children of foreignObject from inheriting its namespace, because
425 // the point of the element is to allow nodes from other namespaces to be inserted.
426 preventNamespaceInheritance: true,
427 }),
428 'math': new HtmlTagDefinition({ implicitNamespacePrefix: 'math' }),
429 'li': new HtmlTagDefinition({ closedByChildren: ['li'], closedByParent: true }),
430 'dt': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'] }),
431 'dd': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'], closedByParent: true }),
432 'rb': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
433 'rt': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
434 'rtc': new HtmlTagDefinition({ closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true }),
435 'rp': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
436 'optgroup': new HtmlTagDefinition({ closedByChildren: ['optgroup'], closedByParent: true }),
437 'option': new HtmlTagDefinition({ closedByChildren: ['option', 'optgroup'], closedByParent: true }),
438 'pre': new HtmlTagDefinition({ ignoreFirstLf: true }),
439 'listing': new HtmlTagDefinition({ ignoreFirstLf: true }),
440 'style': new HtmlTagDefinition({ contentType: TagContentType.RAW_TEXT }),
441 'script': new HtmlTagDefinition({ contentType: TagContentType.RAW_TEXT }),
442 'title': new HtmlTagDefinition({
443 // The browser supports two separate `title` tags which have to use
444 // a different content type: `HTMLTitleElement` and `SVGTitleElement`
445 contentType: { default: TagContentType.ESCAPABLE_RAW_TEXT, svg: TagContentType.PARSABLE_DATA }
446 }),
447 'textarea': new HtmlTagDefinition({ contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true }),
448 };
449 }
450 // We have to make both a case-sensitive and a case-insesitive lookup, because
451 // HTML tag names are case insensitive, whereas some SVG tags are case sensitive.
452 return (_b = (_a = TAG_DEFINITIONS[tagName]) !== null && _a !== void 0 ? _a : TAG_DEFINITIONS[tagName.toLowerCase()]) !== null && _b !== void 0 ? _b : _DEFAULT_TAG_DEFINITION;
453 }
454
455 /**
456 * @license
457 * Copyright Google LLC All Rights Reserved.
458 *
459 * Use of this source code is governed by an MIT-style license that can be
460 * found in the LICENSE file at https://angular.io/license
461 */
462 const _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' + // 1: ":not("
463 '(([\\.\\#]?)[-\\w]+)|' + // 2: "tag"; 3: "."/"#";
464 // "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range
465 // 4: attribute; 5: attribute_string; 6: attribute_value
466 '(?:\\[([-.\\w*]+)(?:=([\"\']?)([^\\]\"\']*)\\5)?\\])|' + // "[name]", "[name=value]",
467 // "[name="value"]",
468 // "[name='value']"
469 '(\\))|' + // 7: ")"
470 '(\\s*,\\s*)', // 8: ","
471 'g');
472 /**
473 * A css selector contains an element name,
474 * css classes and attribute/value pairs with the purpose
475 * of selecting subsets out of them.
476 */
477 class CssSelector {
478 constructor() {
479 this.element = null;
480 this.classNames = [];
481 /**
482 * The selectors are encoded in pairs where:
483 * - even locations are attribute names
484 * - odd locations are attribute values.
485 *
486 * Example:
487 * Selector: `[key1=value1][key2]` would parse to:
488 * ```
489 * ['key1', 'value1', 'key2', '']
490 * ```
491 */
492 this.attrs = [];
493 this.notSelectors = [];
494 }
495 static parse(selector) {
496 const results = [];
497 const _addResult = (res, cssSel) => {
498 if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 &&
499 cssSel.attrs.length == 0) {
500 cssSel.element = '*';
501 }
502 res.push(cssSel);
503 };
504 let cssSelector = new CssSelector();
505 let match;
506 let current = cssSelector;
507 let inNot = false;
508 _SELECTOR_REGEXP.lastIndex = 0;
509 while (match = _SELECTOR_REGEXP.exec(selector)) {
510 if (match[1 /* NOT */]) {
511 if (inNot) {
512 throw new Error('Nesting :not in a selector is not allowed');
513 }
514 inNot = true;
515 current = new CssSelector();
516 cssSelector.notSelectors.push(current);
517 }
518 const tag = match[2 /* TAG */];
519 if (tag) {
520 const prefix = match[3 /* PREFIX */];
521 if (prefix === '#') {
522 // #hash
523 current.addAttribute('id', tag.substr(1));
524 }
525 else if (prefix === '.') {
526 // Class
527 current.addClassName(tag.substr(1));
528 }
529 else {
530 // Element
531 current.setElement(tag);
532 }
533 }
534 const attribute = match[4 /* ATTRIBUTE */];
535 if (attribute) {
536 current.addAttribute(attribute, match[6 /* ATTRIBUTE_VALUE */]);
537 }
538 if (match[7 /* NOT_END */]) {
539 inNot = false;
540 current = cssSelector;
541 }
542 if (match[8 /* SEPARATOR */]) {
543 if (inNot) {
544 throw new Error('Multiple selectors in :not are not supported');
545 }
546 _addResult(results, cssSelector);
547 cssSelector = current = new CssSelector();
548 }
549 }
550 _addResult(results, cssSelector);
551 return results;
552 }
553 isElementSelector() {
554 return this.hasElementSelector() && this.classNames.length == 0 && this.attrs.length == 0 &&
555 this.notSelectors.length === 0;
556 }
557 hasElementSelector() {
558 return !!this.element;
559 }
560 setElement(element = null) {
561 this.element = element;
562 }
563 /** Gets a template string for an element that matches the selector. */
564 getMatchingElementTemplate() {
565 const tagName = this.element || 'div';
566 const classAttr = this.classNames.length > 0 ? ` class="${this.classNames.join(' ')}"` : '';
567 let attrs = '';
568 for (let i = 0; i < this.attrs.length; i += 2) {
569 const attrName = this.attrs[i];
570 const attrValue = this.attrs[i + 1] !== '' ? `="${this.attrs[i + 1]}"` : '';
571 attrs += ` ${attrName}${attrValue}`;
572 }
573 return getHtmlTagDefinition(tagName).isVoid ? `<${tagName}${classAttr}${attrs}/>` :
574 `<${tagName}${classAttr}${attrs}></${tagName}>`;
575 }
576 getAttrs() {
577 const result = [];
578 if (this.classNames.length > 0) {
579 result.push('class', this.classNames.join(' '));
580 }
581 return result.concat(this.attrs);
582 }
583 addAttribute(name, value = '') {
584 this.attrs.push(name, value && value.toLowerCase() || '');
585 }
586 addClassName(name) {
587 this.classNames.push(name.toLowerCase());
588 }
589 toString() {
590 let res = this.element || '';
591 if (this.classNames) {
592 this.classNames.forEach(klass => res += `.${klass}`);
593 }
594 if (this.attrs) {
595 for (let i = 0; i < this.attrs.length; i += 2) {
596 const name = this.attrs[i];
597 const value = this.attrs[i + 1];
598 res += `[${name}${value ? '=' + value : ''}]`;
599 }
600 }
601 this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`);
602 return res;
603 }
604 }
605 /**
606 * Reads a list of CssSelectors and allows to calculate which ones
607 * are contained in a given CssSelector.
608 */
609 class SelectorMatcher {
610 constructor() {
611 this._elementMap = new Map();
612 this._elementPartialMap = new Map();
613 this._classMap = new Map();
614 this._classPartialMap = new Map();
615 this._attrValueMap = new Map();
616 this._attrValuePartialMap = new Map();
617 this._listContexts = [];
618 }
619 static createNotMatcher(notSelectors) {
620 const notMatcher = new SelectorMatcher();
621 notMatcher.addSelectables(notSelectors, null);
622 return notMatcher;
623 }
624 addSelectables(cssSelectors, callbackCtxt) {
625 let listContext = null;
626 if (cssSelectors.length > 1) {
627 listContext = new SelectorListContext(cssSelectors);
628 this._listContexts.push(listContext);
629 }
630 for (let i = 0; i < cssSelectors.length; i++) {
631 this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
632 }
633 }
634 /**
635 * Add an object that can be found later on by calling `match`.
636 * @param cssSelector A css selector
637 * @param callbackCtxt An opaque object that will be given to the callback of the `match` function
638 */
639 _addSelectable(cssSelector, callbackCtxt, listContext) {
640 let matcher = this;
641 const element = cssSelector.element;
642 const classNames = cssSelector.classNames;
643 const attrs = cssSelector.attrs;
644 const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
645 if (element) {
646 const isTerminal = attrs.length === 0 && classNames.length === 0;
647 if (isTerminal) {
648 this._addTerminal(matcher._elementMap, element, selectable);
649 }
650 else {
651 matcher = this._addPartial(matcher._elementPartialMap, element);
652 }
653 }
654 if (classNames) {
655 for (let i = 0; i < classNames.length; i++) {
656 const isTerminal = attrs.length === 0 && i === classNames.length - 1;
657 const className = classNames[i];
658 if (isTerminal) {
659 this._addTerminal(matcher._classMap, className, selectable);
660 }
661 else {
662 matcher = this._addPartial(matcher._classPartialMap, className);
663 }
664 }
665 }
666 if (attrs) {
667 for (let i = 0; i < attrs.length; i += 2) {
668 const isTerminal = i === attrs.length - 2;
669 const name = attrs[i];
670 const value = attrs[i + 1];
671 if (isTerminal) {
672 const terminalMap = matcher._attrValueMap;
673 let terminalValuesMap = terminalMap.get(name);
674 if (!terminalValuesMap) {
675 terminalValuesMap = new Map();
676 terminalMap.set(name, terminalValuesMap);
677 }
678 this._addTerminal(terminalValuesMap, value, selectable);
679 }
680 else {
681 const partialMap = matcher._attrValuePartialMap;
682 let partialValuesMap = partialMap.get(name);
683 if (!partialValuesMap) {
684 partialValuesMap = new Map();
685 partialMap.set(name, partialValuesMap);
686 }
687 matcher = this._addPartial(partialValuesMap, value);
688 }
689 }
690 }
691 }
692 _addTerminal(map, name, selectable) {
693 let terminalList = map.get(name);
694 if (!terminalList) {
695 terminalList = [];
696 map.set(name, terminalList);
697 }
698 terminalList.push(selectable);
699 }
700 _addPartial(map, name) {
701 let matcher = map.get(name);
702 if (!matcher) {
703 matcher = new SelectorMatcher();
704 map.set(name, matcher);
705 }
706 return matcher;
707 }
708 /**
709 * Find the objects that have been added via `addSelectable`
710 * whose css selector is contained in the given css selector.
711 * @param cssSelector A css selector
712 * @param matchedCallback This callback will be called with the object handed into `addSelectable`
713 * @return boolean true if a match was found
714 */
715 match(cssSelector, matchedCallback) {
716 let result = false;
717 const element = cssSelector.element;
718 const classNames = cssSelector.classNames;
719 const attrs = cssSelector.attrs;
720 for (let i = 0; i < this._listContexts.length; i++) {
721 this._listContexts[i].alreadyMatched = false;
722 }
723 result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
724 result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) ||
725 result;
726 if (classNames) {
727 for (let i = 0; i < classNames.length; i++) {
728 const className = classNames[i];
729 result =
730 this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
731 result =
732 this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) ||
733 result;
734 }
735 }
736 if (attrs) {
737 for (let i = 0; i < attrs.length; i += 2) {
738 const name = attrs[i];
739 const value = attrs[i + 1];
740 const terminalValuesMap = this._attrValueMap.get(name);
741 if (value) {
742 result =
743 this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
744 }
745 result =
746 this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
747 const partialValuesMap = this._attrValuePartialMap.get(name);
748 if (value) {
749 result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
750 }
751 result =
752 this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result;
753 }
754 }
755 return result;
756 }
757 /** @internal */
758 _matchTerminal(map, name, cssSelector, matchedCallback) {
759 if (!map || typeof name !== 'string') {
760 return false;
761 }
762 let selectables = map.get(name) || [];
763 const starSelectables = map.get('*');
764 if (starSelectables) {
765 selectables = selectables.concat(starSelectables);
766 }
767 if (selectables.length === 0) {
768 return false;
769 }
770 let selectable;
771 let result = false;
772 for (let i = 0; i < selectables.length; i++) {
773 selectable = selectables[i];
774 result = selectable.finalize(cssSelector, matchedCallback) || result;
775 }
776 return result;
777 }
778 /** @internal */
779 _matchPartial(map, name, cssSelector, matchedCallback) {
780 if (!map || typeof name !== 'string') {
781 return false;
782 }
783 const nestedSelector = map.get(name);
784 if (!nestedSelector) {
785 return false;
786 }
787 // TODO(perf): get rid of recursion and measure again
788 // TODO(perf): don't pass the whole selector into the recursion,
789 // but only the not processed parts
790 return nestedSelector.match(cssSelector, matchedCallback);
791 }
792 }
793 class SelectorListContext {
794 constructor(selectors) {
795 this.selectors = selectors;
796 this.alreadyMatched = false;
797 }
798 }
799 // Store context to pass back selector and context when a selector is matched
800 class SelectorContext {
801 constructor(selector, cbContext, listContext) {
802 this.selector = selector;
803 this.cbContext = cbContext;
804 this.listContext = listContext;
805 this.notSelectors = selector.notSelectors;
806 }
807 finalize(cssSelector, callback) {
808 let result = true;
809 if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
810 const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
811 result = !notMatcher.match(cssSelector, null);
812 }
813 if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) {
814 if (this.listContext) {
815 this.listContext.alreadyMatched = true;
816 }
817 callback(this.selector, this.cbContext);
818 }
819 return result;
820 }
821 }
822
823 /**
824 * @license
825 * Copyright Google LLC All Rights Reserved.
826 *
827 * Use of this source code is governed by an MIT-style license that can be
828 * found in the LICENSE file at https://angular.io/license
829 */
830 // Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not
831 // explicitly set. This value will be changed to `true` in v12.
832 // TODO(misko): switch the default in v12 to `true`. See: packages/core/src/metadata/di.ts
833 const emitDistinctChangesOnlyDefaultValue = false;
834 var ViewEncapsulation;
835 (function (ViewEncapsulation) {
836 ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
837 // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
838 ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
839 ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
840 })(ViewEncapsulation || (ViewEncapsulation = {}));
841 var ChangeDetectionStrategy;
842 (function (ChangeDetectionStrategy) {
843 ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
844 ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
845 })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
846 const CUSTOM_ELEMENTS_SCHEMA = {
847 name: 'custom-elements'
848 };
849 const NO_ERRORS_SCHEMA = {
850 name: 'no-errors-schema'
851 };
852 var SecurityContext;
853 (function (SecurityContext) {
854 SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
855 SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
856 SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
857 SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
858 SecurityContext[SecurityContext["URL"] = 4] = "URL";
859 SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
860 })(SecurityContext || (SecurityContext = {}));
861 var MissingTranslationStrategy;
862 (function (MissingTranslationStrategy) {
863 MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error";
864 MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning";
865 MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore";
866 })(MissingTranslationStrategy || (MissingTranslationStrategy = {}));
867 function parserSelectorToSimpleSelector(selector) {
868 const classes = selector.classNames && selector.classNames.length ?
869 [8 /* CLASS */, ...selector.classNames] :
870 [];
871 const elementName = selector.element && selector.element !== '*' ? selector.element : '';
872 return [elementName, ...selector.attrs, ...classes];
873 }
874 function parserSelectorToNegativeSelector(selector) {
875 const classes = selector.classNames && selector.classNames.length ?
876 [8 /* CLASS */, ...selector.classNames] :
877 [];
878 if (selector.element) {
879 return [
880 1 /* NOT */ | 4 /* ELEMENT */, selector.element, ...selector.attrs, ...classes
881 ];
882 }
883 else if (selector.attrs.length) {
884 return [1 /* NOT */ | 2 /* ATTRIBUTE */, ...selector.attrs, ...classes];
885 }
886 else {
887 return selector.classNames && selector.classNames.length ?
888 [1 /* NOT */ | 8 /* CLASS */, ...selector.classNames] :
889 [];
890 }
891 }
892 function parserSelectorToR3Selector(selector) {
893 const positive = parserSelectorToSimpleSelector(selector);
894 const negative = selector.notSelectors && selector.notSelectors.length ?
895 selector.notSelectors.map(notSelector => parserSelectorToNegativeSelector(notSelector)) :
896 [];
897 return positive.concat(...negative);
898 }
899 function parseSelectorToR3Selector(selector) {
900 return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : [];
901 }
902
903 /**
904 * @license
905 * Copyright Google LLC All Rights Reserved.
906 *
907 * Use of this source code is governed by an MIT-style license that can be
908 * found in the LICENSE file at https://angular.io/license
909 */
910 //// Types
911 var TypeModifier;
912 (function (TypeModifier) {
913 TypeModifier[TypeModifier["Const"] = 0] = "Const";
914 })(TypeModifier || (TypeModifier = {}));
915 class Type {
916 constructor(modifiers = []) {
917 this.modifiers = modifiers;
918 }
919 hasModifier(modifier) {
920 return this.modifiers.indexOf(modifier) !== -1;
921 }
922 }
923 var BuiltinTypeName;
924 (function (BuiltinTypeName) {
925 BuiltinTypeName[BuiltinTypeName["Dynamic"] = 0] = "Dynamic";
926 BuiltinTypeName[BuiltinTypeName["Bool"] = 1] = "Bool";
927 BuiltinTypeName[BuiltinTypeName["String"] = 2] = "String";
928 BuiltinTypeName[BuiltinTypeName["Int"] = 3] = "Int";
929 BuiltinTypeName[BuiltinTypeName["Number"] = 4] = "Number";
930 BuiltinTypeName[BuiltinTypeName["Function"] = 5] = "Function";
931 BuiltinTypeName[BuiltinTypeName["Inferred"] = 6] = "Inferred";
932 BuiltinTypeName[BuiltinTypeName["None"] = 7] = "None";
933 })(BuiltinTypeName || (BuiltinTypeName = {}));
934 class BuiltinType extends Type {
935 constructor(name, modifiers) {
936 super(modifiers);
937 this.name = name;
938 }
939 visitType(visitor, context) {
940 return visitor.visitBuiltinType(this, context);
941 }
942 }
943 class ExpressionType extends Type {
944 constructor(value, modifiers, typeParams = null) {
945 super(modifiers);
946 this.value = value;
947 this.typeParams = typeParams;
948 }
949 visitType(visitor, context) {
950 return visitor.visitExpressionType(this, context);
951 }
952 }
953 const DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
954 const INFERRED_TYPE = new BuiltinType(BuiltinTypeName.Inferred);
955 const BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
956 const INT_TYPE = new BuiltinType(BuiltinTypeName.Int);
957 const NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
958 const STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
959 const FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
960 const NONE_TYPE = new BuiltinType(BuiltinTypeName.None);
961 ///// Expressions
962 var UnaryOperator;
963 (function (UnaryOperator) {
964 UnaryOperator[UnaryOperator["Minus"] = 0] = "Minus";
965 UnaryOperator[UnaryOperator["Plus"] = 1] = "Plus";
966 })(UnaryOperator || (UnaryOperator = {}));
967 var BinaryOperator;
968 (function (BinaryOperator) {
969 BinaryOperator[BinaryOperator["Equals"] = 0] = "Equals";
970 BinaryOperator[BinaryOperator["NotEquals"] = 1] = "NotEquals";
971 BinaryOperator[BinaryOperator["Identical"] = 2] = "Identical";
972 BinaryOperator[BinaryOperator["NotIdentical"] = 3] = "NotIdentical";
973 BinaryOperator[BinaryOperator["Minus"] = 4] = "Minus";
974 BinaryOperator[BinaryOperator["Plus"] = 5] = "Plus";
975 BinaryOperator[BinaryOperator["Divide"] = 6] = "Divide";
976 BinaryOperator[BinaryOperator["Multiply"] = 7] = "Multiply";
977 BinaryOperator[BinaryOperator["Modulo"] = 8] = "Modulo";
978 BinaryOperator[BinaryOperator["And"] = 9] = "And";
979 BinaryOperator[BinaryOperator["Or"] = 10] = "Or";
980 BinaryOperator[BinaryOperator["BitwiseAnd"] = 11] = "BitwiseAnd";
981 BinaryOperator[BinaryOperator["Lower"] = 12] = "Lower";
982 BinaryOperator[BinaryOperator["LowerEquals"] = 13] = "LowerEquals";
983 BinaryOperator[BinaryOperator["Bigger"] = 14] = "Bigger";
984 BinaryOperator[BinaryOperator["BiggerEquals"] = 15] = "BiggerEquals";
985 })(BinaryOperator || (BinaryOperator = {}));
986 function nullSafeIsEquivalent(base, other) {
987 if (base == null || other == null) {
988 return base == other;
989 }
990 return base.isEquivalent(other);
991 }
992 function areAllEquivalentPredicate(base, other, equivalentPredicate) {
993 const len = base.length;
994 if (len !== other.length) {
995 return false;
996 }
997 for (let i = 0; i < len; i++) {
998 if (!equivalentPredicate(base[i], other[i])) {
999 return false;
1000 }
1001 }
1002 return true;
1003 }
1004 function areAllEquivalent(base, other) {
1005 return areAllEquivalentPredicate(base, other, (baseElement, otherElement) => baseElement.isEquivalent(otherElement));
1006 }
1007 class Expression {
1008 constructor(type, sourceSpan) {
1009 this.type = type || null;
1010 this.sourceSpan = sourceSpan || null;
1011 }
1012 prop(name, sourceSpan) {
1013 return new ReadPropExpr(this, name, null, sourceSpan);
1014 }
1015 key(index, type, sourceSpan) {
1016 return new ReadKeyExpr(this, index, type, sourceSpan);
1017 }
1018 callMethod(name, params, sourceSpan) {
1019 return new InvokeMethodExpr(this, name, params, null, sourceSpan);
1020 }
1021 callFn(params, sourceSpan, pure) {
1022 return new InvokeFunctionExpr(this, params, null, sourceSpan, pure);
1023 }
1024 instantiate(params, type, sourceSpan) {
1025 return new InstantiateExpr(this, params, type, sourceSpan);
1026 }
1027 conditional(trueCase, falseCase = null, sourceSpan) {
1028 return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan);
1029 }
1030 equals(rhs, sourceSpan) {
1031 return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs, null, sourceSpan);
1032 }
1033 notEquals(rhs, sourceSpan) {
1034 return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs, null, sourceSpan);
1035 }
1036 identical(rhs, sourceSpan) {
1037 return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs, null, sourceSpan);
1038 }
1039 notIdentical(rhs, sourceSpan) {
1040 return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs, null, sourceSpan);
1041 }
1042 minus(rhs, sourceSpan) {
1043 return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs, null, sourceSpan);
1044 }
1045 plus(rhs, sourceSpan) {
1046 return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs, null, sourceSpan);
1047 }
1048 divide(rhs, sourceSpan) {
1049 return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs, null, sourceSpan);
1050 }
1051 multiply(rhs, sourceSpan) {
1052 return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs, null, sourceSpan);
1053 }
1054 modulo(rhs, sourceSpan) {
1055 return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs, null, sourceSpan);
1056 }
1057 and(rhs, sourceSpan) {
1058 return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan);
1059 }
1060 bitwiseAnd(rhs, sourceSpan, parens = true) {
1061 return new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, this, rhs, null, sourceSpan, parens);
1062 }
1063 or(rhs, sourceSpan) {
1064 return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs, null, sourceSpan);
1065 }
1066 lower(rhs, sourceSpan) {
1067 return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs, null, sourceSpan);
1068 }
1069 lowerEquals(rhs, sourceSpan) {
1070 return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs, null, sourceSpan);
1071 }
1072 bigger(rhs, sourceSpan) {
1073 return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs, null, sourceSpan);
1074 }
1075 biggerEquals(rhs, sourceSpan) {
1076 return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs, null, sourceSpan);
1077 }
1078 isBlank(sourceSpan) {
1079 // Note: We use equals by purpose here to compare to null and undefined in JS.
1080 // We use the typed null to allow strictNullChecks to narrow types.
1081 return this.equals(TYPED_NULL_EXPR, sourceSpan);
1082 }
1083 cast(type, sourceSpan) {
1084 return new CastExpr(this, type, sourceSpan);
1085 }
1086 toStmt() {
1087 return new ExpressionStatement(this, null);
1088 }
1089 }
1090 var BuiltinVar;
1091 (function (BuiltinVar) {
1092 BuiltinVar[BuiltinVar["This"] = 0] = "This";
1093 BuiltinVar[BuiltinVar["Super"] = 1] = "Super";
1094 BuiltinVar[BuiltinVar["CatchError"] = 2] = "CatchError";
1095 BuiltinVar[BuiltinVar["CatchStack"] = 3] = "CatchStack";
1096 })(BuiltinVar || (BuiltinVar = {}));
1097 class ReadVarExpr extends Expression {
1098 constructor(name, type, sourceSpan) {
1099 super(type, sourceSpan);
1100 if (typeof name === 'string') {
1101 this.name = name;
1102 this.builtin = null;
1103 }
1104 else {
1105 this.name = null;
1106 this.builtin = name;
1107 }
1108 }
1109 isEquivalent(e) {
1110 return e instanceof ReadVarExpr && this.name === e.name && this.builtin === e.builtin;
1111 }
1112 isConstant() {
1113 return false;
1114 }
1115 visitExpression(visitor, context) {
1116 return visitor.visitReadVarExpr(this, context);
1117 }
1118 set(value) {
1119 if (!this.name) {
1120 throw new Error(`Built in variable ${this.builtin} can not be assigned to.`);
1121 }
1122 return new WriteVarExpr(this.name, value, null, this.sourceSpan);
1123 }
1124 }
1125 class TypeofExpr extends Expression {
1126 constructor(expr, type, sourceSpan) {
1127 super(type, sourceSpan);
1128 this.expr = expr;
1129 }
1130 visitExpression(visitor, context) {
1131 return visitor.visitTypeofExpr(this, context);
1132 }
1133 isEquivalent(e) {
1134 return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr);
1135 }
1136 isConstant() {
1137 return this.expr.isConstant();
1138 }
1139 }
1140 class WrappedNodeExpr extends Expression {
1141 constructor(node, type, sourceSpan) {
1142 super(type, sourceSpan);
1143 this.node = node;
1144 }
1145 isEquivalent(e) {
1146 return e instanceof WrappedNodeExpr && this.node === e.node;
1147 }
1148 isConstant() {
1149 return false;
1150 }
1151 visitExpression(visitor, context) {
1152 return visitor.visitWrappedNodeExpr(this, context);
1153 }
1154 }
1155 class WriteVarExpr extends Expression {
1156 constructor(name, value, type, sourceSpan) {
1157 super(type || value.type, sourceSpan);
1158 this.name = name;
1159 this.value = value;
1160 }
1161 isEquivalent(e) {
1162 return e instanceof WriteVarExpr && this.name === e.name && this.value.isEquivalent(e.value);
1163 }
1164 isConstant() {
1165 return false;
1166 }
1167 visitExpression(visitor, context) {
1168 return visitor.visitWriteVarExpr(this, context);
1169 }
1170 toDeclStmt(type, modifiers) {
1171 return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
1172 }
1173 toConstDecl() {
1174 return this.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]);
1175 }
1176 }
1177 class WriteKeyExpr extends Expression {
1178 constructor(receiver, index, value, type, sourceSpan) {
1179 super(type || value.type, sourceSpan);
1180 this.receiver = receiver;
1181 this.index = index;
1182 this.value = value;
1183 }
1184 isEquivalent(e) {
1185 return e instanceof WriteKeyExpr && this.receiver.isEquivalent(e.receiver) &&
1186 this.index.isEquivalent(e.index) && this.value.isEquivalent(e.value);
1187 }
1188 isConstant() {
1189 return false;
1190 }
1191 visitExpression(visitor, context) {
1192 return visitor.visitWriteKeyExpr(this, context);
1193 }
1194 }
1195 class WritePropExpr extends Expression {
1196 constructor(receiver, name, value, type, sourceSpan) {
1197 super(type || value.type, sourceSpan);
1198 this.receiver = receiver;
1199 this.name = name;
1200 this.value = value;
1201 }
1202 isEquivalent(e) {
1203 return e instanceof WritePropExpr && this.receiver.isEquivalent(e.receiver) &&
1204 this.name === e.name && this.value.isEquivalent(e.value);
1205 }
1206 isConstant() {
1207 return false;
1208 }
1209 visitExpression(visitor, context) {
1210 return visitor.visitWritePropExpr(this, context);
1211 }
1212 }
1213 var BuiltinMethod;
1214 (function (BuiltinMethod) {
1215 BuiltinMethod[BuiltinMethod["ConcatArray"] = 0] = "ConcatArray";
1216 BuiltinMethod[BuiltinMethod["SubscribeObservable"] = 1] = "SubscribeObservable";
1217 BuiltinMethod[BuiltinMethod["Bind"] = 2] = "Bind";
1218 })(BuiltinMethod || (BuiltinMethod = {}));
1219 class InvokeMethodExpr extends Expression {
1220 constructor(receiver, method, args, type, sourceSpan) {
1221 super(type, sourceSpan);
1222 this.receiver = receiver;
1223 this.args = args;
1224 if (typeof method === 'string') {
1225 this.name = method;
1226 this.builtin = null;
1227 }
1228 else {
1229 this.name = null;
1230 this.builtin = method;
1231 }
1232 }
1233 isEquivalent(e) {
1234 return e instanceof InvokeMethodExpr && this.receiver.isEquivalent(e.receiver) &&
1235 this.name === e.name && this.builtin === e.builtin && areAllEquivalent(this.args, e.args);
1236 }
1237 isConstant() {
1238 return false;
1239 }
1240 visitExpression(visitor, context) {
1241 return visitor.visitInvokeMethodExpr(this, context);
1242 }
1243 }
1244 class InvokeFunctionExpr extends Expression {
1245 constructor(fn, args, type, sourceSpan, pure = false) {
1246 super(type, sourceSpan);
1247 this.fn = fn;
1248 this.args = args;
1249 this.pure = pure;
1250 }
1251 isEquivalent(e) {
1252 return e instanceof InvokeFunctionExpr && this.fn.isEquivalent(e.fn) &&
1253 areAllEquivalent(this.args, e.args) && this.pure === e.pure;
1254 }
1255 isConstant() {
1256 return false;
1257 }
1258 visitExpression(visitor, context) {
1259 return visitor.visitInvokeFunctionExpr(this, context);
1260 }
1261 }
1262 class TaggedTemplateExpr extends Expression {
1263 constructor(tag, template, type, sourceSpan) {
1264 super(type, sourceSpan);
1265 this.tag = tag;
1266 this.template = template;
1267 }
1268 isEquivalent(e) {
1269 return e instanceof TaggedTemplateExpr && this.tag.isEquivalent(e.tag) &&
1270 areAllEquivalentPredicate(this.template.elements, e.template.elements, (a, b) => a.text === b.text) &&
1271 areAllEquivalent(this.template.expressions, e.template.expressions);
1272 }
1273 isConstant() {
1274 return false;
1275 }
1276 visitExpression(visitor, context) {
1277 return visitor.visitTaggedTemplateExpr(this, context);
1278 }
1279 }
1280 class InstantiateExpr extends Expression {
1281 constructor(classExpr, args, type, sourceSpan) {
1282 super(type, sourceSpan);
1283 this.classExpr = classExpr;
1284 this.args = args;
1285 }
1286 isEquivalent(e) {
1287 return e instanceof InstantiateExpr && this.classExpr.isEquivalent(e.classExpr) &&
1288 areAllEquivalent(this.args, e.args);
1289 }
1290 isConstant() {
1291 return false;
1292 }
1293 visitExpression(visitor, context) {
1294 return visitor.visitInstantiateExpr(this, context);
1295 }
1296 }
1297 class LiteralExpr extends Expression {
1298 constructor(value, type, sourceSpan) {
1299 super(type, sourceSpan);
1300 this.value = value;
1301 }
1302 isEquivalent(e) {
1303 return e instanceof LiteralExpr && this.value === e.value;
1304 }
1305 isConstant() {
1306 return true;
1307 }
1308 visitExpression(visitor, context) {
1309 return visitor.visitLiteralExpr(this, context);
1310 }
1311 }
1312 class TemplateLiteral {
1313 constructor(elements, expressions) {
1314 this.elements = elements;
1315 this.expressions = expressions;
1316 }
1317 }
1318 class TemplateLiteralElement {
1319 constructor(text, sourceSpan, rawText) {
1320 var _a;
1321 this.text = text;
1322 this.sourceSpan = sourceSpan;
1323 // If `rawText` is not provided, try to extract the raw string from its
1324 // associated `sourceSpan`. If that is also not available, "fake" the raw
1325 // string instead by escaping the following control sequences:
1326 // - "\" would otherwise indicate that the next character is a control character.
1327 // - "`" and "${" are template string control sequences that would otherwise prematurely
1328 // indicate the end of the template literal element.
1329 this.rawText = (_a = rawText !== null && rawText !== void 0 ? rawText : sourceSpan === null || sourceSpan === void 0 ? void 0 : sourceSpan.toString()) !== null && _a !== void 0 ? _a : escapeForTemplateLiteral(escapeSlashes(text));
1330 }
1331 }
1332 class MessagePiece {
1333 constructor(text, sourceSpan) {
1334 this.text = text;
1335 this.sourceSpan = sourceSpan;
1336 }
1337 }
1338 class LiteralPiece extends MessagePiece {
1339 }
1340 class PlaceholderPiece extends MessagePiece {
1341 }
1342 class LocalizedString extends Expression {
1343 constructor(metaBlock, messageParts, placeHolderNames, expressions, sourceSpan) {
1344 super(STRING_TYPE, sourceSpan);
1345 this.metaBlock = metaBlock;
1346 this.messageParts = messageParts;
1347 this.placeHolderNames = placeHolderNames;
1348 this.expressions = expressions;
1349 }
1350 isEquivalent(e) {
1351 // return e instanceof LocalizedString && this.message === e.message;
1352 return false;
1353 }
1354 isConstant() {
1355 return false;
1356 }
1357 visitExpression(visitor, context) {
1358 return visitor.visitLocalizedString(this, context);
1359 }
1360 /**
1361 * Serialize the given `meta` and `messagePart` into "cooked" and "raw" strings that can be used
1362 * in a `$localize` tagged string. The format of the metadata is the same as that parsed by
1363 * `parseI18nMeta()`.
1364 *
1365 * @param meta The metadata to serialize
1366 * @param messagePart The first part of the tagged string
1367 */
1368 serializeI18nHead() {
1369 const MEANING_SEPARATOR = '|';
1370 const ID_SEPARATOR = '@@';
1371 const LEGACY_ID_INDICATOR = '␟';
1372 let metaBlock = this.metaBlock.description || '';
1373 if (this.metaBlock.meaning) {
1374 metaBlock = `${this.metaBlock.meaning}${MEANING_SEPARATOR}${metaBlock}`;
1375 }
1376 if (this.metaBlock.customId) {
1377 metaBlock = `${metaBlock}${ID_SEPARATOR}${this.metaBlock.customId}`;
1378 }
1379 if (this.metaBlock.legacyIds) {
1380 this.metaBlock.legacyIds.forEach(legacyId => {
1381 metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`;
1382 });
1383 }
1384 return createCookedRawString(metaBlock, this.messageParts[0].text, this.getMessagePartSourceSpan(0));
1385 }
1386 getMessagePartSourceSpan(i) {
1387 var _a, _b;
1388 return (_b = (_a = this.messageParts[i]) === null || _a === void 0 ? void 0 : _a.sourceSpan) !== null && _b !== void 0 ? _b : this.sourceSpan;
1389 }
1390 getPlaceholderSourceSpan(i) {
1391 var _a, _b, _c, _d;
1392 return (_d = (_b = (_a = this.placeHolderNames[i]) === null || _a === void 0 ? void 0 : _a.sourceSpan) !== null && _b !== void 0 ? _b : (_c = this.expressions[i]) === null || _c === void 0 ? void 0 : _c.sourceSpan) !== null && _d !== void 0 ? _d : this.sourceSpan;
1393 }
1394 /**
1395 * Serialize the given `placeholderName` and `messagePart` into "cooked" and "raw" strings that
1396 * can be used in a `$localize` tagged string.
1397 *
1398 * @param placeholderName The placeholder name to serialize
1399 * @param messagePart The following message string after this placeholder
1400 */
1401 serializeI18nTemplatePart(partIndex) {
1402 const placeholderName = this.placeHolderNames[partIndex - 1].text;
1403 const messagePart = this.messageParts[partIndex];
1404 return createCookedRawString(placeholderName, messagePart.text, this.getMessagePartSourceSpan(partIndex));
1405 }
1406 }
1407 const escapeSlashes = (str) => str.replace(/\\/g, '\\\\');
1408 const escapeStartingColon = (str) => str.replace(/^:/, '\\:');
1409 const escapeColons = (str) => str.replace(/:/g, '\\:');
1410 const escapeForTemplateLiteral = (str) => str.replace(/`/g, '\\`').replace(/\${/g, '$\\{');
1411 /**
1412 * Creates a `{cooked, raw}` object from the `metaBlock` and `messagePart`.
1413 *
1414 * The `raw` text must have various character sequences escaped:
1415 * * "\" would otherwise indicate that the next character is a control character.
1416 * * "`" and "${" are template string control sequences that would otherwise prematurely indicate
1417 * the end of a message part.
1418 * * ":" inside a metablock would prematurely indicate the end of the metablock.
1419 * * ":" at the start of a messagePart with no metablock would erroneously indicate the start of a
1420 * metablock.
1421 *
1422 * @param metaBlock Any metadata that should be prepended to the string
1423 * @param messagePart The message part of the string
1424 */
1425 function createCookedRawString(metaBlock, messagePart, range) {
1426 if (metaBlock === '') {
1427 return {
1428 cooked: messagePart,
1429 raw: escapeForTemplateLiteral(escapeStartingColon(escapeSlashes(messagePart))),
1430 range,
1431 };
1432 }
1433 else {
1434 return {
1435 cooked: `:${metaBlock}:${messagePart}`,
1436 raw: escapeForTemplateLiteral(`:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`),
1437 range,
1438 };
1439 }
1440 }
1441 class ExternalExpr extends Expression {
1442 constructor(value, type, typeParams = null, sourceSpan) {
1443 super(type, sourceSpan);
1444 this.value = value;
1445 this.typeParams = typeParams;
1446 }
1447 isEquivalent(e) {
1448 return e instanceof ExternalExpr && this.value.name === e.value.name &&
1449 this.value.moduleName === e.value.moduleName && this.value.runtime === e.value.runtime;
1450 }
1451 isConstant() {
1452 return false;
1453 }
1454 visitExpression(visitor, context) {
1455 return visitor.visitExternalExpr(this, context);
1456 }
1457 }
1458 class ExternalReference {
1459 constructor(moduleName, name, runtime) {
1460 this.moduleName = moduleName;
1461 this.name = name;
1462 this.runtime = runtime;
1463 }
1464 }
1465 class ConditionalExpr extends Expression {
1466 constructor(condition, trueCase, falseCase = null, type, sourceSpan) {
1467 super(type || trueCase.type, sourceSpan);
1468 this.condition = condition;
1469 this.falseCase = falseCase;
1470 this.trueCase = trueCase;
1471 }
1472 isEquivalent(e) {
1473 return e instanceof ConditionalExpr && this.condition.isEquivalent(e.condition) &&
1474 this.trueCase.isEquivalent(e.trueCase) && nullSafeIsEquivalent(this.falseCase, e.falseCase);
1475 }
1476 isConstant() {
1477 return false;
1478 }
1479 visitExpression(visitor, context) {
1480 return visitor.visitConditionalExpr(this, context);
1481 }
1482 }
1483 class NotExpr extends Expression {
1484 constructor(condition, sourceSpan) {
1485 super(BOOL_TYPE, sourceSpan);
1486 this.condition = condition;
1487 }
1488 isEquivalent(e) {
1489 return e instanceof NotExpr && this.condition.isEquivalent(e.condition);
1490 }
1491 isConstant() {
1492 return false;
1493 }
1494 visitExpression(visitor, context) {
1495 return visitor.visitNotExpr(this, context);
1496 }
1497 }
1498 class AssertNotNull extends Expression {
1499 constructor(condition, sourceSpan) {
1500 super(condition.type, sourceSpan);
1501 this.condition = condition;
1502 }
1503 isEquivalent(e) {
1504 return e instanceof AssertNotNull && this.condition.isEquivalent(e.condition);
1505 }
1506 isConstant() {
1507 return false;
1508 }
1509 visitExpression(visitor, context) {
1510 return visitor.visitAssertNotNullExpr(this, context);
1511 }
1512 }
1513 class CastExpr extends Expression {
1514 constructor(value, type, sourceSpan) {
1515 super(type, sourceSpan);
1516 this.value = value;
1517 }
1518 isEquivalent(e) {
1519 return e instanceof CastExpr && this.value.isEquivalent(e.value);
1520 }
1521 isConstant() {
1522 return false;
1523 }
1524 visitExpression(visitor, context) {
1525 return visitor.visitCastExpr(this, context);
1526 }
1527 }
1528 class FnParam {
1529 constructor(name, type = null) {
1530 this.name = name;
1531 this.type = type;
1532 }
1533 isEquivalent(param) {
1534 return this.name === param.name;
1535 }
1536 }
1537 class FunctionExpr extends Expression {
1538 constructor(params, statements, type, sourceSpan, name) {
1539 super(type, sourceSpan);
1540 this.params = params;
1541 this.statements = statements;
1542 this.name = name;
1543 }
1544 isEquivalent(e) {
1545 return e instanceof FunctionExpr && areAllEquivalent(this.params, e.params) &&
1546 areAllEquivalent(this.statements, e.statements);
1547 }
1548 isConstant() {
1549 return false;
1550 }
1551 visitExpression(visitor, context) {
1552 return visitor.visitFunctionExpr(this, context);
1553 }
1554 toDeclStmt(name, modifiers) {
1555 return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
1556 }
1557 }
1558 class UnaryOperatorExpr extends Expression {
1559 constructor(operator, expr, type, sourceSpan, parens = true) {
1560 super(type || NUMBER_TYPE, sourceSpan);
1561 this.operator = operator;
1562 this.expr = expr;
1563 this.parens = parens;
1564 }
1565 isEquivalent(e) {
1566 return e instanceof UnaryOperatorExpr && this.operator === e.operator &&
1567 this.expr.isEquivalent(e.expr);
1568 }
1569 isConstant() {
1570 return false;
1571 }
1572 visitExpression(visitor, context) {
1573 return visitor.visitUnaryOperatorExpr(this, context);
1574 }
1575 }
1576 class BinaryOperatorExpr extends Expression {
1577 constructor(operator, lhs, rhs, type, sourceSpan, parens = true) {
1578 super(type || lhs.type, sourceSpan);
1579 this.operator = operator;
1580 this.rhs = rhs;
1581 this.parens = parens;
1582 this.lhs = lhs;
1583 }
1584 isEquivalent(e) {
1585 return e instanceof BinaryOperatorExpr && this.operator === e.operator &&
1586 this.lhs.isEquivalent(e.lhs) && this.rhs.isEquivalent(e.rhs);
1587 }
1588 isConstant() {
1589 return false;
1590 }
1591 visitExpression(visitor, context) {
1592 return visitor.visitBinaryOperatorExpr(this, context);
1593 }
1594 }
1595 class ReadPropExpr extends Expression {
1596 constructor(receiver, name, type, sourceSpan) {
1597 super(type, sourceSpan);
1598 this.receiver = receiver;
1599 this.name = name;
1600 }
1601 isEquivalent(e) {
1602 return e instanceof ReadPropExpr && this.receiver.isEquivalent(e.receiver) &&
1603 this.name === e.name;
1604 }
1605 isConstant() {
1606 return false;
1607 }
1608 visitExpression(visitor, context) {
1609 return visitor.visitReadPropExpr(this, context);
1610 }
1611 set(value) {
1612 return new WritePropExpr(this.receiver, this.name, value, null, this.sourceSpan);
1613 }
1614 }
1615 class ReadKeyExpr extends Expression {
1616 constructor(receiver, index, type, sourceSpan) {
1617 super(type, sourceSpan);
1618 this.receiver = receiver;
1619 this.index = index;
1620 }
1621 isEquivalent(e) {
1622 return e instanceof ReadKeyExpr && this.receiver.isEquivalent(e.receiver) &&
1623 this.index.isEquivalent(e.index);
1624 }
1625 isConstant() {
1626 return false;
1627 }
1628 visitExpression(visitor, context) {
1629 return visitor.visitReadKeyExpr(this, context);
1630 }
1631 set(value) {
1632 return new WriteKeyExpr(this.receiver, this.index, value, null, this.sourceSpan);
1633 }
1634 }
1635 class LiteralArrayExpr extends Expression {
1636 constructor(entries, type, sourceSpan) {
1637 super(type, sourceSpan);
1638 this.entries = entries;
1639 }
1640 isConstant() {
1641 return this.entries.every(e => e.isConstant());
1642 }
1643 isEquivalent(e) {
1644 return e instanceof LiteralArrayExpr && areAllEquivalent(this.entries, e.entries);
1645 }
1646 visitExpression(visitor, context) {
1647 return visitor.visitLiteralArrayExpr(this, context);
1648 }
1649 }
1650 class LiteralMapEntry {
1651 constructor(key, value, quoted) {
1652 this.key = key;
1653 this.value = value;
1654 this.quoted = quoted;
1655 }
1656 isEquivalent(e) {
1657 return this.key === e.key && this.value.isEquivalent(e.value);
1658 }
1659 }
1660 class LiteralMapExpr extends Expression {
1661 constructor(entries, type, sourceSpan) {
1662 super(type, sourceSpan);
1663 this.entries = entries;
1664 this.valueType = null;
1665 if (type) {
1666 this.valueType = type.valueType;
1667 }
1668 }
1669 isEquivalent(e) {
1670 return e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries);
1671 }
1672 isConstant() {
1673 return this.entries.every(e => e.value.isConstant());
1674 }
1675 visitExpression(visitor, context) {
1676 return visitor.visitLiteralMapExpr(this, context);
1677 }
1678 }
1679 const THIS_EXPR = new ReadVarExpr(BuiltinVar.This, null, null);
1680 const SUPER_EXPR = new ReadVarExpr(BuiltinVar.Super, null, null);
1681 const CATCH_ERROR_VAR = new ReadVarExpr(BuiltinVar.CatchError, null, null);
1682 const CATCH_STACK_VAR = new ReadVarExpr(BuiltinVar.CatchStack, null, null);
1683 const NULL_EXPR = new LiteralExpr(null, null, null);
1684 const TYPED_NULL_EXPR = new LiteralExpr(null, INFERRED_TYPE, null);
1685 //// Statements
1686 var StmtModifier;
1687 (function (StmtModifier) {
1688 StmtModifier[StmtModifier["Final"] = 0] = "Final";
1689 StmtModifier[StmtModifier["Private"] = 1] = "Private";
1690 StmtModifier[StmtModifier["Exported"] = 2] = "Exported";
1691 StmtModifier[StmtModifier["Static"] = 3] = "Static";
1692 })(StmtModifier || (StmtModifier = {}));
1693 class LeadingComment {
1694 constructor(text, multiline, trailingNewline) {
1695 this.text = text;
1696 this.multiline = multiline;
1697 this.trailingNewline = trailingNewline;
1698 }
1699 toString() {
1700 return this.multiline ? ` ${this.text} ` : this.text;
1701 }
1702 }
1703 class JSDocComment extends LeadingComment {
1704 constructor(tags) {
1705 super('', /* multiline */ true, /* trailingNewline */ true);
1706 this.tags = tags;
1707 }
1708 toString() {
1709 return serializeTags(this.tags);
1710 }
1711 }
1712 class Statement {
1713 constructor(modifiers = [], sourceSpan = null, leadingComments) {
1714 this.modifiers = modifiers;
1715 this.sourceSpan = sourceSpan;
1716 this.leadingComments = leadingComments;
1717 }
1718 hasModifier(modifier) {
1719 return this.modifiers.indexOf(modifier) !== -1;
1720 }
1721 addLeadingComment(leadingComment) {
1722 var _a;
1723 this.leadingComments = (_a = this.leadingComments) !== null && _a !== void 0 ? _a : [];
1724 this.leadingComments.push(leadingComment);
1725 }
1726 }
1727 class DeclareVarStmt extends Statement {
1728 constructor(name, value, type, modifiers, sourceSpan, leadingComments) {
1729 super(modifiers, sourceSpan, leadingComments);
1730 this.name = name;
1731 this.value = value;
1732 this.type = type || (value && value.type) || null;
1733 }
1734 isEquivalent(stmt) {
1735 return stmt instanceof DeclareVarStmt && this.name === stmt.name &&
1736 (this.value ? !!stmt.value && this.value.isEquivalent(stmt.value) : !stmt.value);
1737 }
1738 visitStatement(visitor, context) {
1739 return visitor.visitDeclareVarStmt(this, context);
1740 }
1741 }
1742 class DeclareFunctionStmt extends Statement {
1743 constructor(name, params, statements, type, modifiers, sourceSpan, leadingComments) {
1744 super(modifiers, sourceSpan, leadingComments);
1745 this.name = name;
1746 this.params = params;
1747 this.statements = statements;
1748 this.type = type || null;
1749 }
1750 isEquivalent(stmt) {
1751 return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) &&
1752 areAllEquivalent(this.statements, stmt.statements);
1753 }
1754 visitStatement(visitor, context) {
1755 return visitor.visitDeclareFunctionStmt(this, context);
1756 }
1757 }
1758 class ExpressionStatement extends Statement {
1759 constructor(expr, sourceSpan, leadingComments) {
1760 super([], sourceSpan, leadingComments);
1761 this.expr = expr;
1762 }
1763 isEquivalent(stmt) {
1764 return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr);
1765 }
1766 visitStatement(visitor, context) {
1767 return visitor.visitExpressionStmt(this, context);
1768 }
1769 }
1770 class ReturnStatement extends Statement {
1771 constructor(value, sourceSpan = null, leadingComments) {
1772 super([], sourceSpan, leadingComments);
1773 this.value = value;
1774 }
1775 isEquivalent(stmt) {
1776 return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value);
1777 }
1778 visitStatement(visitor, context) {
1779 return visitor.visitReturnStmt(this, context);
1780 }
1781 }
1782 class IfStmt extends Statement {
1783 constructor(condition, trueCase, falseCase = [], sourceSpan, leadingComments) {
1784 super([], sourceSpan, leadingComments);
1785 this.condition = condition;
1786 this.trueCase = trueCase;
1787 this.falseCase = falseCase;
1788 }
1789 isEquivalent(stmt) {
1790 return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) &&
1791 areAllEquivalent(this.trueCase, stmt.trueCase) &&
1792 areAllEquivalent(this.falseCase, stmt.falseCase);
1793 }
1794 visitStatement(visitor, context) {
1795 return visitor.visitIfStmt(this, context);
1796 }
1797 }
1798 function jsDocComment(tags = []) {
1799 return new JSDocComment(tags);
1800 }
1801 function variable(name, type, sourceSpan) {
1802 return new ReadVarExpr(name, type, sourceSpan);
1803 }
1804 function importExpr(id, typeParams = null, sourceSpan) {
1805 return new ExternalExpr(id, null, typeParams, sourceSpan);
1806 }
1807 function expressionType(expr, typeModifiers, typeParams) {
1808 return new ExpressionType(expr, typeModifiers, typeParams);
1809 }
1810 function typeofExpr(expr) {
1811 return new TypeofExpr(expr);
1812 }
1813 function literalArr(values, type, sourceSpan) {
1814 return new LiteralArrayExpr(values, type, sourceSpan);
1815 }
1816 function literalMap(values, type = null) {
1817 return new LiteralMapExpr(values.map(e => new LiteralMapEntry(e.key, e.value, e.quoted)), type, null);
1818 }
1819 function not(expr, sourceSpan) {
1820 return new NotExpr(expr, sourceSpan);
1821 }
1822 function assertNotNull(expr, sourceSpan) {
1823 return new AssertNotNull(expr, sourceSpan);
1824 }
1825 function fn(params, body, type, sourceSpan, name) {
1826 return new FunctionExpr(params, body, type, sourceSpan, name);
1827 }
1828 function ifStmt(condition, thenClause, elseClause, sourceSpan, leadingComments) {
1829 return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments);
1830 }
1831 function taggedTemplate(tag, template, type, sourceSpan) {
1832 return new TaggedTemplateExpr(tag, template, type, sourceSpan);
1833 }
1834 function literal(value, type, sourceSpan) {
1835 return new LiteralExpr(value, type, sourceSpan);
1836 }
1837 function localizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan) {
1838 return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan);
1839 }
1840 function isNull(exp) {
1841 return exp instanceof LiteralExpr && exp.value === null;
1842 }
1843 /*
1844 * Serializes a `Tag` into a string.
1845 * Returns a string like " @foo {bar} baz" (note the leading whitespace before `@foo`).
1846 */
1847 function tagToString(tag) {
1848 let out = '';
1849 if (tag.tagName) {
1850 out += ` @${tag.tagName}`;
1851 }
1852 if (tag.text) {
1853 if (tag.text.match(/\/\*|\*\//)) {
1854 throw new Error('JSDoc text cannot contain "/*" and "*/"');
1855 }
1856 out += ' ' + tag.text.replace(/@/g, '\\@');
1857 }
1858 return out;
1859 }
1860 function serializeTags(tags) {
1861 if (tags.length === 0)
1862 return '';
1863 if (tags.length === 1 && tags[0].tagName && !tags[0].text) {
1864 // The JSDOC comment is a single simple tag: e.g `/** @tagname */`.
1865 return `*${tagToString(tags[0])} `;
1866 }
1867 let out = '*\n';
1868 for (const tag of tags) {
1869 out += ' *';
1870 // If the tagToString is multi-line, insert " * " prefixes on lines.
1871 out += tagToString(tag).replace(/\n/g, '\n * ');
1872 out += '\n';
1873 }
1874 out += ' ';
1875 return out;
1876 }
1877
1878 /**
1879 * @license
1880 * Copyright Google LLC All Rights Reserved.
1881 *
1882 * Use of this source code is governed by an MIT-style license that can be
1883 * found in the LICENSE file at https://angular.io/license
1884 */
1885 const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
1886 function dashCaseToCamelCase(input) {
1887 return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase());
1888 }
1889 function splitAtColon(input, defaultValues) {
1890 return _splitAt(input, ':', defaultValues);
1891 }
1892 function splitAtPeriod(input, defaultValues) {
1893 return _splitAt(input, '.', defaultValues);
1894 }
1895 function _splitAt(input, character, defaultValues) {
1896 const characterIndex = input.indexOf(character);
1897 if (characterIndex == -1)
1898 return defaultValues;
1899 return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
1900 }
1901 function error(msg) {
1902 throw new Error(`Internal Error: ${msg}`);
1903 }
1904 function utf8Encode(str) {
1905 let encoded = [];
1906 for (let index = 0; index < str.length; index++) {
1907 let codePoint = str.charCodeAt(index);
1908 // decode surrogate
1909 // see https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
1910 if (codePoint >= 0xd800 && codePoint <= 0xdbff && str.length > (index + 1)) {
1911 const low = str.charCodeAt(index + 1);
1912 if (low >= 0xdc00 && low <= 0xdfff) {
1913 index++;
1914 codePoint = ((codePoint - 0xd800) << 10) + low - 0xdc00 + 0x10000;
1915 }
1916 }
1917 if (codePoint <= 0x7f) {
1918 encoded.push(codePoint);
1919 }
1920 else if (codePoint <= 0x7ff) {
1921 encoded.push(((codePoint >> 6) & 0x1F) | 0xc0, (codePoint & 0x3f) | 0x80);
1922 }
1923 else if (codePoint <= 0xffff) {
1924 encoded.push((codePoint >> 12) | 0xe0, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
1925 }
1926 else if (codePoint <= 0x1fffff) {
1927 encoded.push(((codePoint >> 18) & 0x07) | 0xf0, ((codePoint >> 12) & 0x3f) | 0x80, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
1928 }
1929 }
1930 return encoded;
1931 }
1932 function stringify(token) {
1933 if (typeof token === 'string') {
1934 return token;
1935 }
1936 if (Array.isArray(token)) {
1937 return '[' + token.map(stringify).join(', ') + ']';
1938 }
1939 if (token == null) {
1940 return '' + token;
1941 }
1942 if (token.overriddenName) {
1943 return `${token.overriddenName}`;
1944 }
1945 if (token.name) {
1946 return `${token.name}`;
1947 }
1948 if (!token.toString) {
1949 return 'object';
1950 }
1951 // WARNING: do not try to `JSON.stringify(token)` here
1952 // see https://github.com/angular/angular/issues/23440
1953 const res = token.toString();
1954 if (res == null) {
1955 return '' + res;
1956 }
1957 const newLineIndex = res.indexOf('\n');
1958 return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
1959 }
1960 class Version {
1961 constructor(full) {
1962 this.full = full;
1963 const splits = full.split('.');
1964 this.major = splits[0];
1965 this.minor = splits[1];
1966 this.patch = splits.slice(2).join('.');
1967 }
1968 }
1969 const __window = typeof window !== 'undefined' && window;
1970 const __self = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
1971 self instanceof WorkerGlobalScope && self;
1972 const __global = typeof global !== 'undefined' && global;
1973 // Check __global first, because in Node tests both __global and __window may be defined and _global
1974 // should be __global in that case.
1975 const _global = __global || __window || __self;
1976 function newArray(size, value) {
1977 const list = [];
1978 for (let i = 0; i < size; i++) {
1979 list.push(value);
1980 }
1981 return list;
1982 }
1983 /**
1984 * Partitions a given array into 2 arrays, based on a boolean value returned by the condition
1985 * function.
1986 *
1987 * @param arr Input array that should be partitioned
1988 * @param conditionFn Condition function that is called for each item in a given array and returns a
1989 * boolean value.
1990 */
1991 function partitionArray(arr, conditionFn) {
1992 const truthy = [];
1993 const falsy = [];
1994 for (const item of arr) {
1995 (conditionFn(item) ? truthy : falsy).push(item);
1996 }
1997 return [truthy, falsy];
1998 }
1999
2000 /**
2001 * @license
2002 * Copyright Google LLC All Rights Reserved.
2003 *
2004 * Use of this source code is governed by an MIT-style license that can be
2005 * found in the LICENSE file at https://angular.io/license
2006 */
2007 const CONSTANT_PREFIX = '_c';
2008 /**
2009 * `ConstantPool` tries to reuse literal factories when two or more literals are identical.
2010 * We determine whether literals are identical by creating a key out of their AST using the
2011 * `KeyVisitor`. This constant is used to replace dynamic expressions which can't be safely
2012 * converted into a key. E.g. given an expression `{foo: bar()}`, since we don't know what
2013 * the result of `bar` will be, we create a key that looks like `{foo: <unknown>}`. Note
2014 * that we use a variable, rather than something like `null` in order to avoid collisions.
2015 */
2016 const UNKNOWN_VALUE_KEY = variable('<unknown>');
2017 /**
2018 * Context to use when producing a key.
2019 *
2020 * This ensures we see the constant not the reference variable when producing
2021 * a key.
2022 */
2023 const KEY_CONTEXT = {};
2024 /**
2025 * Generally all primitive values are excluded from the `ConstantPool`, but there is an exclusion
2026 * for strings that reach a certain length threshold. This constant defines the length threshold for
2027 * strings.
2028 */
2029 const POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS = 50;
2030 /**
2031 * A node that is a place-holder that allows the node to be replaced when the actual
2032 * node is known.
2033 *
2034 * This allows the constant pool to change an expression from a direct reference to
2035 * a constant to a shared constant. It returns a fix-up node that is later allowed to
2036 * change the referenced expression.
2037 */
2038 class FixupExpression extends Expression {
2039 constructor(resolved) {
2040 super(resolved.type);
2041 this.resolved = resolved;
2042 this.original = resolved;
2043 }
2044 visitExpression(visitor, context) {
2045 if (context === KEY_CONTEXT) {
2046 // When producing a key we want to traverse the constant not the
2047 // variable used to refer to it.
2048 return this.original.visitExpression(visitor, context);
2049 }
2050 else {
2051 return this.resolved.visitExpression(visitor, context);
2052 }
2053 }
2054 isEquivalent(e) {
2055 return e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved);
2056 }
2057 isConstant() {
2058 return true;
2059 }
2060 fixup(expression) {
2061 this.resolved = expression;
2062 this.shared = true;
2063 }
2064 }
2065 /**
2066 * A constant pool allows a code emitter to share constant in an output context.
2067 *
2068 * The constant pool also supports sharing access to ivy definitions references.
2069 */
2070 class ConstantPool {
2071 constructor(isClosureCompilerEnabled = false) {
2072 this.isClosureCompilerEnabled = isClosureCompilerEnabled;
2073 this.statements = [];
2074 this.literals = new Map();
2075 this.literalFactories = new Map();
2076 this.injectorDefinitions = new Map();
2077 this.directiveDefinitions = new Map();
2078 this.componentDefinitions = new Map();
2079 this.pipeDefinitions = new Map();
2080 this.nextNameIndex = 0;
2081 }
2082 getConstLiteral(literal, forceShared) {
2083 if ((literal instanceof LiteralExpr && !isLongStringLiteral(literal)) ||
2084 literal instanceof FixupExpression) {
2085 // Do no put simple literals into the constant pool or try to produce a constant for a
2086 // reference to a constant.
2087 return literal;
2088 }
2089 const key = this.keyOf(literal);
2090 let fixup = this.literals.get(key);
2091 let newValue = false;
2092 if (!fixup) {
2093 fixup = new FixupExpression(literal);
2094 this.literals.set(key, fixup);
2095 newValue = true;
2096 }
2097 if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
2098 // Replace the expression with a variable
2099 const name = this.freshName();
2100 let definition;
2101 let usage;
2102 if (this.isClosureCompilerEnabled && isLongStringLiteral(literal)) {
2103 // For string literals, Closure will **always** inline the string at
2104 // **all** usages, duplicating it each time. For large strings, this
2105 // unnecessarily bloats bundle size. To work around this restriction, we
2106 // wrap the string in a function, and call that function for each usage.
2107 // This tricks Closure into using inline logic for functions instead of
2108 // string literals. Function calls are only inlined if the body is small
2109 // enough to be worth it. By doing this, very large strings will be
2110 // shared across multiple usages, rather than duplicating the string at
2111 // each usage site.
2112 //
2113 // const myStr = function() { return "very very very long string"; };
2114 // const usage1 = myStr();
2115 // const usage2 = myStr();
2116 definition = variable(name).set(new FunctionExpr([], // Params.
2117 [
2118 // Statements.
2119 new ReturnStatement(literal),
2120 ]));
2121 usage = variable(name).callFn([]);
2122 }
2123 else {
2124 // Just declare and use the variable directly, without a function call
2125 // indirection. This saves a few bytes and avoids an unncessary call.
2126 definition = variable(name).set(literal);
2127 usage = variable(name);
2128 }
2129 this.statements.push(definition.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]));
2130 fixup.fixup(usage);
2131 }
2132 return fixup;
2133 }
2134 getDefinition(type, kind, ctx, forceShared = false) {
2135 const definitions = this.definitionsOf(kind);
2136 let fixup = definitions.get(type);
2137 let newValue = false;
2138 if (!fixup) {
2139 const property = this.propertyNameOf(kind);
2140 fixup = new FixupExpression(ctx.importExpr(type).prop(property));
2141 definitions.set(type, fixup);
2142 newValue = true;
2143 }
2144 if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
2145 const name = this.freshName();
2146 this.statements.push(variable(name).set(fixup.resolved).toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]));
2147 fixup.fixup(variable(name));
2148 }
2149 return fixup;
2150 }
2151 getLiteralFactory(literal) {
2152 // Create a pure function that builds an array of a mix of constant and variable expressions
2153 if (literal instanceof LiteralArrayExpr) {
2154 const argumentsForKey = literal.entries.map(e => e.isConstant() ? e : UNKNOWN_VALUE_KEY);
2155 const key = this.keyOf(literalArr(argumentsForKey));
2156 return this._getLiteralFactory(key, literal.entries, entries => literalArr(entries));
2157 }
2158 else {
2159 const expressionForKey = literalMap(literal.entries.map(e => ({
2160 key: e.key,
2161 value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY,
2162 quoted: e.quoted
2163 })));
2164 const key = this.keyOf(expressionForKey);
2165 return this._getLiteralFactory(key, literal.entries.map(e => e.value), entries => literalMap(entries.map((value, index) => ({
2166 key: literal.entries[index].key,
2167 value,
2168 quoted: literal.entries[index].quoted
2169 }))));
2170 }
2171 }
2172 _getLiteralFactory(key, values, resultMap) {
2173 let literalFactory = this.literalFactories.get(key);
2174 const literalFactoryArguments = values.filter((e => !e.isConstant()));
2175 if (!literalFactory) {
2176 const resultExpressions = values.map((e, index) => e.isConstant() ? this.getConstLiteral(e, true) : variable(`a${index}`));
2177 const parameters = resultExpressions.filter(isVariable).map(e => new FnParam(e.name, DYNAMIC_TYPE));
2178 const pureFunctionDeclaration = fn(parameters, [new ReturnStatement(resultMap(resultExpressions))], INFERRED_TYPE);
2179 const name = this.freshName();
2180 this.statements.push(variable(name).set(pureFunctionDeclaration).toDeclStmt(INFERRED_TYPE, [
2181 StmtModifier.Final
2182 ]));
2183 literalFactory = variable(name);
2184 this.literalFactories.set(key, literalFactory);
2185 }
2186 return { literalFactory, literalFactoryArguments };
2187 }
2188 /**
2189 * Produce a unique name.
2190 *
2191 * The name might be unique among different prefixes if any of the prefixes end in
2192 * a digit so the prefix should be a constant string (not based on user input) and
2193 * must not end in a digit.
2194 */
2195 uniqueName(prefix) {
2196 return `${prefix}${this.nextNameIndex++}`;
2197 }
2198 definitionsOf(kind) {
2199 switch (kind) {
2200 case 2 /* Component */:
2201 return this.componentDefinitions;
2202 case 1 /* Directive */:
2203 return this.directiveDefinitions;
2204 case 0 /* Injector */:
2205 return this.injectorDefinitions;
2206 case 3 /* Pipe */:
2207 return this.pipeDefinitions;
2208 }
2209 error(`Unknown definition kind ${kind}`);
2210 return this.componentDefinitions;
2211 }
2212 propertyNameOf(kind) {
2213 switch (kind) {
2214 case 2 /* Component */:
2215 return 'ɵcmp';
2216 case 1 /* Directive */:
2217 return 'ɵdir';
2218 case 0 /* Injector */:
2219 return 'ɵinj';
2220 case 3 /* Pipe */:
2221 return 'ɵpipe';
2222 }
2223 error(`Unknown definition kind ${kind}`);
2224 return '<unknown>';
2225 }
2226 freshName() {
2227 return this.uniqueName(CONSTANT_PREFIX);
2228 }
2229 keyOf(expression) {
2230 return expression.visitExpression(new KeyVisitor(), KEY_CONTEXT);
2231 }
2232 }
2233 /**
2234 * Visitor used to determine if 2 expressions are equivalent and can be shared in the
2235 * `ConstantPool`.
2236 *
2237 * When the id (string) generated by the visitor is equal, expressions are considered equivalent.
2238 */
2239 class KeyVisitor {
2240 constructor() {
2241 this.visitWrappedNodeExpr = invalid;
2242 this.visitWriteVarExpr = invalid;
2243 this.visitWriteKeyExpr = invalid;
2244 this.visitWritePropExpr = invalid;
2245 this.visitInvokeMethodExpr = invalid;
2246 this.visitInvokeFunctionExpr = invalid;
2247 this.visitTaggedTemplateExpr = invalid;
2248 this.visitInstantiateExpr = invalid;
2249 this.visitConditionalExpr = invalid;
2250 this.visitNotExpr = invalid;
2251 this.visitAssertNotNullExpr = invalid;
2252 this.visitCastExpr = invalid;
2253 this.visitFunctionExpr = invalid;
2254 this.visitUnaryOperatorExpr = invalid;
2255 this.visitBinaryOperatorExpr = invalid;
2256 this.visitReadPropExpr = invalid;
2257 this.visitReadKeyExpr = invalid;
2258 this.visitCommaExpr = invalid;
2259 this.visitLocalizedString = invalid;
2260 }
2261 visitLiteralExpr(ast) {
2262 return `${typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value}`;
2263 }
2264 visitLiteralArrayExpr(ast, context) {
2265 return `[${ast.entries.map(entry => entry.visitExpression(this, context)).join(',')}]`;
2266 }
2267 visitLiteralMapExpr(ast, context) {
2268 const mapKey = (entry) => {
2269 const quote = entry.quoted ? '"' : '';
2270 return `${quote}${entry.key}${quote}`;
2271 };
2272 const mapEntry = (entry) => `${mapKey(entry)}:${entry.value.visitExpression(this, context)}`;
2273 return `{${ast.entries.map(mapEntry).join(',')}`;
2274 }
2275 visitExternalExpr(ast) {
2276 return ast.value.moduleName ? `EX:${ast.value.moduleName}:${ast.value.name}` :
2277 `EX:${ast.value.runtime.name}`;
2278 }
2279 visitReadVarExpr(node) {
2280 return `VAR:${node.name}`;
2281 }
2282 visitTypeofExpr(node, context) {
2283 return `TYPEOF:${node.expr.visitExpression(this, context)}`;
2284 }
2285 }
2286 function invalid(arg) {
2287 throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
2288 }
2289 function isVariable(e) {
2290 return e instanceof ReadVarExpr;
2291 }
2292 function isLongStringLiteral(expr) {
2293 return expr instanceof LiteralExpr && typeof expr.value === 'string' &&
2294 expr.value.length >= POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS;
2295 }
2296
2297 /**
2298 * @license
2299 * Copyright Google LLC All Rights Reserved.
2300 *
2301 * Use of this source code is governed by an MIT-style license that can be
2302 * found in the LICENSE file at https://angular.io/license
2303 */
2304 const CORE = '@angular/core';
2305 class Identifiers {
2306 }
2307 Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS = {
2308 name: 'ANALYZE_FOR_ENTRY_COMPONENTS',
2309 moduleName: CORE,
2310 };
2311 Identifiers.ElementRef = { name: 'ElementRef', moduleName: CORE };
2312 Identifiers.NgModuleRef = { name: 'NgModuleRef', moduleName: CORE };
2313 Identifiers.ViewContainerRef = { name: 'ViewContainerRef', moduleName: CORE };
2314 Identifiers.ChangeDetectorRef = {
2315 name: 'ChangeDetectorRef',
2316 moduleName: CORE,
2317 };
2318 Identifiers.QueryList = { name: 'QueryList', moduleName: CORE };
2319 Identifiers.TemplateRef = { name: 'TemplateRef', moduleName: CORE };
2320 Identifiers.Renderer2 = { name: 'Renderer2', moduleName: CORE };
2321 Identifiers.CodegenComponentFactoryResolver = {
2322 name: 'ɵCodegenComponentFactoryResolver',
2323 moduleName: CORE,
2324 };
2325 Identifiers.ComponentFactoryResolver = {
2326 name: 'ComponentFactoryResolver',
2327 moduleName: CORE,
2328 };
2329 Identifiers.ComponentFactory = { name: 'ComponentFactory', moduleName: CORE };
2330 Identifiers.ComponentRef = { name: 'ComponentRef', moduleName: CORE };
2331 Identifiers.NgModuleFactory = { name: 'NgModuleFactory', moduleName: CORE };
2332 Identifiers.createModuleFactory = {
2333 name: 'ɵcmf',
2334 moduleName: CORE,
2335 };
2336 Identifiers.moduleDef = {
2337 name: 'ɵmod',
2338 moduleName: CORE,
2339 };
2340 Identifiers.moduleProviderDef = {
2341 name: 'ɵmpd',
2342 moduleName: CORE,
2343 };
2344 Identifiers.RegisterModuleFactoryFn = {
2345 name: 'ɵregisterModuleFactory',
2346 moduleName: CORE,
2347 };
2348 Identifiers.inject = { name: 'ɵɵinject', moduleName: CORE };
2349 Identifiers.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE };
2350 Identifiers.INJECTOR = { name: 'INJECTOR', moduleName: CORE };
2351 Identifiers.Injector = { name: 'Injector', moduleName: CORE };
2352 Identifiers.ɵɵdefineInjectable = { name: 'ɵɵdefineInjectable', moduleName: CORE };
2353 Identifiers.InjectableDef = { name: 'ɵɵInjectableDef', moduleName: CORE };
2354 Identifiers.ViewEncapsulation = {
2355 name: 'ViewEncapsulation',
2356 moduleName: CORE,
2357 };
2358 Identifiers.ChangeDetectionStrategy = {
2359 name: 'ChangeDetectionStrategy',
2360 moduleName: CORE,
2361 };
2362 Identifiers.SecurityContext = {
2363 name: 'SecurityContext',
2364 moduleName: CORE,
2365 };
2366 Identifiers.LOCALE_ID = { name: 'LOCALE_ID', moduleName: CORE };
2367 Identifiers.TRANSLATIONS_FORMAT = {
2368 name: 'TRANSLATIONS_FORMAT',
2369 moduleName: CORE,
2370 };
2371 Identifiers.inlineInterpolate = {
2372 name: 'ɵinlineInterpolate',
2373 moduleName: CORE,
2374 };
2375 Identifiers.interpolate = { name: 'ɵinterpolate', moduleName: CORE };
2376 Identifiers.EMPTY_ARRAY = { name: 'ɵEMPTY_ARRAY', moduleName: CORE };
2377 Identifiers.EMPTY_MAP = { name: 'ɵEMPTY_MAP', moduleName: CORE };
2378 Identifiers.Renderer = { name: 'Renderer', moduleName: CORE };
2379 Identifiers.viewDef = { name: 'ɵvid', moduleName: CORE };
2380 Identifiers.elementDef = { name: 'ɵeld', moduleName: CORE };
2381 Identifiers.anchorDef = { name: 'ɵand', moduleName: CORE };
2382 Identifiers.textDef = { name: 'ɵted', moduleName: CORE };
2383 Identifiers.directiveDef = { name: 'ɵdid', moduleName: CORE };
2384 Identifiers.providerDef = { name: 'ɵprd', moduleName: CORE };
2385 Identifiers.queryDef = { name: 'ɵqud', moduleName: CORE };
2386 Identifiers.pureArrayDef = { name: 'ɵpad', moduleName: CORE };
2387 Identifiers.pureObjectDef = { name: 'ɵpod', moduleName: CORE };
2388 Identifiers.purePipeDef = { name: 'ɵppd', moduleName: CORE };
2389 Identifiers.pipeDef = { name: 'ɵpid', moduleName: CORE };
2390 Identifiers.nodeValue = { name: 'ɵnov', moduleName: CORE };
2391 Identifiers.ngContentDef = { name: 'ɵncd', moduleName: CORE };
2392 Identifiers.unwrapValue = { name: 'ɵunv', moduleName: CORE };
2393 Identifiers.createRendererType2 = { name: 'ɵcrt', moduleName: CORE };
2394 // type only
2395 Identifiers.RendererType2 = {
2396 name: 'RendererType2',
2397 moduleName: CORE,
2398 };
2399 // type only
2400 Identifiers.ViewDefinition = {
2401 name: 'ɵViewDefinition',
2402 moduleName: CORE,
2403 };
2404 Identifiers.createComponentFactory = { name: 'ɵccf', moduleName: CORE };
2405 Identifiers.setClassMetadata = { name: 'ɵsetClassMetadata', moduleName: CORE };
2406
2407 /**
2408 * @license
2409 * Copyright Google LLC All Rights Reserved.
2410 *
2411 * Use of this source code is governed by an MIT-style license that can be
2412 * found in the LICENSE file at https://angular.io/license
2413 */
2414 /**
2415 * A token representing the a reference to a static type.
2416 *
2417 * This token is unique for a filePath and name and can be used as a hash table key.
2418 */
2419 class StaticSymbol {
2420 constructor(filePath, name, members) {
2421 this.filePath = filePath;
2422 this.name = name;
2423 this.members = members;
2424 }
2425 assertNoMembers() {
2426 if (this.members.length) {
2427 throw new Error(`Illegal state: symbol without members expected, but got ${JSON.stringify(this)}.`);
2428 }
2429 }
2430 }
2431
2432 /**
2433 * @license
2434 * Copyright Google LLC All Rights Reserved.
2435 *
2436 * Use of this source code is governed by an MIT-style license that can be
2437 * found in the LICENSE file at https://angular.io/license
2438 */
2439 function sanitizeIdentifier(name) {
2440 return name.replace(/\W/g, '_');
2441 }
2442 let _anonymousTypeIndex = 0;
2443 function identifierName(compileIdentifier) {
2444 if (!compileIdentifier || !compileIdentifier.reference) {
2445 return null;
2446 }
2447 const ref = compileIdentifier.reference;
2448 if (ref instanceof StaticSymbol) {
2449 return ref.name;
2450 }
2451 if (ref['__anonymousType']) {
2452 return ref['__anonymousType'];
2453 }
2454 let identifier = stringify(ref);
2455 if (identifier.indexOf('(') >= 0) {
2456 // case: anonymous functions!
2457 identifier = `anonymous_${_anonymousTypeIndex++}`;
2458 ref['__anonymousType'] = identifier;
2459 }
2460 else {
2461 identifier = sanitizeIdentifier(identifier);
2462 }
2463 return identifier;
2464 }
2465 var CompileSummaryKind;
2466 (function (CompileSummaryKind) {
2467 CompileSummaryKind[CompileSummaryKind["Pipe"] = 0] = "Pipe";
2468 CompileSummaryKind[CompileSummaryKind["Directive"] = 1] = "Directive";
2469 CompileSummaryKind[CompileSummaryKind["NgModule"] = 2] = "NgModule";
2470 CompileSummaryKind[CompileSummaryKind["Injectable"] = 3] = "Injectable";
2471 })(CompileSummaryKind || (CompileSummaryKind = {}));
2472 function flatten(list) {
2473 return list.reduce((flat, item) => {
2474 const flatItem = Array.isArray(item) ? flatten(item) : item;
2475 return flat.concat(flatItem);
2476 }, []);
2477 }
2478
2479 /**
2480 * @license
2481 * Copyright Google LLC All Rights Reserved.
2482 *
2483 * Use of this source code is governed by an MIT-style license that can be
2484 * found in the LICENSE file at https://angular.io/license
2485 */
2486 const CORE$1 = '@angular/core';
2487 class Identifiers$1 {
2488 }
2489 /* Methods */
2490 Identifiers$1.NEW_METHOD = 'factory';
2491 Identifiers$1.TRANSFORM_METHOD = 'transform';
2492 Identifiers$1.PATCH_DEPS = 'patchedDeps';
2493 Identifiers$1.core = { name: null, moduleName: CORE$1 };
2494 /* Instructions */
2495 Identifiers$1.namespaceHTML = { name: 'ɵɵnamespaceHTML', moduleName: CORE$1 };
2496 Identifiers$1.namespaceMathML = { name: 'ɵɵnamespaceMathML', moduleName: CORE$1 };
2497 Identifiers$1.namespaceSVG = { name: 'ɵɵnamespaceSVG', moduleName: CORE$1 };
2498 Identifiers$1.element = { name: 'ɵɵelement', moduleName: CORE$1 };
2499 Identifiers$1.elementStart = { name: 'ɵɵelementStart', moduleName: CORE$1 };
2500 Identifiers$1.elementEnd = { name: 'ɵɵelementEnd', moduleName: CORE$1 };
2501 Identifiers$1.advance = { name: 'ɵɵadvance', moduleName: CORE$1 };
2502 Identifiers$1.syntheticHostProperty = { name: 'ɵɵsyntheticHostProperty', moduleName: CORE$1 };
2503 Identifiers$1.syntheticHostListener = { name: 'ɵɵsyntheticHostListener', moduleName: CORE$1 };
2504 Identifiers$1.attribute = { name: 'ɵɵattribute', moduleName: CORE$1 };
2505 Identifiers$1.attributeInterpolate1 = { name: 'ɵɵattributeInterpolate1', moduleName: CORE$1 };
2506 Identifiers$1.attributeInterpolate2 = { name: 'ɵɵattributeInterpolate2', moduleName: CORE$1 };
2507 Identifiers$1.attributeInterpolate3 = { name: 'ɵɵattributeInterpolate3', moduleName: CORE$1 };
2508 Identifiers$1.attributeInterpolate4 = { name: 'ɵɵattributeInterpolate4', moduleName: CORE$1 };
2509 Identifiers$1.attributeInterpolate5 = { name: 'ɵɵattributeInterpolate5', moduleName: CORE$1 };
2510 Identifiers$1.attributeInterpolate6 = { name: 'ɵɵattributeInterpolate6', moduleName: CORE$1 };
2511 Identifiers$1.attributeInterpolate7 = { name: 'ɵɵattributeInterpolate7', moduleName: CORE$1 };
2512 Identifiers$1.attributeInterpolate8 = { name: 'ɵɵattributeInterpolate8', moduleName: CORE$1 };
2513 Identifiers$1.attributeInterpolateV = { name: 'ɵɵattributeInterpolateV', moduleName: CORE$1 };
2514 Identifiers$1.classProp = { name: 'ɵɵclassProp', moduleName: CORE$1 };
2515 Identifiers$1.elementContainerStart = { name: 'ɵɵelementContainerStart', moduleName: CORE$1 };
2516 Identifiers$1.elementContainerEnd = { name: 'ɵɵelementContainerEnd', moduleName: CORE$1 };
2517 Identifiers$1.elementContainer = { name: 'ɵɵelementContainer', moduleName: CORE$1 };
2518 Identifiers$1.styleMap = { name: 'ɵɵstyleMap', moduleName: CORE$1 };
2519 Identifiers$1.styleMapInterpolate1 = { name: 'ɵɵstyleMapInterpolate1', moduleName: CORE$1 };
2520 Identifiers$1.styleMapInterpolate2 = { name: 'ɵɵstyleMapInterpolate2', moduleName: CORE$1 };
2521 Identifiers$1.styleMapInterpolate3 = { name: 'ɵɵstyleMapInterpolate3', moduleName: CORE$1 };
2522 Identifiers$1.styleMapInterpolate4 = { name: 'ɵɵstyleMapInterpolate4', moduleName: CORE$1 };
2523 Identifiers$1.styleMapInterpolate5 = { name: 'ɵɵstyleMapInterpolate5', moduleName: CORE$1 };
2524 Identifiers$1.styleMapInterpolate6 = { name: 'ɵɵstyleMapInterpolate6', moduleName: CORE$1 };
2525 Identifiers$1.styleMapInterpolate7 = { name: 'ɵɵstyleMapInterpolate7', moduleName: CORE$1 };
2526 Identifiers$1.styleMapInterpolate8 = { name: 'ɵɵstyleMapInterpolate8', moduleName: CORE$1 };
2527 Identifiers$1.styleMapInterpolateV = { name: 'ɵɵstyleMapInterpolateV', moduleName: CORE$1 };
2528 Identifiers$1.classMap = { name: 'ɵɵclassMap', moduleName: CORE$1 };
2529 Identifiers$1.classMapInterpolate1 = { name: 'ɵɵclassMapInterpolate1', moduleName: CORE$1 };
2530 Identifiers$1.classMapInterpolate2 = { name: 'ɵɵclassMapInterpolate2', moduleName: CORE$1 };
2531 Identifiers$1.classMapInterpolate3 = { name: 'ɵɵclassMapInterpolate3', moduleName: CORE$1 };
2532 Identifiers$1.classMapInterpolate4 = { name: 'ɵɵclassMapInterpolate4', moduleName: CORE$1 };
2533 Identifiers$1.classMapInterpolate5 = { name: 'ɵɵclassMapInterpolate5', moduleName: CORE$1 };
2534 Identifiers$1.classMapInterpolate6 = { name: 'ɵɵclassMapInterpolate6', moduleName: CORE$1 };
2535 Identifiers$1.classMapInterpolate7 = { name: 'ɵɵclassMapInterpolate7', moduleName: CORE$1 };
2536 Identifiers$1.classMapInterpolate8 = { name: 'ɵɵclassMapInterpolate8', moduleName: CORE$1 };
2537 Identifiers$1.classMapInterpolateV = { name: 'ɵɵclassMapInterpolateV', moduleName: CORE$1 };
2538 Identifiers$1.styleProp = { name: 'ɵɵstyleProp', moduleName: CORE$1 };
2539 Identifiers$1.stylePropInterpolate1 = { name: 'ɵɵstylePropInterpolate1', moduleName: CORE$1 };
2540 Identifiers$1.stylePropInterpolate2 = { name: 'ɵɵstylePropInterpolate2', moduleName: CORE$1 };
2541 Identifiers$1.stylePropInterpolate3 = { name: 'ɵɵstylePropInterpolate3', moduleName: CORE$1 };
2542 Identifiers$1.stylePropInterpolate4 = { name: 'ɵɵstylePropInterpolate4', moduleName: CORE$1 };
2543 Identifiers$1.stylePropInterpolate5 = { name: 'ɵɵstylePropInterpolate5', moduleName: CORE$1 };
2544 Identifiers$1.stylePropInterpolate6 = { name: 'ɵɵstylePropInterpolate6', moduleName: CORE$1 };
2545 Identifiers$1.stylePropInterpolate7 = { name: 'ɵɵstylePropInterpolate7', moduleName: CORE$1 };
2546 Identifiers$1.stylePropInterpolate8 = { name: 'ɵɵstylePropInterpolate8', moduleName: CORE$1 };
2547 Identifiers$1.stylePropInterpolateV = { name: 'ɵɵstylePropInterpolateV', moduleName: CORE$1 };
2548 Identifiers$1.nextContext = { name: 'ɵɵnextContext', moduleName: CORE$1 };
2549 Identifiers$1.templateCreate = { name: 'ɵɵtemplate', moduleName: CORE$1 };
2550 Identifiers$1.text = { name: 'ɵɵtext', moduleName: CORE$1 };
2551 Identifiers$1.enableBindings = { name: 'ɵɵenableBindings', moduleName: CORE$1 };
2552 Identifiers$1.disableBindings = { name: 'ɵɵdisableBindings', moduleName: CORE$1 };
2553 Identifiers$1.getCurrentView = { name: 'ɵɵgetCurrentView', moduleName: CORE$1 };
2554 Identifiers$1.textInterpolate = { name: 'ɵɵtextInterpolate', moduleName: CORE$1 };
2555 Identifiers$1.textInterpolate1 = { name: 'ɵɵtextInterpolate1', moduleName: CORE$1 };
2556 Identifiers$1.textInterpolate2 = { name: 'ɵɵtextInterpolate2', moduleName: CORE$1 };
2557 Identifiers$1.textInterpolate3 = { name: 'ɵɵtextInterpolate3', moduleName: CORE$1 };
2558 Identifiers$1.textInterpolate4 = { name: 'ɵɵtextInterpolate4', moduleName: CORE$1 };
2559 Identifiers$1.textInterpolate5 = { name: 'ɵɵtextInterpolate5', moduleName: CORE$1 };
2560 Identifiers$1.textInterpolate6 = { name: 'ɵɵtextInterpolate6', moduleName: CORE$1 };
2561 Identifiers$1.textInterpolate7 = { name: 'ɵɵtextInterpolate7', moduleName: CORE$1 };
2562 Identifiers$1.textInterpolate8 = { name: 'ɵɵtextInterpolate8', moduleName: CORE$1 };
2563 Identifiers$1.textInterpolateV = { name: 'ɵɵtextInterpolateV', moduleName: CORE$1 };
2564 Identifiers$1.restoreView = { name: 'ɵɵrestoreView', moduleName: CORE$1 };
2565 Identifiers$1.pureFunction0 = { name: 'ɵɵpureFunction0', moduleName: CORE$1 };
2566 Identifiers$1.pureFunction1 = { name: 'ɵɵpureFunction1', moduleName: CORE$1 };
2567 Identifiers$1.pureFunction2 = { name: 'ɵɵpureFunction2', moduleName: CORE$1 };
2568 Identifiers$1.pureFunction3 = { name: 'ɵɵpureFunction3', moduleName: CORE$1 };
2569 Identifiers$1.pureFunction4 = { name: 'ɵɵpureFunction4', moduleName: CORE$1 };
2570 Identifiers$1.pureFunction5 = { name: 'ɵɵpureFunction5', moduleName: CORE$1 };
2571 Identifiers$1.pureFunction6 = { name: 'ɵɵpureFunction6', moduleName: CORE$1 };
2572 Identifiers$1.pureFunction7 = { name: 'ɵɵpureFunction7', moduleName: CORE$1 };
2573 Identifiers$1.pureFunction8 = { name: 'ɵɵpureFunction8', moduleName: CORE$1 };
2574 Identifiers$1.pureFunctionV = { name: 'ɵɵpureFunctionV', moduleName: CORE$1 };
2575 Identifiers$1.pipeBind1 = { name: 'ɵɵpipeBind1', moduleName: CORE$1 };
2576 Identifiers$1.pipeBind2 = { name: 'ɵɵpipeBind2', moduleName: CORE$1 };
2577 Identifiers$1.pipeBind3 = { name: 'ɵɵpipeBind3', moduleName: CORE$1 };
2578 Identifiers$1.pipeBind4 = { name: 'ɵɵpipeBind4', moduleName: CORE$1 };
2579 Identifiers$1.pipeBindV = { name: 'ɵɵpipeBindV', moduleName: CORE$1 };
2580 Identifiers$1.hostProperty = { name: 'ɵɵhostProperty', moduleName: CORE$1 };
2581 Identifiers$1.property = { name: 'ɵɵproperty', moduleName: CORE$1 };
2582 Identifiers$1.propertyInterpolate = { name: 'ɵɵpropertyInterpolate', moduleName: CORE$1 };
2583 Identifiers$1.propertyInterpolate1 = { name: 'ɵɵpropertyInterpolate1', moduleName: CORE$1 };
2584 Identifiers$1.propertyInterpolate2 = { name: 'ɵɵpropertyInterpolate2', moduleName: CORE$1 };
2585 Identifiers$1.propertyInterpolate3 = { name: 'ɵɵpropertyInterpolate3', moduleName: CORE$1 };
2586 Identifiers$1.propertyInterpolate4 = { name: 'ɵɵpropertyInterpolate4', moduleName: CORE$1 };
2587 Identifiers$1.propertyInterpolate5 = { name: 'ɵɵpropertyInterpolate5', moduleName: CORE$1 };
2588 Identifiers$1.propertyInterpolate6 = { name: 'ɵɵpropertyInterpolate6', moduleName: CORE$1 };
2589 Identifiers$1.propertyInterpolate7 = { name: 'ɵɵpropertyInterpolate7', moduleName: CORE$1 };
2590 Identifiers$1.propertyInterpolate8 = { name: 'ɵɵpropertyInterpolate8', moduleName: CORE$1 };
2591 Identifiers$1.propertyInterpolateV = { name: 'ɵɵpropertyInterpolateV', moduleName: CORE$1 };
2592 Identifiers$1.i18n = { name: 'ɵɵi18n', moduleName: CORE$1 };
2593 Identifiers$1.i18nAttributes = { name: 'ɵɵi18nAttributes', moduleName: CORE$1 };
2594 Identifiers$1.i18nExp = { name: 'ɵɵi18nExp', moduleName: CORE$1 };
2595 Identifiers$1.i18nStart = { name: 'ɵɵi18nStart', moduleName: CORE$1 };
2596 Identifiers$1.i18nEnd = { name: 'ɵɵi18nEnd', moduleName: CORE$1 };
2597 Identifiers$1.i18nApply = { name: 'ɵɵi18nApply', moduleName: CORE$1 };
2598 Identifiers$1.i18nPostprocess = { name: 'ɵɵi18nPostprocess', moduleName: CORE$1 };
2599 Identifiers$1.pipe = { name: 'ɵɵpipe', moduleName: CORE$1 };
2600 Identifiers$1.projection = { name: 'ɵɵprojection', moduleName: CORE$1 };
2601 Identifiers$1.projectionDef = { name: 'ɵɵprojectionDef', moduleName: CORE$1 };
2602 Identifiers$1.reference = { name: 'ɵɵreference', moduleName: CORE$1 };
2603 Identifiers$1.inject = { name: 'ɵɵinject', moduleName: CORE$1 };
2604 Identifiers$1.injectAttribute = { name: 'ɵɵinjectAttribute', moduleName: CORE$1 };
2605 Identifiers$1.injectPipeChangeDetectorRef = { name: 'ɵɵinjectPipeChangeDetectorRef', moduleName: CORE$1 };
2606 Identifiers$1.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE$1 };
2607 Identifiers$1.invalidFactory = { name: 'ɵɵinvalidFactory', moduleName: CORE$1 };
2608 Identifiers$1.invalidFactoryDep = { name: 'ɵɵinvalidFactoryDep', moduleName: CORE$1 };
2609 Identifiers$1.templateRefExtractor = { name: 'ɵɵtemplateRefExtractor', moduleName: CORE$1 };
2610 Identifiers$1.forwardRef = { name: 'forwardRef', moduleName: CORE$1 };
2611 Identifiers$1.resolveForwardRef = { name: 'resolveForwardRef', moduleName: CORE$1 };
2612 Identifiers$1.resolveWindow = { name: 'ɵɵresolveWindow', moduleName: CORE$1 };
2613 Identifiers$1.resolveDocument = { name: 'ɵɵresolveDocument', moduleName: CORE$1 };
2614 Identifiers$1.resolveBody = { name: 'ɵɵresolveBody', moduleName: CORE$1 };
2615 Identifiers$1.defineComponent = { name: 'ɵɵdefineComponent', moduleName: CORE$1 };
2616 Identifiers$1.declareComponent = { name: 'ɵɵngDeclareComponent', moduleName: CORE$1 };
2617 Identifiers$1.setComponentScope = { name: 'ɵɵsetComponentScope', moduleName: CORE$1 };
2618 Identifiers$1.ChangeDetectionStrategy = {
2619 name: 'ChangeDetectionStrategy',
2620 moduleName: CORE$1,
2621 };
2622 Identifiers$1.ViewEncapsulation = {
2623 name: 'ViewEncapsulation',
2624 moduleName: CORE$1,
2625 };
2626 Identifiers$1.ComponentDefWithMeta = {
2627 name: 'ɵɵComponentDefWithMeta',
2628 moduleName: CORE$1,
2629 };
2630 Identifiers$1.FactoryDef = {
2631 name: 'ɵɵFactoryDef',
2632 moduleName: CORE$1,
2633 };
2634 Identifiers$1.defineDirective = { name: 'ɵɵdefineDirective', moduleName: CORE$1 };
2635 Identifiers$1.declareDirective = { name: 'ɵɵngDeclareDirective', moduleName: CORE$1 };
2636 Identifiers$1.DirectiveDefWithMeta = {
2637 name: 'ɵɵDirectiveDefWithMeta',
2638 moduleName: CORE$1,
2639 };
2640 Identifiers$1.InjectorDef = {
2641 name: 'ɵɵInjectorDef',
2642 moduleName: CORE$1,
2643 };
2644 Identifiers$1.defineInjector = {
2645 name: 'ɵɵdefineInjector',
2646 moduleName: CORE$1,
2647 };
2648 Identifiers$1.NgModuleDefWithMeta = {
2649 name: 'ɵɵNgModuleDefWithMeta',
2650 moduleName: CORE$1,
2651 };
2652 Identifiers$1.ModuleWithProviders = {
2653 name: 'ModuleWithProviders',
2654 moduleName: CORE$1,
2655 };
2656 Identifiers$1.defineNgModule = { name: 'ɵɵdefineNgModule', moduleName: CORE$1 };
2657 Identifiers$1.setNgModuleScope = { name: 'ɵɵsetNgModuleScope', moduleName: CORE$1 };
2658 Identifiers$1.PipeDefWithMeta = { name: 'ɵɵPipeDefWithMeta', moduleName: CORE$1 };
2659 Identifiers$1.definePipe = { name: 'ɵɵdefinePipe', moduleName: CORE$1 };
2660 Identifiers$1.queryRefresh = { name: 'ɵɵqueryRefresh', moduleName: CORE$1 };
2661 Identifiers$1.viewQuery = { name: 'ɵɵviewQuery', moduleName: CORE$1 };
2662 Identifiers$1.loadQuery = { name: 'ɵɵloadQuery', moduleName: CORE$1 };
2663 Identifiers$1.contentQuery = { name: 'ɵɵcontentQuery', moduleName: CORE$1 };
2664 Identifiers$1.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE$1 };
2665 Identifiers$1.InheritDefinitionFeature = { name: 'ɵɵInheritDefinitionFeature', moduleName: CORE$1 };
2666 Identifiers$1.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE$1 };
2667 Identifiers$1.ProvidersFeature = { name: 'ɵɵProvidersFeature', moduleName: CORE$1 };
2668 Identifiers$1.listener = { name: 'ɵɵlistener', moduleName: CORE$1 };
2669 Identifiers$1.getFactoryOf = {
2670 name: 'ɵɵgetFactoryOf',
2671 moduleName: CORE$1,
2672 };
2673 Identifiers$1.getInheritedFactory = {
2674 name: 'ɵɵgetInheritedFactory',
2675 moduleName: CORE$1,
2676 };
2677 // sanitization-related functions
2678 Identifiers$1.sanitizeHtml = { name: 'ɵɵsanitizeHtml', moduleName: CORE$1 };
2679 Identifiers$1.sanitizeStyle = { name: 'ɵɵsanitizeStyle', moduleName: CORE$1 };
2680 Identifiers$1.sanitizeResourceUrl = { name: 'ɵɵsanitizeResourceUrl', moduleName: CORE$1 };
2681 Identifiers$1.sanitizeScript = { name: 'ɵɵsanitizeScript', moduleName: CORE$1 };
2682 Identifiers$1.sanitizeUrl = { name: 'ɵɵsanitizeUrl', moduleName: CORE$1 };
2683 Identifiers$1.sanitizeUrlOrResourceUrl = { name: 'ɵɵsanitizeUrlOrResourceUrl', moduleName: CORE$1 };
2684 Identifiers$1.trustConstantHtml = { name: 'ɵɵtrustConstantHtml', moduleName: CORE$1 };
2685 Identifiers$1.trustConstantResourceUrl = { name: 'ɵɵtrustConstantResourceUrl', moduleName: CORE$1 };
2686
2687 /**
2688 * @license
2689 * Copyright Google LLC All Rights Reserved.
2690 *
2691 * Use of this source code is governed by an MIT-style license that can be
2692 * found in the LICENSE file at https://angular.io/license
2693 */
2694 // https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
2695 const VERSION = 3;
2696 const JS_B64_PREFIX = '# sourceMappingURL=data:application/json;base64,';
2697 class SourceMapGenerator {
2698 constructor(file = null) {
2699 this.file = file;
2700 this.sourcesContent = new Map();
2701 this.lines = [];
2702 this.lastCol0 = 0;
2703 this.hasMappings = false;
2704 }
2705 // The content is `null` when the content is expected to be loaded using the URL
2706 addSource(url, content = null) {
2707 if (!this.sourcesContent.has(url)) {
2708 this.sourcesContent.set(url, content);
2709 }
2710 return this;
2711 }
2712 addLine() {
2713 this.lines.push([]);
2714 this.lastCol0 = 0;
2715 return this;
2716 }
2717 addMapping(col0, sourceUrl, sourceLine0, sourceCol0) {
2718 if (!this.currentLine) {
2719 throw new Error(`A line must be added before mappings can be added`);
2720 }
2721 if (sourceUrl != null && !this.sourcesContent.has(sourceUrl)) {
2722 throw new Error(`Unknown source file "${sourceUrl}"`);
2723 }
2724 if (col0 == null) {
2725 throw new Error(`The column in the generated code must be provided`);
2726 }
2727 if (col0 < this.lastCol0) {
2728 throw new Error(`Mapping should be added in output order`);
2729 }
2730 if (sourceUrl && (sourceLine0 == null || sourceCol0 == null)) {
2731 throw new Error(`The source location must be provided when a source url is provided`);
2732 }
2733 this.hasMappings = true;
2734 this.lastCol0 = col0;
2735 this.currentLine.push({ col0, sourceUrl, sourceLine0, sourceCol0 });
2736 return this;
2737 }
2738 /**
2739 * @internal strip this from published d.ts files due to
2740 * https://github.com/microsoft/TypeScript/issues/36216
2741 */
2742 get currentLine() {
2743 return this.lines.slice(-1)[0];
2744 }
2745 toJSON() {
2746 if (!this.hasMappings) {
2747 return null;
2748 }
2749 const sourcesIndex = new Map();
2750 const sources = [];
2751 const sourcesContent = [];
2752 Array.from(this.sourcesContent.keys()).forEach((url, i) => {
2753 sourcesIndex.set(url, i);
2754 sources.push(url);
2755 sourcesContent.push(this.sourcesContent.get(url) || null);
2756 });
2757 let mappings = '';
2758 let lastCol0 = 0;
2759 let lastSourceIndex = 0;
2760 let lastSourceLine0 = 0;
2761 let lastSourceCol0 = 0;
2762 this.lines.forEach(segments => {
2763 lastCol0 = 0;
2764 mappings += segments
2765 .map(segment => {
2766 // zero-based starting column of the line in the generated code
2767 let segAsStr = toBase64VLQ(segment.col0 - lastCol0);
2768 lastCol0 = segment.col0;
2769 if (segment.sourceUrl != null) {
2770 // zero-based index into the “sources” list
2771 segAsStr +=
2772 toBase64VLQ(sourcesIndex.get(segment.sourceUrl) - lastSourceIndex);
2773 lastSourceIndex = sourcesIndex.get(segment.sourceUrl);
2774 // the zero-based starting line in the original source
2775 segAsStr += toBase64VLQ(segment.sourceLine0 - lastSourceLine0);
2776 lastSourceLine0 = segment.sourceLine0;
2777 // the zero-based starting column in the original source
2778 segAsStr += toBase64VLQ(segment.sourceCol0 - lastSourceCol0);
2779 lastSourceCol0 = segment.sourceCol0;
2780 }
2781 return segAsStr;
2782 })
2783 .join(',');
2784 mappings += ';';
2785 });
2786 mappings = mappings.slice(0, -1);
2787 return {
2788 'file': this.file || '',
2789 'version': VERSION,
2790 'sourceRoot': '',
2791 'sources': sources,
2792 'sourcesContent': sourcesContent,
2793 'mappings': mappings,
2794 };
2795 }
2796 toJsComment() {
2797 return this.hasMappings ? '//' + JS_B64_PREFIX + toBase64String(JSON.stringify(this, null, 0)) :
2798 '';
2799 }
2800 }
2801 function toBase64String(value) {
2802 let b64 = '';
2803 const encoded = utf8Encode(value);
2804 for (let i = 0; i < encoded.length;) {
2805 const i1 = encoded[i++];
2806 const i2 = i < encoded.length ? encoded[i++] : null;
2807 const i3 = i < encoded.length ? encoded[i++] : null;
2808 b64 += toBase64Digit(i1 >> 2);
2809 b64 += toBase64Digit(((i1 & 3) << 4) | (i2 === null ? 0 : i2 >> 4));
2810 b64 += i2 === null ? '=' : toBase64Digit(((i2 & 15) << 2) | (i3 === null ? 0 : i3 >> 6));
2811 b64 += i2 === null || i3 === null ? '=' : toBase64Digit(i3 & 63);
2812 }
2813 return b64;
2814 }
2815 function toBase64VLQ(value) {
2816 value = value < 0 ? ((-value) << 1) + 1 : value << 1;
2817 let out = '';
2818 do {
2819 let digit = value & 31;
2820 value = value >> 5;
2821 if (value > 0) {
2822 digit = digit | 32;
2823 }
2824 out += toBase64Digit(digit);
2825 } while (value > 0);
2826 return out;
2827 }
2828 const B64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
2829 function toBase64Digit(value) {
2830 if (value < 0 || value >= 64) {
2831 throw new Error(`Can only encode value in the range [0, 63]`);
2832 }
2833 return B64_DIGITS[value];
2834 }
2835
2836 /**
2837 * @license
2838 * Copyright Google LLC All Rights Reserved.
2839 *
2840 * Use of this source code is governed by an MIT-style license that can be
2841 * found in the LICENSE file at https://angular.io/license
2842 */
2843 const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
2844 const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
2845 const _INDENT_WITH = ' ';
2846 const CATCH_ERROR_VAR$1 = variable('error', null, null);
2847 const CATCH_STACK_VAR$1 = variable('stack', null, null);
2848 class _EmittedLine {
2849 constructor(indent) {
2850 this.indent = indent;
2851 this.partsLength = 0;
2852 this.parts = [];
2853 this.srcSpans = [];
2854 }
2855 }
2856 class EmitterVisitorContext {
2857 constructor(_indent) {
2858 this._indent = _indent;
2859 this._classes = [];
2860 this._preambleLineCount = 0;
2861 this._lines = [new _EmittedLine(_indent)];
2862 }
2863 static createRoot() {
2864 return new EmitterVisitorContext(0);
2865 }
2866 /**
2867 * @internal strip this from published d.ts files due to
2868 * https://github.com/microsoft/TypeScript/issues/36216
2869 */
2870 get _currentLine() {
2871 return this._lines[this._lines.length - 1];
2872 }
2873 println(from, lastPart = '') {
2874 this.print(from || null, lastPart, true);
2875 }
2876 lineIsEmpty() {
2877 return this._currentLine.parts.length === 0;
2878 }
2879 lineLength() {
2880 return this._currentLine.indent * _INDENT_WITH.length + this._currentLine.partsLength;
2881 }
2882 print(from, part, newLine = false) {
2883 if (part.length > 0) {
2884 this._currentLine.parts.push(part);
2885 this._currentLine.partsLength += part.length;
2886 this._currentLine.srcSpans.push(from && from.sourceSpan || null);
2887 }
2888 if (newLine) {
2889 this._lines.push(new _EmittedLine(this._indent));
2890 }
2891 }
2892 removeEmptyLastLine() {
2893 if (this.lineIsEmpty()) {
2894 this._lines.pop();
2895 }
2896 }
2897 incIndent() {
2898 this._indent++;
2899 if (this.lineIsEmpty()) {
2900 this._currentLine.indent = this._indent;
2901 }
2902 }
2903 decIndent() {
2904 this._indent--;
2905 if (this.lineIsEmpty()) {
2906 this._currentLine.indent = this._indent;
2907 }
2908 }
2909 pushClass(clazz) {
2910 this._classes.push(clazz);
2911 }
2912 popClass() {
2913 return this._classes.pop();
2914 }
2915 get currentClass() {
2916 return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null;
2917 }
2918 toSource() {
2919 return this.sourceLines
2920 .map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '')
2921 .join('\n');
2922 }
2923 toSourceMapGenerator(genFilePath, startsAtLine = 0) {
2924 const map = new SourceMapGenerator(genFilePath);
2925 let firstOffsetMapped = false;
2926 const mapFirstOffsetIfNeeded = () => {
2927 if (!firstOffsetMapped) {
2928 // Add a single space so that tools won't try to load the file from disk.
2929 // Note: We are using virtual urls like `ng:///`, so we have to
2930 // provide a content here.
2931 map.addSource(genFilePath, ' ').addMapping(0, genFilePath, 0, 0);
2932 firstOffsetMapped = true;
2933 }
2934 };
2935 for (let i = 0; i < startsAtLine; i++) {
2936 map.addLine();
2937 mapFirstOffsetIfNeeded();
2938 }
2939 this.sourceLines.forEach((line, lineIdx) => {
2940 map.addLine();
2941 const spans = line.srcSpans;
2942 const parts = line.parts;
2943 let col0 = line.indent * _INDENT_WITH.length;
2944 let spanIdx = 0;
2945 // skip leading parts without source spans
2946 while (spanIdx < spans.length && !spans[spanIdx]) {
2947 col0 += parts[spanIdx].length;
2948 spanIdx++;
2949 }
2950 if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) {
2951 firstOffsetMapped = true;
2952 }
2953 else {
2954 mapFirstOffsetIfNeeded();
2955 }
2956 while (spanIdx < spans.length) {
2957 const span = spans[spanIdx];
2958 const source = span.start.file;
2959 const sourceLine = span.start.line;
2960 const sourceCol = span.start.col;
2961 map.addSource(source.url, source.content)
2962 .addMapping(col0, source.url, sourceLine, sourceCol);
2963 col0 += parts[spanIdx].length;
2964 spanIdx++;
2965 // assign parts without span or the same span to the previous segment
2966 while (spanIdx < spans.length && (span === spans[spanIdx] || !spans[spanIdx])) {
2967 col0 += parts[spanIdx].length;
2968 spanIdx++;
2969 }
2970 }
2971 });
2972 return map;
2973 }
2974 setPreambleLineCount(count) {
2975 return this._preambleLineCount = count;
2976 }
2977 spanOf(line, column) {
2978 const emittedLine = this._lines[line - this._preambleLineCount];
2979 if (emittedLine) {
2980 let columnsLeft = column - _createIndent(emittedLine.indent).length;
2981 for (let partIndex = 0; partIndex < emittedLine.parts.length; partIndex++) {
2982 const part = emittedLine.parts[partIndex];
2983 if (part.length > columnsLeft) {
2984 return emittedLine.srcSpans[partIndex];
2985 }
2986 columnsLeft -= part.length;
2987 }
2988 }
2989 return null;
2990 }
2991 /**
2992 * @internal strip this from published d.ts files due to
2993 * https://github.com/microsoft/TypeScript/issues/36216
2994 */
2995 get sourceLines() {
2996 if (this._lines.length && this._lines[this._lines.length - 1].parts.length === 0) {
2997 return this._lines.slice(0, -1);
2998 }
2999 return this._lines;
3000 }
3001 }
3002 class AbstractEmitterVisitor {
3003 constructor(_escapeDollarInStrings) {
3004 this._escapeDollarInStrings = _escapeDollarInStrings;
3005 }
3006 printLeadingComments(stmt, ctx) {
3007 if (stmt.leadingComments === undefined) {
3008 return;
3009 }
3010 for (const comment of stmt.leadingComments) {
3011 if (comment instanceof JSDocComment) {
3012 ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline);
3013 }
3014 else {
3015 if (comment.multiline) {
3016 ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline);
3017 }
3018 else {
3019 comment.text.split('\n').forEach((line) => {
3020 ctx.println(stmt, `// ${line}`);
3021 });
3022 }
3023 }
3024 }
3025 }
3026 visitExpressionStmt(stmt, ctx) {
3027 this.printLeadingComments(stmt, ctx);
3028 stmt.expr.visitExpression(this, ctx);
3029 ctx.println(stmt, ';');
3030 return null;
3031 }
3032 visitReturnStmt(stmt, ctx) {
3033 this.printLeadingComments(stmt, ctx);
3034 ctx.print(stmt, `return `);
3035 stmt.value.visitExpression(this, ctx);
3036 ctx.println(stmt, ';');
3037 return null;
3038 }
3039 visitIfStmt(stmt, ctx) {
3040 this.printLeadingComments(stmt, ctx);
3041 ctx.print(stmt, `if (`);
3042 stmt.condition.visitExpression(this, ctx);
3043 ctx.print(stmt, `) {`);
3044 const hasElseCase = stmt.falseCase != null && stmt.falseCase.length > 0;
3045 if (stmt.trueCase.length <= 1 && !hasElseCase) {
3046 ctx.print(stmt, ` `);
3047 this.visitAllStatements(stmt.trueCase, ctx);
3048 ctx.removeEmptyLastLine();
3049 ctx.print(stmt, ` `);
3050 }
3051 else {
3052 ctx.println();
3053 ctx.incIndent();
3054 this.visitAllStatements(stmt.trueCase, ctx);
3055 ctx.decIndent();
3056 if (hasElseCase) {
3057 ctx.println(stmt, `} else {`);
3058 ctx.incIndent();
3059 this.visitAllStatements(stmt.falseCase, ctx);
3060 ctx.decIndent();
3061 }
3062 }
3063 ctx.println(stmt, `}`);
3064 return null;
3065 }
3066 visitThrowStmt(stmt, ctx) {
3067 this.printLeadingComments(stmt, ctx);
3068 ctx.print(stmt, `throw `);
3069 stmt.error.visitExpression(this, ctx);
3070 ctx.println(stmt, `;`);
3071 return null;
3072 }
3073 visitWriteVarExpr(expr, ctx) {
3074 const lineWasEmpty = ctx.lineIsEmpty();
3075 if (!lineWasEmpty) {
3076 ctx.print(expr, '(');
3077 }
3078 ctx.print(expr, `${expr.name} = `);
3079 expr.value.visitExpression(this, ctx);
3080 if (!lineWasEmpty) {
3081 ctx.print(expr, ')');
3082 }
3083 return null;
3084 }
3085 visitWriteKeyExpr(expr, ctx) {
3086 const lineWasEmpty = ctx.lineIsEmpty();
3087 if (!lineWasEmpty) {
3088 ctx.print(expr, '(');
3089 }
3090 expr.receiver.visitExpression(this, ctx);
3091 ctx.print(expr, `[`);
3092 expr.index.visitExpression(this, ctx);
3093 ctx.print(expr, `] = `);
3094 expr.value.visitExpression(this, ctx);
3095 if (!lineWasEmpty) {
3096 ctx.print(expr, ')');
3097 }
3098 return null;
3099 }
3100 visitWritePropExpr(expr, ctx) {
3101 const lineWasEmpty = ctx.lineIsEmpty();
3102 if (!lineWasEmpty) {
3103 ctx.print(expr, '(');
3104 }
3105 expr.receiver.visitExpression(this, ctx);
3106 ctx.print(expr, `.${expr.name} = `);
3107 expr.value.visitExpression(this, ctx);
3108 if (!lineWasEmpty) {
3109 ctx.print(expr, ')');
3110 }
3111 return null;
3112 }
3113 visitInvokeMethodExpr(expr, ctx) {
3114 expr.receiver.visitExpression(this, ctx);
3115 let name = expr.name;
3116 if (expr.builtin != null) {
3117 name = this.getBuiltinMethodName(expr.builtin);
3118 if (name == null) {
3119 // some builtins just mean to skip the call.
3120 return null;
3121 }
3122 }
3123 ctx.print(expr, `.${name}(`);
3124 this.visitAllExpressions(expr.args, ctx, `,`);
3125 ctx.print(expr, `)`);
3126 return null;
3127 }
3128 visitInvokeFunctionExpr(expr, ctx) {
3129 expr.fn.visitExpression(this, ctx);
3130 ctx.print(expr, `(`);
3131 this.visitAllExpressions(expr.args, ctx, ',');
3132 ctx.print(expr, `)`);
3133 return null;
3134 }
3135 visitTaggedTemplateExpr(expr, ctx) {
3136 expr.tag.visitExpression(this, ctx);
3137 ctx.print(expr, '`' + expr.template.elements[0].rawText);
3138 for (let i = 1; i < expr.template.elements.length; i++) {
3139 ctx.print(expr, '${');
3140 expr.template.expressions[i - 1].visitExpression(this, ctx);
3141 ctx.print(expr, `}${expr.template.elements[i].rawText}`);
3142 }
3143 ctx.print(expr, '`');
3144 return null;
3145 }
3146 visitWrappedNodeExpr(ast, ctx) {
3147 throw new Error('Abstract emitter cannot visit WrappedNodeExpr.');
3148 }
3149 visitTypeofExpr(expr, ctx) {
3150 ctx.print(expr, 'typeof ');
3151 expr.expr.visitExpression(this, ctx);
3152 }
3153 visitReadVarExpr(ast, ctx) {
3154 let varName = ast.name;
3155 if (ast.builtin != null) {
3156 switch (ast.builtin) {
3157 case BuiltinVar.Super:
3158 varName = 'super';
3159 break;
3160 case BuiltinVar.This:
3161 varName = 'this';
3162 break;
3163 case BuiltinVar.CatchError:
3164 varName = CATCH_ERROR_VAR$1.name;
3165 break;
3166 case BuiltinVar.CatchStack:
3167 varName = CATCH_STACK_VAR$1.name;
3168 break;
3169 default:
3170 throw new Error(`Unknown builtin variable ${ast.builtin}`);
3171 }
3172 }
3173 ctx.print(ast, varName);
3174 return null;
3175 }
3176 visitInstantiateExpr(ast, ctx) {
3177 ctx.print(ast, `new `);
3178 ast.classExpr.visitExpression(this, ctx);
3179 ctx.print(ast, `(`);
3180 this.visitAllExpressions(ast.args, ctx, ',');
3181 ctx.print(ast, `)`);
3182 return null;
3183 }
3184 visitLiteralExpr(ast, ctx) {
3185 const value = ast.value;
3186 if (typeof value === 'string') {
3187 ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings));
3188 }
3189 else {
3190 ctx.print(ast, `${value}`);
3191 }
3192 return null;
3193 }
3194 visitLocalizedString(ast, ctx) {
3195 const head = ast.serializeI18nHead();
3196 ctx.print(ast, '$localize `' + head.raw);
3197 for (let i = 1; i < ast.messageParts.length; i++) {
3198 ctx.print(ast, '${');
3199 ast.expressions[i - 1].visitExpression(this, ctx);
3200 ctx.print(ast, `}${ast.serializeI18nTemplatePart(i).raw}`);
3201 }
3202 ctx.print(ast, '`');
3203 return null;
3204 }
3205 visitConditionalExpr(ast, ctx) {
3206 ctx.print(ast, `(`);
3207 ast.condition.visitExpression(this, ctx);
3208 ctx.print(ast, '? ');
3209 ast.trueCase.visitExpression(this, ctx);
3210 ctx.print(ast, ': ');
3211 ast.falseCase.visitExpression(this, ctx);
3212 ctx.print(ast, `)`);
3213 return null;
3214 }
3215 visitNotExpr(ast, ctx) {
3216 ctx.print(ast, '!');
3217 ast.condition.visitExpression(this, ctx);
3218 return null;
3219 }
3220 visitAssertNotNullExpr(ast, ctx) {
3221 ast.condition.visitExpression(this, ctx);
3222 return null;
3223 }
3224 visitUnaryOperatorExpr(ast, ctx) {
3225 let opStr;
3226 switch (ast.operator) {
3227 case UnaryOperator.Plus:
3228 opStr = '+';
3229 break;
3230 case UnaryOperator.Minus:
3231 opStr = '-';
3232 break;
3233 default:
3234 throw new Error(`Unknown operator ${ast.operator}`);
3235 }
3236 if (ast.parens)
3237 ctx.print(ast, `(`);
3238 ctx.print(ast, opStr);
3239 ast.expr.visitExpression(this, ctx);
3240 if (ast.parens)
3241 ctx.print(ast, `)`);
3242 return null;
3243 }
3244 visitBinaryOperatorExpr(ast, ctx) {
3245 let opStr;
3246 switch (ast.operator) {
3247 case BinaryOperator.Equals:
3248 opStr = '==';
3249 break;
3250 case BinaryOperator.Identical:
3251 opStr = '===';
3252 break;
3253 case BinaryOperator.NotEquals:
3254 opStr = '!=';
3255 break;
3256 case BinaryOperator.NotIdentical:
3257 opStr = '!==';
3258 break;
3259 case BinaryOperator.And:
3260 opStr = '&&';
3261 break;
3262 case BinaryOperator.BitwiseAnd:
3263 opStr = '&';
3264 break;
3265 case BinaryOperator.Or:
3266 opStr = '||';
3267 break;
3268 case BinaryOperator.Plus:
3269 opStr = '+';
3270 break;
3271 case BinaryOperator.Minus:
3272 opStr = '-';
3273 break;
3274 case BinaryOperator.Divide:
3275 opStr = '/';
3276 break;
3277 case BinaryOperator.Multiply:
3278 opStr = '*';
3279 break;
3280 case BinaryOperator.Modulo:
3281 opStr = '%';
3282 break;
3283 case BinaryOperator.Lower:
3284 opStr = '<';
3285 break;
3286 case BinaryOperator.LowerEquals:
3287 opStr = '<=';
3288 break;
3289 case BinaryOperator.Bigger:
3290 opStr = '>';
3291 break;
3292 case BinaryOperator.BiggerEquals:
3293 opStr = '>=';
3294 break;
3295 default:
3296 throw new Error(`Unknown operator ${ast.operator}`);
3297 }
3298 if (ast.parens)
3299 ctx.print(ast, `(`);
3300 ast.lhs.visitExpression(this, ctx);
3301 ctx.print(ast, ` ${opStr} `);
3302 ast.rhs.visitExpression(this, ctx);
3303 if (ast.parens)
3304 ctx.print(ast, `)`);
3305 return null;
3306 }
3307 visitReadPropExpr(ast, ctx) {
3308 ast.receiver.visitExpression(this, ctx);
3309 ctx.print(ast, `.`);
3310 ctx.print(ast, ast.name);
3311 return null;
3312 }
3313 visitReadKeyExpr(ast, ctx) {
3314 ast.receiver.visitExpression(this, ctx);
3315 ctx.print(ast, `[`);
3316 ast.index.visitExpression(this, ctx);
3317 ctx.print(ast, `]`);
3318 return null;
3319 }
3320 visitLiteralArrayExpr(ast, ctx) {
3321 ctx.print(ast, `[`);
3322 this.visitAllExpressions(ast.entries, ctx, ',');
3323 ctx.print(ast, `]`);
3324 return null;
3325 }
3326 visitLiteralMapExpr(ast, ctx) {
3327 ctx.print(ast, `{`);
3328 this.visitAllObjects(entry => {
3329 ctx.print(ast, `${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`);
3330 entry.value.visitExpression(this, ctx);
3331 }, ast.entries, ctx, ',');
3332 ctx.print(ast, `}`);
3333 return null;
3334 }
3335 visitCommaExpr(ast, ctx) {
3336 ctx.print(ast, '(');
3337 this.visitAllExpressions(ast.parts, ctx, ',');
3338 ctx.print(ast, ')');
3339 return null;
3340 }
3341 visitAllExpressions(expressions, ctx, separator) {
3342 this.visitAllObjects(expr => expr.visitExpression(this, ctx), expressions, ctx, separator);
3343 }
3344 visitAllObjects(handler, expressions, ctx, separator) {
3345 let incrementedIndent = false;
3346 for (let i = 0; i < expressions.length; i++) {
3347 if (i > 0) {
3348 if (ctx.lineLength() > 80) {
3349 ctx.print(null, separator, true);
3350 if (!incrementedIndent) {
3351 // continuation are marked with double indent.
3352 ctx.incIndent();
3353 ctx.incIndent();
3354 incrementedIndent = true;
3355 }
3356 }
3357 else {
3358 ctx.print(null, separator, false);
3359 }
3360 }
3361 handler(expressions[i]);
3362 }
3363 if (incrementedIndent) {
3364 // continuation are marked with double indent.
3365 ctx.decIndent();
3366 ctx.decIndent();
3367 }
3368 }
3369 visitAllStatements(statements, ctx) {
3370 statements.forEach((stmt) => stmt.visitStatement(this, ctx));
3371 }
3372 }
3373 function escapeIdentifier(input, escapeDollar, alwaysQuote = true) {
3374 if (input == null) {
3375 return null;
3376 }
3377 const body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match) => {
3378 if (match[0] == '$') {
3379 return escapeDollar ? '\\$' : '$';
3380 }
3381 else if (match[0] == '\n') {
3382 return '\\n';
3383 }
3384 else if (match[0] == '\r') {
3385 return '\\r';
3386 }
3387 else {
3388 return `\\${match[0]}`;
3389 }
3390 });
3391 const requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body);
3392 return requiresQuotes ? `'${body}'` : body;
3393 }
3394 function _createIndent(count) {
3395 let res = '';
3396 for (let i = 0; i < count; i++) {
3397 res += _INDENT_WITH;
3398 }
3399 return res;
3400 }
3401
3402 /**
3403 * @license
3404 * Copyright Google LLC All Rights Reserved.
3405 *
3406 * Use of this source code is governed by an MIT-style license that can be
3407 * found in the LICENSE file at https://angular.io/license
3408 */
3409 /**
3410 * Convert an object map with `Expression` values into a `LiteralMapExpr`.
3411 */
3412 function mapToMapExpression(map) {
3413 const result = Object.keys(map).map(key => ({
3414 key,
3415 // The assertion here is because really TypeScript doesn't allow us to express that if the
3416 // key is present, it will have a value, but this is true in reality.
3417 value: map[key],
3418 quoted: false,
3419 }));
3420 return literalMap(result);
3421 }
3422 function typeWithParameters(type, numParams) {
3423 if (numParams === 0) {
3424 return expressionType(type);
3425 }
3426 const params = [];
3427 for (let i = 0; i < numParams; i++) {
3428 params.push(DYNAMIC_TYPE);
3429 }
3430 return expressionType(type, undefined, params);
3431 }
3432 const ANIMATE_SYMBOL_PREFIX = '@';
3433 function prepareSyntheticPropertyName(name) {
3434 return `${ANIMATE_SYMBOL_PREFIX}${name}`;
3435 }
3436 function prepareSyntheticListenerName(name, phase) {
3437 return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`;
3438 }
3439 function getSafePropertyAccessString(accessor, name) {
3440 const escapedName = escapeIdentifier(name, false, false);
3441 return escapedName !== name ? `${accessor}[${escapedName}]` : `${accessor}.${name}`;
3442 }
3443 function prepareSyntheticListenerFunctionName(name, phase) {
3444 return `animation_${name}_${phase}`;
3445 }
3446 function jitOnlyGuardedExpression(expr) {
3447 return guardedExpression('ngJitMode', expr);
3448 }
3449 function devOnlyGuardedExpression(expr) {
3450 return guardedExpression('ngDevMode', expr);
3451 }
3452 function guardedExpression(guard, expr) {
3453 const guardExpr = new ExternalExpr({ name: guard, moduleName: null });
3454 const guardNotDefined = new BinaryOperatorExpr(BinaryOperator.Identical, new TypeofExpr(guardExpr), literal('undefined'));
3455 const guardUndefinedOrTrue = new BinaryOperatorExpr(BinaryOperator.Or, guardNotDefined, guardExpr, /* type */ undefined,
3456 /* sourceSpan */ undefined, true);
3457 return new BinaryOperatorExpr(BinaryOperator.And, guardUndefinedOrTrue, expr);
3458 }
3459
3460 /**
3461 * @license
3462 * Copyright Google LLC All Rights Reserved.
3463 *
3464 * Use of this source code is governed by an MIT-style license that can be
3465 * found in the LICENSE file at https://angular.io/license
3466 */
3467 class Text {
3468 constructor(value, sourceSpan) {
3469 this.value = value;
3470 this.sourceSpan = sourceSpan;
3471 }
3472 visit(visitor) {
3473 return visitor.visitText(this);
3474 }
3475 }
3476 class BoundText {
3477 constructor(value, sourceSpan, i18n) {
3478 this.value = value;
3479 this.sourceSpan = sourceSpan;
3480 this.i18n = i18n;
3481 }
3482 visit(visitor) {
3483 return visitor.visitBoundText(this);
3484 }
3485 }
3486 /**
3487 * Represents a text attribute in the template.
3488 *
3489 * `valueSpan` may not be present in cases where there is no value `<div a></div>`.
3490 * `keySpan` may also not be present for synthetic attributes from ICU expansions.
3491 */
3492 class TextAttribute {
3493 constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) {
3494 this.name = name;
3495 this.value = value;
3496 this.sourceSpan = sourceSpan;
3497 this.keySpan = keySpan;
3498 this.valueSpan = valueSpan;
3499 this.i18n = i18n;
3500 }
3501 visit(visitor) {
3502 return visitor.visitTextAttribute(this);
3503 }
3504 }
3505 class BoundAttribute {
3506 constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan, i18n) {
3507 this.name = name;
3508 this.type = type;
3509 this.securityContext = securityContext;
3510 this.value = value;
3511 this.unit = unit;
3512 this.sourceSpan = sourceSpan;
3513 this.keySpan = keySpan;
3514 this.valueSpan = valueSpan;
3515 this.i18n = i18n;
3516 }
3517 static fromBoundElementProperty(prop, i18n) {
3518 if (prop.keySpan === undefined) {
3519 throw new Error(`Unexpected state: keySpan must be defined for bound attributes but was not for ${prop.name}: ${prop.sourceSpan}`);
3520 }
3521 return new BoundAttribute(prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n);
3522 }
3523 visit(visitor) {
3524 return visitor.visitBoundAttribute(this);
3525 }
3526 }
3527 class BoundEvent {
3528 constructor(name, type, handler, target, phase, sourceSpan, handlerSpan, keySpan) {
3529 this.name = name;
3530 this.type = type;
3531 this.handler = handler;
3532 this.target = target;
3533 this.phase = phase;
3534 this.sourceSpan = sourceSpan;
3535 this.handlerSpan = handlerSpan;
3536 this.keySpan = keySpan;
3537 }
3538 static fromParsedEvent(event) {
3539 const target = event.type === 0 /* Regular */ ? event.targetOrPhase : null;
3540 const phase = event.type === 1 /* Animation */ ? event.targetOrPhase : null;
3541 if (event.keySpan === undefined) {
3542 throw new Error(`Unexpected state: keySpan must be defined for bound event but was not for ${event.name}: ${event.sourceSpan}`);
3543 }
3544 return new BoundEvent(event.name, event.type, event.handler, target, phase, event.sourceSpan, event.handlerSpan, event.keySpan);
3545 }
3546 visit(visitor) {
3547 return visitor.visitBoundEvent(this);
3548 }
3549 }
3550 class Element {
3551 constructor(name, attributes, inputs, outputs, children, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
3552 this.name = name;
3553 this.attributes = attributes;
3554 this.inputs = inputs;
3555 this.outputs = outputs;
3556 this.children = children;
3557 this.references = references;
3558 this.sourceSpan = sourceSpan;
3559 this.startSourceSpan = startSourceSpan;
3560 this.endSourceSpan = endSourceSpan;
3561 this.i18n = i18n;
3562 }
3563 visit(visitor) {
3564 return visitor.visitElement(this);
3565 }
3566 }
3567 class Template {
3568 constructor(tagName, attributes, inputs, outputs, templateAttrs, children, references, variables, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
3569 this.tagName = tagName;
3570 this.attributes = attributes;
3571 this.inputs = inputs;
3572 this.outputs = outputs;
3573 this.templateAttrs = templateAttrs;
3574 this.children = children;
3575 this.references = references;
3576 this.variables = variables;
3577 this.sourceSpan = sourceSpan;
3578 this.startSourceSpan = startSourceSpan;
3579 this.endSourceSpan = endSourceSpan;
3580 this.i18n = i18n;
3581 }
3582 visit(visitor) {
3583 return visitor.visitTemplate(this);
3584 }
3585 }
3586 class Content {
3587 constructor(selector, attributes, sourceSpan, i18n) {
3588 this.selector = selector;
3589 this.attributes = attributes;
3590 this.sourceSpan = sourceSpan;
3591 this.i18n = i18n;
3592 this.name = 'ng-content';
3593 }
3594 visit(visitor) {
3595 return visitor.visitContent(this);
3596 }
3597 }
3598 class Variable {
3599 constructor(name, value, sourceSpan, keySpan, valueSpan) {
3600 this.name = name;
3601 this.value = value;
3602 this.sourceSpan = sourceSpan;
3603 this.keySpan = keySpan;
3604 this.valueSpan = valueSpan;
3605 }
3606 visit(visitor) {
3607 return visitor.visitVariable(this);
3608 }
3609 }
3610 class Reference {
3611 constructor(name, value, sourceSpan, keySpan, valueSpan) {
3612 this.name = name;
3613 this.value = value;
3614 this.sourceSpan = sourceSpan;
3615 this.keySpan = keySpan;
3616 this.valueSpan = valueSpan;
3617 }
3618 visit(visitor) {
3619 return visitor.visitReference(this);
3620 }
3621 }
3622 class Icu {
3623 constructor(vars, placeholders, sourceSpan, i18n) {
3624 this.vars = vars;
3625 this.placeholders = placeholders;
3626 this.sourceSpan = sourceSpan;
3627 this.i18n = i18n;
3628 }
3629 visit(visitor) {
3630 return visitor.visitIcu(this);
3631 }
3632 }
3633 class RecursiveVisitor {
3634 visitElement(element) {
3635 visitAll(this, element.attributes);
3636 visitAll(this, element.children);
3637 visitAll(this, element.references);
3638 }
3639 visitTemplate(template) {
3640 visitAll(this, template.attributes);
3641 visitAll(this, template.children);
3642 visitAll(this, template.references);
3643 visitAll(this, template.variables);
3644 }
3645 visitContent(content) { }
3646 visitVariable(variable) { }
3647 visitReference(reference) { }
3648 visitTextAttribute(attribute) { }
3649 visitBoundAttribute(attribute) { }
3650 visitBoundEvent(attribute) { }
3651 visitText(text) { }
3652 visitBoundText(text) { }
3653 visitIcu(icu) { }
3654 }
3655 function visitAll(visitor, nodes) {
3656 const result = [];
3657 if (visitor.visit) {
3658 for (const node of nodes) {
3659 const newNode = visitor.visit(node) || node.visit(visitor);
3660 }
3661 }
3662 else {
3663 for (const node of nodes) {
3664 const newNode = node.visit(visitor);
3665 if (newNode) {
3666 result.push(newNode);
3667 }
3668 }
3669 }
3670 return result;
3671 }
3672
3673 /**
3674 * @license
3675 * Copyright Google LLC All Rights Reserved.
3676 *
3677 * Use of this source code is governed by an MIT-style license that can be
3678 * found in the LICENSE file at https://angular.io/license
3679 */
3680 class Message {
3681 /**
3682 * @param nodes message AST
3683 * @param placeholders maps placeholder names to static content and their source spans
3684 * @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
3685 * @param meaning
3686 * @param description
3687 * @param customId
3688 */
3689 constructor(nodes, placeholders, placeholderToMessage, meaning, description, customId) {
3690 this.nodes = nodes;
3691 this.placeholders = placeholders;
3692 this.placeholderToMessage = placeholderToMessage;
3693 this.meaning = meaning;
3694 this.description = description;
3695 this.customId = customId;
3696 this.id = this.customId;
3697 /** The ids to use if there are no custom id and if `i18nLegacyMessageIdFormat` is not empty */
3698 this.legacyIds = [];
3699 if (nodes.length) {
3700 this.sources = [{
3701 filePath: nodes[0].sourceSpan.start.file.url,
3702 startLine: nodes[0].sourceSpan.start.line + 1,
3703 startCol: nodes[0].sourceSpan.start.col + 1,
3704 endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1,
3705 endCol: nodes[0].sourceSpan.start.col + 1
3706 }];
3707 }
3708 else {
3709 this.sources = [];
3710 }
3711 }
3712 }
3713 class Text$1 {
3714 constructor(value, sourceSpan) {
3715 this.value = value;
3716 this.sourceSpan = sourceSpan;
3717 }
3718 visit(visitor, context) {
3719 return visitor.visitText(this, context);
3720 }
3721 }
3722 // TODO(vicb): do we really need this node (vs an array) ?
3723 class Container {
3724 constructor(children, sourceSpan) {
3725 this.children = children;
3726 this.sourceSpan = sourceSpan;
3727 }
3728 visit(visitor, context) {
3729 return visitor.visitContainer(this, context);
3730 }
3731 }
3732 class Icu$1 {
3733 constructor(expression, type, cases, sourceSpan) {
3734 this.expression = expression;
3735 this.type = type;
3736 this.cases = cases;
3737 this.sourceSpan = sourceSpan;
3738 }
3739 visit(visitor, context) {
3740 return visitor.visitIcu(this, context);
3741 }
3742 }
3743 class TagPlaceholder {
3744 constructor(tag, attrs, startName, closeName, children, isVoid,
3745 // TODO sourceSpan should cover all (we need a startSourceSpan and endSourceSpan)
3746 sourceSpan, startSourceSpan, endSourceSpan) {
3747 this.tag = tag;
3748 this.attrs = attrs;
3749 this.startName = startName;
3750 this.closeName = closeName;
3751 this.children = children;
3752 this.isVoid = isVoid;
3753 this.sourceSpan = sourceSpan;
3754 this.startSourceSpan = startSourceSpan;
3755 this.endSourceSpan = endSourceSpan;
3756 }
3757 visit(visitor, context) {
3758 return visitor.visitTagPlaceholder(this, context);
3759 }
3760 }
3761 class Placeholder {
3762 constructor(value, name, sourceSpan) {
3763 this.value = value;
3764 this.name = name;
3765 this.sourceSpan = sourceSpan;
3766 }
3767 visit(visitor, context) {
3768 return visitor.visitPlaceholder(this, context);
3769 }
3770 }
3771 class IcuPlaceholder {
3772 constructor(value, name, sourceSpan) {
3773 this.value = value;
3774 this.name = name;
3775 this.sourceSpan = sourceSpan;
3776 }
3777 visit(visitor, context) {
3778 return visitor.visitIcuPlaceholder(this, context);
3779 }
3780 }
3781
3782 /**
3783 * @license
3784 * Copyright Google LLC All Rights Reserved.
3785 *
3786 * Use of this source code is governed by an MIT-style license that can be
3787 * found in the LICENSE file at https://angular.io/license
3788 */
3789 /**
3790 * Represents a big integer using a buffer of its individual digits, with the least significant
3791 * digit stored at the beginning of the array (little endian).
3792 *
3793 * For performance reasons, each instance is mutable. The addition operation can be done in-place
3794 * to reduce memory pressure of allocation for the digits array.
3795 */
3796 class BigInteger {
3797 /**
3798 * Creates a big integer using its individual digits in little endian storage.
3799 */
3800 constructor(digits) {
3801 this.digits = digits;
3802 }
3803 static zero() {
3804 return new BigInteger([0]);
3805 }
3806 static one() {
3807 return new BigInteger([1]);
3808 }
3809 /**
3810 * Creates a clone of this instance.
3811 */
3812 clone() {
3813 return new BigInteger(this.digits.slice());
3814 }
3815 /**
3816 * Returns a new big integer with the sum of `this` and `other` as its value. This does not mutate
3817 * `this` but instead returns a new instance, unlike `addToSelf`.
3818 */
3819 add(other) {
3820 const result = this.clone();
3821 result.addToSelf(other);
3822 return result;
3823 }
3824 /**
3825 * Adds `other` to the instance itself, thereby mutating its value.
3826 */
3827 addToSelf(other) {
3828 const maxNrOfDigits = Math.max(this.digits.length, other.digits.length);
3829 let carry = 0;
3830 for (let i = 0; i < maxNrOfDigits; i++) {
3831 let digitSum = carry;
3832 if (i < this.digits.length) {
3833 digitSum += this.digits[i];
3834 }
3835 if (i < other.digits.length) {
3836 digitSum += other.digits[i];
3837 }
3838 if (digitSum >= 10) {
3839 this.digits[i] = digitSum - 10;
3840 carry = 1;
3841 }
3842 else {
3843 this.digits[i] = digitSum;
3844 carry = 0;
3845 }
3846 }
3847 // Apply a remaining carry if needed.
3848 if (carry > 0) {
3849 this.digits[maxNrOfDigits] = 1;
3850 }
3851 }
3852 /**
3853 * Builds the decimal string representation of the big integer. As this is stored in
3854 * little endian, the digits are concatenated in reverse order.
3855 */
3856 toString() {
3857 let res = '';
3858 for (let i = this.digits.length - 1; i >= 0; i--) {
3859 res += this.digits[i];
3860 }
3861 return res;
3862 }
3863 }
3864 /**
3865 * Represents a big integer which is optimized for multiplication operations, as its power-of-twos
3866 * are memoized. See `multiplyBy()` for details on the multiplication algorithm.
3867 */
3868 class BigIntForMultiplication {
3869 constructor(value) {
3870 this.powerOfTwos = [value];
3871 }
3872 /**
3873 * Returns the big integer itself.
3874 */
3875 getValue() {
3876 return this.powerOfTwos[0];
3877 }
3878 /**
3879 * Computes the value for `num * b`, where `num` is a JS number and `b` is a big integer. The
3880 * value for `b` is represented by a storage model that is optimized for this computation.
3881 *
3882 * This operation is implemented in N(log2(num)) by continuous halving of the number, where the
3883 * least-significant bit (LSB) is tested in each iteration. If the bit is set, the bit's index is
3884 * used as exponent into the power-of-two multiplication of `b`.
3885 *
3886 * As an example, consider the multiplication num=42, b=1337. In binary 42 is 0b00101010 and the
3887 * algorithm unrolls into the following iterations:
3888 *
3889 * Iteration | num | LSB | b * 2^iter | Add? | product
3890 * -----------|------------|------|------------|------|--------
3891 * 0 | 0b00101010 | 0 | 1337 | No | 0
3892 * 1 | 0b00010101 | 1 | 2674 | Yes | 2674
3893 * 2 | 0b00001010 | 0 | 5348 | No | 2674
3894 * 3 | 0b00000101 | 1 | 10696 | Yes | 13370
3895 * 4 | 0b00000010 | 0 | 21392 | No | 13370
3896 * 5 | 0b00000001 | 1 | 42784 | Yes | 56154
3897 * 6 | 0b00000000 | 0 | 85568 | No | 56154
3898 *
3899 * The computed product of 56154 is indeed the correct result.
3900 *
3901 * The `BigIntForMultiplication` representation for a big integer provides memoized access to the
3902 * power-of-two values to reduce the workload in computing those values.
3903 */
3904 multiplyBy(num) {
3905 const product = BigInteger.zero();
3906 this.multiplyByAndAddTo(num, product);
3907 return product;
3908 }
3909 /**
3910 * See `multiplyBy()` for details. This function allows for the computed product to be added
3911 * directly to the provided result big integer.
3912 */
3913 multiplyByAndAddTo(num, result) {
3914 for (let exponent = 0; num !== 0; num = num >>> 1, exponent++) {
3915 if (num & 1) {
3916 const value = this.getMultipliedByPowerOfTwo(exponent);
3917 result.addToSelf(value);
3918 }
3919 }
3920 }
3921 /**
3922 * Computes and memoizes the big integer value for `this.number * 2^exponent`.
3923 */
3924 getMultipliedByPowerOfTwo(exponent) {
3925 // Compute the powers up until the requested exponent, where each value is computed from its
3926 // predecessor. This is simple as `this.number * 2^(exponent - 1)` only has to be doubled (i.e.
3927 // added to itself) to reach `this.number * 2^exponent`.
3928 for (let i = this.powerOfTwos.length; i <= exponent; i++) {
3929 const previousPower = this.powerOfTwos[i - 1];
3930 this.powerOfTwos[i] = previousPower.add(previousPower);
3931 }
3932 return this.powerOfTwos[exponent];
3933 }
3934 }
3935 /**
3936 * Represents an exponentiation operation for the provided base, of which exponents are computed and
3937 * memoized. The results are represented by a `BigIntForMultiplication` which is tailored for
3938 * multiplication operations by memoizing the power-of-twos. This effectively results in a matrix
3939 * representation that is lazily computed upon request.
3940 */
3941 class BigIntExponentiation {
3942 constructor(base) {
3943 this.base = base;
3944 this.exponents = [new BigIntForMultiplication(BigInteger.one())];
3945 }
3946 /**
3947 * Compute the value for `this.base^exponent`, resulting in a big integer that is optimized for
3948 * further multiplication operations.
3949 */
3950 toThePowerOf(exponent) {
3951 // Compute the results up until the requested exponent, where every value is computed from its
3952 // predecessor. This is because `this.base^(exponent - 1)` only has to be multiplied by `base`
3953 // to reach `this.base^exponent`.
3954 for (let i = this.exponents.length; i <= exponent; i++) {
3955 const value = this.exponents[i - 1].multiplyBy(this.base);
3956 this.exponents[i] = new BigIntForMultiplication(value);
3957 }
3958 return this.exponents[exponent];
3959 }
3960 }
3961
3962 /**
3963 * @license
3964 * Copyright Google LLC All Rights Reserved.
3965 *
3966 * Use of this source code is governed by an MIT-style license that can be
3967 * found in the LICENSE file at https://angular.io/license
3968 */
3969 /**
3970 * Compute the message id using the XLIFF1 digest.
3971 */
3972 function computeDigest(message) {
3973 return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
3974 }
3975 /**
3976 * Return the message id or compute it using the XLIFF2/XMB/$localize digest.
3977 */
3978 function decimalDigest(message) {
3979 return message.id || computeDecimalDigest(message);
3980 }
3981 /**
3982 * Compute the message id using the XLIFF2/XMB/$localize digest.
3983 */
3984 function computeDecimalDigest(message) {
3985 const visitor = new _SerializerIgnoreIcuExpVisitor();
3986 const parts = message.nodes.map(a => a.visit(visitor, null));
3987 return computeMsgId(parts.join(''), message.meaning);
3988 }
3989 /**
3990 * Serialize the i18n ast to something xml-like in order to generate an UID.
3991 *
3992 * The visitor is also used in the i18n parser tests
3993 *
3994 * @internal
3995 */
3996 class _SerializerVisitor {
3997 visitText(text, context) {
3998 return text.value;
3999 }
4000 visitContainer(container, context) {
4001 return `[${container.children.map(child => child.visit(this)).join(', ')}]`;
4002 }
4003 visitIcu(icu, context) {
4004 const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
4005 return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`;
4006 }
4007 visitTagPlaceholder(ph, context) {
4008 return ph.isVoid ?
4009 `<ph tag name="${ph.startName}"/>` :
4010 `<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`;
4011 }
4012 visitPlaceholder(ph, context) {
4013 return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`;
4014 }
4015 visitIcuPlaceholder(ph, context) {
4016 return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`;
4017 }
4018 }
4019 const serializerVisitor = new _SerializerVisitor();
4020 function serializeNodes(nodes) {
4021 return nodes.map(a => a.visit(serializerVisitor, null));
4022 }
4023 /**
4024 * Serialize the i18n ast to something xml-like in order to generate an UID.
4025 *
4026 * Ignore the ICU expressions so that message IDs stays identical if only the expression changes.
4027 *
4028 * @internal
4029 */
4030 class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor {
4031 visitIcu(icu, context) {
4032 let strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
4033 // Do not take the expression into account
4034 return `{${icu.type}, ${strCases.join(', ')}}`;
4035 }
4036 }
4037 /**
4038 * Compute the SHA1 of the given string
4039 *
4040 * see https://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
4041 *
4042 * WARNING: this function has not been designed not tested with security in mind.
4043 * DO NOT USE IT IN A SECURITY SENSITIVE CONTEXT.
4044 */
4045 function sha1(str) {
4046 const utf8 = utf8Encode(str);
4047 const words32 = bytesToWords32(utf8, Endian.Big);
4048 const len = utf8.length * 8;
4049 const w = newArray(80);
4050 let a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476, e = 0xc3d2e1f0;
4051 words32[len >> 5] |= 0x80 << (24 - len % 32);
4052 words32[((len + 64 >> 9) << 4) + 15] = len;
4053 for (let i = 0; i < words32.length; i += 16) {
4054 const h0 = a, h1 = b, h2 = c, h3 = d, h4 = e;
4055 for (let j = 0; j < 80; j++) {
4056 if (j < 16) {
4057 w[j] = words32[i + j];
4058 }
4059 else {
4060 w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
4061 }
4062 const fkVal = fk(j, b, c, d);
4063 const f = fkVal[0];
4064 const k = fkVal[1];
4065 const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32);
4066 e = d;
4067 d = c;
4068 c = rol32(b, 30);
4069 b = a;
4070 a = temp;
4071 }
4072 a = add32(a, h0);
4073 b = add32(b, h1);
4074 c = add32(c, h2);
4075 d = add32(d, h3);
4076 e = add32(e, h4);
4077 }
4078 return bytesToHexString(words32ToByteString([a, b, c, d, e]));
4079 }
4080 function fk(index, b, c, d) {
4081 if (index < 20) {
4082 return [(b & c) | (~b & d), 0x5a827999];
4083 }
4084 if (index < 40) {
4085 return [b ^ c ^ d, 0x6ed9eba1];
4086 }
4087 if (index < 60) {
4088 return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
4089 }
4090 return [b ^ c ^ d, 0xca62c1d6];
4091 }
4092 /**
4093 * Compute the fingerprint of the given string
4094 *
4095 * The output is 64 bit number encoded as a decimal string
4096 *
4097 * based on:
4098 * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java
4099 */
4100 function fingerprint(str) {
4101 const utf8 = utf8Encode(str);
4102 let hi = hash32(utf8, 0);
4103 let lo = hash32(utf8, 102072);
4104 if (hi == 0 && (lo == 0 || lo == 1)) {
4105 hi = hi ^ 0x130f9bef;
4106 lo = lo ^ -0x6b5f56d8;
4107 }
4108 return [hi, lo];
4109 }
4110 function computeMsgId(msg, meaning = '') {
4111 let msgFingerprint = fingerprint(msg);
4112 if (meaning) {
4113 const meaningFingerprint = fingerprint(meaning);
4114 msgFingerprint = add64(rol64(msgFingerprint, 1), meaningFingerprint);
4115 }
4116 const hi = msgFingerprint[0];
4117 const lo = msgFingerprint[1];
4118 return wordsToDecimalString(hi & 0x7fffffff, lo);
4119 }
4120 function hash32(bytes, c) {
4121 let a = 0x9e3779b9, b = 0x9e3779b9;
4122 let i;
4123 const len = bytes.length;
4124 for (i = 0; i + 12 <= len; i += 12) {
4125 a = add32(a, wordAt(bytes, i, Endian.Little));
4126 b = add32(b, wordAt(bytes, i + 4, Endian.Little));
4127 c = add32(c, wordAt(bytes, i + 8, Endian.Little));
4128 const res = mix(a, b, c);
4129 a = res[0], b = res[1], c = res[2];
4130 }
4131 a = add32(a, wordAt(bytes, i, Endian.Little));
4132 b = add32(b, wordAt(bytes, i + 4, Endian.Little));
4133 // the first byte of c is reserved for the length
4134 c = add32(c, len);
4135 c = add32(c, wordAt(bytes, i + 8, Endian.Little) << 8);
4136 return mix(a, b, c)[2];
4137 }
4138 // clang-format off
4139 function mix(a, b, c) {
4140 a = sub32(a, b);
4141 a = sub32(a, c);
4142 a ^= c >>> 13;
4143 b = sub32(b, c);
4144 b = sub32(b, a);
4145 b ^= a << 8;
4146 c = sub32(c, a);
4147 c = sub32(c, b);
4148 c ^= b >>> 13;
4149 a = sub32(a, b);
4150 a = sub32(a, c);
4151 a ^= c >>> 12;
4152 b = sub32(b, c);
4153 b = sub32(b, a);
4154 b ^= a << 16;
4155 c = sub32(c, a);
4156 c = sub32(c, b);
4157 c ^= b >>> 5;
4158 a = sub32(a, b);
4159 a = sub32(a, c);
4160 a ^= c >>> 3;
4161 b = sub32(b, c);
4162 b = sub32(b, a);
4163 b ^= a << 10;
4164 c = sub32(c, a);
4165 c = sub32(c, b);
4166 c ^= b >>> 15;
4167 return [a, b, c];
4168 }
4169 // clang-format on
4170 // Utils
4171 var Endian;
4172 (function (Endian) {
4173 Endian[Endian["Little"] = 0] = "Little";
4174 Endian[Endian["Big"] = 1] = "Big";
4175 })(Endian || (Endian = {}));
4176 function add32(a, b) {
4177 return add32to64(a, b)[1];
4178 }
4179 function add32to64(a, b) {
4180 const low = (a & 0xffff) + (b & 0xffff);
4181 const high = (a >>> 16) + (b >>> 16) + (low >>> 16);
4182 return [high >>> 16, (high << 16) | (low & 0xffff)];
4183 }
4184 function add64(a, b) {
4185 const ah = a[0], al = a[1];
4186 const bh = b[0], bl = b[1];
4187 const result = add32to64(al, bl);
4188 const carry = result[0];
4189 const l = result[1];
4190 const h = add32(add32(ah, bh), carry);
4191 return [h, l];
4192 }
4193 function sub32(a, b) {
4194 const low = (a & 0xffff) - (b & 0xffff);
4195 const high = (a >> 16) - (b >> 16) + (low >> 16);
4196 return (high << 16) | (low & 0xffff);
4197 }
4198 // Rotate a 32b number left `count` position
4199 function rol32(a, count) {
4200 return (a << count) | (a >>> (32 - count));
4201 }
4202 // Rotate a 64b number left `count` position
4203 function rol64(num, count) {
4204 const hi = num[0], lo = num[1];
4205 const h = (hi << count) | (lo >>> (32 - count));
4206 const l = (lo << count) | (hi >>> (32 - count));
4207 return [h, l];
4208 }
4209 function bytesToWords32(bytes, endian) {
4210 const size = (bytes.length + 3) >>> 2;
4211 const words32 = [];
4212 for (let i = 0; i < size; i++) {
4213 words32[i] = wordAt(bytes, i * 4, endian);
4214 }
4215 return words32;
4216 }
4217 function byteAt(bytes, index) {
4218 return index >= bytes.length ? 0 : bytes[index];
4219 }
4220 function wordAt(bytes, index, endian) {
4221 let word = 0;
4222 if (endian === Endian.Big) {
4223 for (let i = 0; i < 4; i++) {
4224 word += byteAt(bytes, index + i) << (24 - 8 * i);
4225 }
4226 }
4227 else {
4228 for (let i = 0; i < 4; i++) {
4229 word += byteAt(bytes, index + i) << 8 * i;
4230 }
4231 }
4232 return word;
4233 }
4234 function words32ToByteString(words32) {
4235 return words32.reduce((bytes, word) => bytes.concat(word32ToByteString(word)), []);
4236 }
4237 function word32ToByteString(word) {
4238 let bytes = [];
4239 for (let i = 0; i < 4; i++) {
4240 bytes.push((word >>> 8 * (3 - i)) & 0xff);
4241 }
4242 return bytes;
4243 }
4244 function bytesToHexString(bytes) {
4245 let hex = '';
4246 for (let i = 0; i < bytes.length; i++) {
4247 const b = byteAt(bytes, i);
4248 hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16);
4249 }
4250 return hex.toLowerCase();
4251 }
4252 /**
4253 * Create a shared exponentiation pool for base-256 computations. This shared pool provides memoized
4254 * power-of-256 results with memoized power-of-two computations for efficient multiplication.
4255 *
4256 * For our purposes, this can be safely stored as a global without memory concerns. The reason is
4257 * that we encode two words, so only need the 0th (for the low word) and 4th (for the high word)
4258 * exponent.
4259 */
4260 const base256 = new BigIntExponentiation(256);
4261 /**
4262 * Represents two 32-bit words as a single decimal number. This requires a big integer storage
4263 * model as JS numbers are not accurate enough to represent the 64-bit number.
4264 *
4265 * Based on https://www.danvk.org/hex2dec.html
4266 */
4267 function wordsToDecimalString(hi, lo) {
4268 // Encode the four bytes in lo in the lower digits of the decimal number.
4269 // Note: the multiplication results in lo itself but represented by a big integer using its
4270 // decimal digits.
4271 const decimal = base256.toThePowerOf(0).multiplyBy(lo);
4272 // Encode the four bytes in hi above the four lo bytes. lo is a maximum of (2^8)^4, which is why
4273 // this multiplication factor is applied.
4274 base256.toThePowerOf(4).multiplyByAndAddTo(hi, decimal);
4275 return decimal.toString();
4276 }
4277
4278 /**
4279 * @license
4280 * Copyright Google LLC All Rights Reserved.
4281 *
4282 * Use of this source code is governed by an MIT-style license that can be
4283 * found in the LICENSE file at https://angular.io/license
4284 */
4285 // XMB/XTB placeholders can only contain A-Z, 0-9 and _
4286 function toPublicName(internalName) {
4287 return internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
4288 }
4289
4290 /**
4291 * @license
4292 * Copyright Google LLC All Rights Reserved.
4293 *
4294 * Use of this source code is governed by an MIT-style license that can be
4295 * found in the LICENSE file at https://angular.io/license
4296 */
4297 /* Closure variables holding messages must be named `MSG_[A-Z0-9]+` */
4298 const CLOSURE_TRANSLATION_VAR_PREFIX = 'MSG_';
4299 /**
4300 * Prefix for non-`goog.getMsg` i18n-related vars.
4301 * Note: the prefix uses lowercase characters intentionally due to a Closure behavior that
4302 * considers variables like `I18N_0` as constants and throws an error when their value changes.
4303 */
4304 const TRANSLATION_VAR_PREFIX = 'i18n_';
4305 /** Name of the i18n attributes **/
4306 const I18N_ATTR = 'i18n';
4307 const I18N_ATTR_PREFIX = 'i18n-';
4308 /** Prefix of var expressions used in ICUs */
4309 const I18N_ICU_VAR_PREFIX = 'VAR_';
4310 /** Prefix of ICU expressions for post processing */
4311 const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
4312 /** Placeholder wrapper for i18n expressions **/
4313 const I18N_PLACEHOLDER_SYMBOL = '�';
4314 function isI18nAttribute(name) {
4315 return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX);
4316 }
4317 function isI18nRootNode(meta) {
4318 return meta instanceof Message;
4319 }
4320 function isSingleI18nIcu(meta) {
4321 return isI18nRootNode(meta) && meta.nodes.length === 1 && meta.nodes[0] instanceof Icu$1;
4322 }
4323 function hasI18nMeta(node) {
4324 return !!node.i18n;
4325 }
4326 function hasI18nAttrs(element) {
4327 return element.attrs.some((attr) => isI18nAttribute(attr.name));
4328 }
4329 function icuFromI18nMessage(message) {
4330 return message.nodes[0];
4331 }
4332 function wrapI18nPlaceholder(content, contextId = 0) {
4333 const blockId = contextId > 0 ? `:${contextId}` : '';
4334 return `${I18N_PLACEHOLDER_SYMBOL}${content}${blockId}${I18N_PLACEHOLDER_SYMBOL}`;
4335 }
4336 function assembleI18nBoundString(strings, bindingStartIndex = 0, contextId = 0) {
4337 if (!strings.length)
4338 return '';
4339 let acc = '';
4340 const lastIdx = strings.length - 1;
4341 for (let i = 0; i < lastIdx; i++) {
4342 acc += `${strings[i]}${wrapI18nPlaceholder(bindingStartIndex + i, contextId)}`;
4343 }
4344 acc += strings[lastIdx];
4345 return acc;
4346 }
4347 function getSeqNumberGenerator(startsAt = 0) {
4348 let current = startsAt;
4349 return () => current++;
4350 }
4351 function placeholdersToParams(placeholders) {
4352 const params = {};
4353 placeholders.forEach((values, key) => {
4354 params[key] = literal(values.length > 1 ? `[${values.join('|')}]` : values[0]);
4355 });
4356 return params;
4357 }
4358 function updatePlaceholderMap(map, name, ...values) {
4359 const current = map.get(name) || [];
4360 current.push(...values);
4361 map.set(name, current);
4362 }
4363 function assembleBoundTextPlaceholders(meta, bindingStartIndex = 0, contextId = 0) {
4364 const startIdx = bindingStartIndex;
4365 const placeholders = new Map();
4366 const node = meta instanceof Message ? meta.nodes.find(node => node instanceof Container) : meta;
4367 if (node) {
4368 node
4369 .children
4370 .filter((child) => child instanceof Placeholder)
4371 .forEach((child, idx) => {
4372 const content = wrapI18nPlaceholder(startIdx + idx, contextId);
4373 updatePlaceholderMap(placeholders, child.name, content);
4374 });
4375 }
4376 return placeholders;
4377 }
4378 /**
4379 * Format the placeholder names in a map of placeholders to expressions.
4380 *
4381 * The placeholder names are converted from "internal" format (e.g. `START_TAG_DIV_1`) to "external"
4382 * format (e.g. `startTagDiv_1`).
4383 *
4384 * @param params A map of placeholder names to expressions.
4385 * @param useCamelCase whether to camelCase the placeholder name when formatting.
4386 * @returns A new map of formatted placeholder names to expressions.
4387 */
4388 function i18nFormatPlaceholderNames(params = {}, useCamelCase) {
4389 const _params = {};
4390 if (params && Object.keys(params).length) {
4391 Object.keys(params).forEach(key => _params[formatI18nPlaceholderName(key, useCamelCase)] = params[key]);
4392 }
4393 return _params;
4394 }
4395 /**
4396 * Converts internal placeholder names to public-facing format
4397 * (for example to use in goog.getMsg call).
4398 * Example: `START_TAG_DIV_1` is converted to `startTagDiv_1`.
4399 *
4400 * @param name The placeholder name that should be formatted
4401 * @returns Formatted placeholder name
4402 */
4403 function formatI18nPlaceholderName(name, useCamelCase = true) {
4404 const publicName = toPublicName(name);
4405 if (!useCamelCase) {
4406 return publicName;
4407 }
4408 const chunks = publicName.split('_');
4409 if (chunks.length === 1) {
4410 // if no "_" found - just lowercase the value
4411 return name.toLowerCase();
4412 }
4413 let postfix;
4414 // eject last element if it's a number
4415 if (/^\d+$/.test(chunks[chunks.length - 1])) {
4416 postfix = chunks.pop();
4417 }
4418 let raw = chunks.shift().toLowerCase();
4419 if (chunks.length) {
4420 raw += chunks.map(c => c.charAt(0).toUpperCase() + c.slice(1).toLowerCase()).join('');
4421 }
4422 return postfix ? `${raw}_${postfix}` : raw;
4423 }
4424 /**
4425 * Generates a prefix for translation const name.
4426 *
4427 * @param extra Additional local prefix that should be injected into translation var name
4428 * @returns Complete translation const prefix
4429 */
4430 function getTranslationConstPrefix(extra) {
4431 return `${CLOSURE_TRANSLATION_VAR_PREFIX}${extra}`.toUpperCase();
4432 }
4433 /**
4434 * Generate AST to declare a variable. E.g. `var I18N_1;`.
4435 * @param variable the name of the variable to declare.
4436 */
4437 function declareI18nVariable(variable) {
4438 return new DeclareVarStmt(variable.name, undefined, INFERRED_TYPE, undefined, variable.sourceSpan);
4439 }
4440
4441 /**
4442 * @license
4443 * Copyright Google LLC All Rights Reserved.
4444 *
4445 * Use of this source code is governed by an MIT-style license that can be
4446 * found in the LICENSE file at https://angular.io/license
4447 */
4448 /**
4449 * Checks whether an object key contains potentially unsafe chars, thus the key should be wrapped in
4450 * quotes. Note: we do not wrap all keys into quotes, as it may have impact on minification and may
4451 * bot work in some cases when object keys are mangled by minifier.
4452 *
4453 * TODO(FW-1136): this is a temporary solution, we need to come up with a better way of working with
4454 * inputs that contain potentially unsafe chars.
4455 */
4456 const UNSAFE_OBJECT_KEY_NAME_REGEXP = /[-.]/;
4457 /** Name of the temporary to use during data binding */
4458 const TEMPORARY_NAME = '_t';
4459 /** Name of the context parameter passed into a template function */
4460 const CONTEXT_NAME = 'ctx';
4461 /** Name of the RenderFlag passed into a template function */
4462 const RENDER_FLAGS = 'rf';
4463 /** The prefix reference variables */
4464 const REFERENCE_PREFIX = '_r';
4465 /** The name of the implicit context reference */
4466 const IMPLICIT_REFERENCE = '$implicit';
4467 /** Non bindable attribute name **/
4468 const NON_BINDABLE_ATTR = 'ngNonBindable';
4469 /**
4470 * Creates an allocator for a temporary variable.
4471 *
4472 * A variable declaration is added to the statements the first time the allocator is invoked.
4473 */
4474 function temporaryAllocator(statements, name) {
4475 let temp = null;
4476 return () => {
4477 if (!temp) {
4478 statements.push(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE));
4479 temp = variable(name);
4480 }
4481 return temp;
4482 };
4483 }
4484 function unsupported(feature) {
4485 if (this) {
4486 throw new Error(`Builder ${this.constructor.name} doesn't support ${feature} yet`);
4487 }
4488 throw new Error(`Feature ${feature} is not supported yet`);
4489 }
4490 function invalid$1(arg) {
4491 throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
4492 }
4493 function asLiteral(value) {
4494 if (Array.isArray(value)) {
4495 return literalArr(value.map(asLiteral));
4496 }
4497 return literal(value, INFERRED_TYPE);
4498 }
4499 function conditionallyCreateMapObjectLiteral(keys, keepDeclared) {
4500 if (Object.getOwnPropertyNames(keys).length > 0) {
4501 return mapToExpression(keys, keepDeclared);
4502 }
4503 return null;
4504 }
4505 function mapToExpression(map, keepDeclared) {
4506 return literalMap(Object.getOwnPropertyNames(map).map(key => {
4507 // canonical syntax: `dirProp: publicProp`
4508 // if there is no `:`, use dirProp = elProp
4509 const value = map[key];
4510 let declaredName;
4511 let publicName;
4512 let minifiedName;
4513 let needsDeclaredName;
4514 if (Array.isArray(value)) {
4515 [publicName, declaredName] = value;
4516 minifiedName = key;
4517 needsDeclaredName = publicName !== declaredName;
4518 }
4519 else {
4520 [declaredName, publicName] = splitAtColon(key, [key, value]);
4521 minifiedName = declaredName;
4522 // Only include the declared name if extracted from the key, i.e. the key contains a colon.
4523 // Otherwise the declared name should be omitted even if it is different from the public name,
4524 // as it may have already been minified.
4525 needsDeclaredName = publicName !== declaredName && key.includes(':');
4526 }
4527 return {
4528 key: minifiedName,
4529 // put quotes around keys that contain potentially unsafe characters
4530 quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(minifiedName),
4531 value: (keepDeclared && needsDeclaredName) ?
4532 literalArr([asLiteral(publicName), asLiteral(declaredName)]) :
4533 asLiteral(publicName)
4534 };
4535 }));
4536 }
4537 /**
4538 * Remove trailing null nodes as they are implied.
4539 */
4540 function trimTrailingNulls(parameters) {
4541 while (isNull(parameters[parameters.length - 1])) {
4542 parameters.pop();
4543 }
4544 return parameters;
4545 }
4546 function getQueryPredicate(query, constantPool) {
4547 if (Array.isArray(query.predicate)) {
4548 let predicate = [];
4549 query.predicate.forEach((selector) => {
4550 // Each item in predicates array may contain strings with comma-separated refs
4551 // (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them
4552 // as separate array entities
4553 const selectors = selector.split(',').map(token => literal(token.trim()));
4554 predicate.push(...selectors);
4555 });
4556 return constantPool.getConstLiteral(literalArr(predicate), true);
4557 }
4558 else {
4559 return query.predicate;
4560 }
4561 }
4562 /**
4563 * A representation for an object literal used during codegen of definition objects. The generic
4564 * type `T` allows to reference a documented type of the generated structure, such that the
4565 * property names that are set can be resolved to their documented declaration.
4566 */
4567 class DefinitionMap {
4568 constructor() {
4569 this.values = [];
4570 }
4571 set(key, value) {
4572 if (value) {
4573 this.values.push({ key: key, value, quoted: false });
4574 }
4575 }
4576 toLiteralMap() {
4577 return literalMap(this.values);
4578 }
4579 }
4580 /**
4581 * Extract a map of properties to values for a given element or template node, which can be used
4582 * by the directive matching machinery.
4583 *
4584 * @param elOrTpl the element or template in question
4585 * @return an object set up for directive matching. For attributes on the element/template, this
4586 * object maps a property name to its (static) value. For any bindings, this map simply maps the
4587 * property name to an empty string.
4588 */
4589 function getAttrsForDirectiveMatching(elOrTpl) {
4590 const attributesMap = {};
4591 if (elOrTpl instanceof Template && elOrTpl.tagName !== 'ng-template') {
4592 elOrTpl.templateAttrs.forEach(a => attributesMap[a.name] = '');
4593 }
4594 else {
4595 elOrTpl.attributes.forEach(a => {
4596 if (!isI18nAttribute(a.name)) {
4597 attributesMap[a.name] = a.value;
4598 }
4599 });
4600 elOrTpl.inputs.forEach(i => {
4601 attributesMap[i.name] = '';
4602 });
4603 elOrTpl.outputs.forEach(o => {
4604 attributesMap[o.name] = '';
4605 });
4606 }
4607 return attributesMap;
4608 }
4609 /** Returns a call expression to a chained instruction, e.g. `property(params[0])(params[1])`. */
4610 function chainedInstruction(reference, calls, span) {
4611 let expression = importExpr(reference, null, span);
4612 if (calls.length > 0) {
4613 for (let i = 0; i < calls.length; i++) {
4614 expression = expression.callFn(calls[i], span);
4615 }
4616 }
4617 else {
4618 // Add a blank invocation, in case the `calls` array is empty.
4619 expression = expression.callFn([], span);
4620 }
4621 return expression;
4622 }
4623 /**
4624 * Gets the number of arguments expected to be passed to a generated instruction in the case of
4625 * interpolation instructions.
4626 * @param interpolation An interpolation ast
4627 */
4628 function getInterpolationArgsLength(interpolation) {
4629 const { expressions, strings } = interpolation;
4630 if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
4631 // If the interpolation has one interpolated value, but the prefix and suffix are both empty
4632 // strings, we only pass one argument, to a special instruction like `propertyInterpolate` or
4633 // `textInterpolate`.
4634 return 1;
4635 }
4636 else {
4637 return expressions.length + strings.length;
4638 }
4639 }
4640
4641 /**
4642 * @license
4643 * Copyright Google LLC All Rights Reserved.
4644 *
4645 * Use of this source code is governed by an MIT-style license that can be
4646 * found in the LICENSE file at https://angular.io/license
4647 */
4648 var R3FactoryDelegateType;
4649 (function (R3FactoryDelegateType) {
4650 R3FactoryDelegateType[R3FactoryDelegateType["Class"] = 0] = "Class";
4651 R3FactoryDelegateType[R3FactoryDelegateType["Function"] = 1] = "Function";
4652 R3FactoryDelegateType[R3FactoryDelegateType["Factory"] = 2] = "Factory";
4653 })(R3FactoryDelegateType || (R3FactoryDelegateType = {}));
4654 var R3FactoryTarget;
4655 (function (R3FactoryTarget) {
4656 R3FactoryTarget[R3FactoryTarget["Directive"] = 0] = "Directive";
4657 R3FactoryTarget[R3FactoryTarget["Component"] = 1] = "Component";
4658 R3FactoryTarget[R3FactoryTarget["Injectable"] = 2] = "Injectable";
4659 R3FactoryTarget[R3FactoryTarget["Pipe"] = 3] = "Pipe";
4660 R3FactoryTarget[R3FactoryTarget["NgModule"] = 4] = "NgModule";
4661 })(R3FactoryTarget || (R3FactoryTarget = {}));
4662 /**
4663 * Resolved type of a dependency.
4664 *
4665 * Occasionally, dependencies will have special significance which is known statically. In that
4666 * case the `R3ResolvedDependencyType` informs the factory generator that a particular dependency
4667 * should be generated specially (usually by calling a special injection function instead of the
4668 * standard one).
4669 */
4670 var R3ResolvedDependencyType;
4671 (function (R3ResolvedDependencyType) {
4672 /**
4673 * A normal token dependency.
4674 */
4675 R3ResolvedDependencyType[R3ResolvedDependencyType["Token"] = 0] = "Token";
4676 /**
4677 * The dependency is for an attribute.
4678 *
4679 * The token expression is a string representing the attribute name.
4680 */
4681 R3ResolvedDependencyType[R3ResolvedDependencyType["Attribute"] = 1] = "Attribute";
4682 /**
4683 * Injecting the `ChangeDetectorRef` token. Needs special handling when injected into a pipe.
4684 */
4685 R3ResolvedDependencyType[R3ResolvedDependencyType["ChangeDetectorRef"] = 2] = "ChangeDetectorRef";
4686 /**
4687 * An invalid dependency (no token could be determined). An error should be thrown at runtime.
4688 */
4689 R3ResolvedDependencyType[R3ResolvedDependencyType["Invalid"] = 3] = "Invalid";
4690 })(R3ResolvedDependencyType || (R3ResolvedDependencyType = {}));
4691 /**
4692 * Construct a factory function expression for the given `R3FactoryMetadata`.
4693 */
4694 function compileFactoryFunction(meta) {
4695 const t = variable('t');
4696 const statements = [];
4697 let ctorDepsType = NONE_TYPE;
4698 // The type to instantiate via constructor invocation. If there is no delegated factory, meaning
4699 // this type is always created by constructor invocation, then this is the type-to-create
4700 // parameter provided by the user (t) if specified, or the current type if not. If there is a
4701 // delegated factory (which is used to create the current type) then this is only the type-to-
4702 // create parameter (t).
4703 const typeForCtor = !isDelegatedMetadata(meta) ?
4704 new BinaryOperatorExpr(BinaryOperator.Or, t, meta.internalType) :
4705 t;
4706 let ctorExpr = null;
4707 if (meta.deps !== null) {
4708 // There is a constructor (either explicitly or implicitly defined).
4709 if (meta.deps !== 'invalid') {
4710 ctorExpr = new InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.injectFn, meta.target === R3FactoryTarget.Pipe));
4711 ctorDepsType = createCtorDepsType(meta.deps);
4712 }
4713 }
4714 else {
4715 const baseFactory = variable(${meta.name}_BaseFactory`);
4716 const getInheritedFactory = importExpr(Identifiers$1.getInheritedFactory);
4717 const baseFactoryStmt = baseFactory
4718 .set(getInheritedFactory.callFn([meta.internalType], /* sourceSpan */ undefined, /* pure */ true))
4719 .toDeclStmt(INFERRED_TYPE, [StmtModifier.Exported, StmtModifier.Final]);
4720 statements.push(baseFactoryStmt);
4721 // There is no constructor, use the base class' factory to construct typeForCtor.
4722 ctorExpr = baseFactory.callFn([typeForCtor]);
4723 }
4724 const ctorExprFinal = ctorExpr;
4725 const body = [];
4726 let retExpr = null;
4727 function makeConditionalFactory(nonCtorExpr) {
4728 const r = variable('r');
4729 body.push(r.set(NULL_EXPR).toDeclStmt());
4730 let ctorStmt = null;
4731 if (ctorExprFinal !== null) {
4732 ctorStmt = r.set(ctorExprFinal).toStmt();
4733 }
4734 else {
4735 ctorStmt = importExpr(Identifiers$1.invalidFactory).callFn([]).toStmt();
4736 }
4737 body.push(ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()]));
4738 return r;
4739 }
4740 if (isDelegatedMetadata(meta) && meta.delegateType === R3FactoryDelegateType.Factory) {
4741 const delegateFactory = variable(${meta.name}_BaseFactory`);
4742 const getFactoryOf = importExpr(Identifiers$1.getFactoryOf);
4743 if (meta.delegate.isEquivalent(meta.internalType)) {
4744 throw new Error(`Illegal state: compiling factory that delegates to itself`);
4745 }
4746 const delegateFactoryStmt = delegateFactory.set(getFactoryOf.callFn([meta.delegate])).toDeclStmt(INFERRED_TYPE, [
4747 StmtModifier.Exported, StmtModifier.Final
4748 ]);
4749 statements.push(delegateFactoryStmt);
4750 retExpr = makeConditionalFactory(delegateFactory.callFn([]));
4751 }
4752 else if (isDelegatedMetadata(meta)) {
4753 // This type is created with a delegated factory. If a type parameter is not specified, call
4754 // the factory instead.
4755 const delegateArgs = injectDependencies(meta.delegateDeps, meta.injectFn, meta.target === R3FactoryTarget.Pipe);
4756 // Either call `new delegate(...)` or `delegate(...)` depending on meta.delegateType.
4757 const factoryExpr = new (meta.delegateType === R3FactoryDelegateType.Class ?
4758 InstantiateExpr :
4759 InvokeFunctionExpr)(meta.delegate, delegateArgs);
4760 retExpr = makeConditionalFactory(factoryExpr);
4761 }
4762 else if (isExpressionFactoryMetadata(meta)) {
4763 // TODO(alxhub): decide whether to lower the value here or in the caller
4764 retExpr = makeConditionalFactory(meta.expression);
4765 }
4766 else {
4767 retExpr = ctorExpr;
4768 }
4769 if (retExpr !== null) {
4770 body.push(new ReturnStatement(retExpr));
4771 }
4772 else {
4773 body.push(importExpr(Identifiers$1.invalidFactory).callFn([]).toStmt());
4774 }
4775 return {
4776 factory: fn([new FnParam('t', DYNAMIC_TYPE)], body, INFERRED_TYPE, undefined, `${meta.name}_Factory`),
4777 statements,
4778 type: expressionType(importExpr(Identifiers$1.FactoryDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount), ctorDepsType]))
4779 };
4780 }
4781 function injectDependencies(deps, injectFn, isPipe) {
4782 return deps.map((dep, index) => compileInjectDependency(dep, injectFn, isPipe, index));
4783 }
4784 function compileInjectDependency(dep, injectFn, isPipe, index) {
4785 // Interpret the dependency according to its resolved type.
4786 switch (dep.resolved) {
4787 case R3ResolvedDependencyType.Token:
4788 case R3ResolvedDependencyType.ChangeDetectorRef:
4789 // Build up the injection flags according to the metadata.
4790 const flags = 0 /* Default */ | (dep.self ? 2 /* Self */ : 0) |
4791 (dep.skipSelf ? 4 /* SkipSelf */ : 0) | (dep.host ? 1 /* Host */ : 0) |
4792 (dep.optional ? 8 /* Optional */ : 0);
4793 // If this dependency is optional or otherwise has non-default flags, then additional
4794 // parameters describing how to inject the dependency must be passed to the inject function
4795 // that's being used.
4796 let flagsParam = (flags !== 0 /* Default */ || dep.optional) ? literal(flags) : null;
4797 // We have a separate instruction for injecting ChangeDetectorRef into a pipe.
4798 if (isPipe && dep.resolved === R3ResolvedDependencyType.ChangeDetectorRef) {
4799 return importExpr(Identifiers$1.injectPipeChangeDetectorRef).callFn(flagsParam ? [flagsParam] : []);
4800 }
4801 // Build up the arguments to the injectFn call.
4802 const injectArgs = [dep.token];
4803 if (flagsParam) {
4804 injectArgs.push(flagsParam);
4805 }
4806 return importExpr(injectFn).callFn(injectArgs);
4807 case R3ResolvedDependencyType.Attribute:
4808 // In the case of attributes, the attribute name in question is given as the token.
4809 return importExpr(Identifiers$1.injectAttribute).callFn([dep.token]);
4810 case R3ResolvedDependencyType.Invalid:
4811 return importExpr(Identifiers$1.invalidFactoryDep).callFn([literal(index)]);
4812 default:
4813 return unsupported(`Unknown R3ResolvedDependencyType: ${R3ResolvedDependencyType[dep.resolved]}`);
4814 }
4815 }
4816 function createCtorDepsType(deps) {
4817 let hasTypes = false;
4818 const attributeTypes = deps.map(dep => {
4819 const type = createCtorDepType(dep);
4820 if (type !== null) {
4821 hasTypes = true;
4822 return type;
4823 }
4824 else {
4825 return literal(null);
4826 }
4827 });
4828 if (hasTypes) {
4829 return expressionType(literalArr(attributeTypes));
4830 }
4831 else {
4832 return NONE_TYPE;
4833 }
4834 }
4835 function createCtorDepType(dep) {
4836 const entries = [];
4837 if (dep.resolved === R3ResolvedDependencyType.Attribute) {
4838 if (dep.attribute !== null) {
4839 entries.push({ key: 'attribute', value: dep.attribute, quoted: false });
4840 }
4841 }
4842 if (dep.optional) {
4843 entries.push({ key: 'optional', value: literal(true), quoted: false });
4844 }
4845 if (dep.host) {
4846 entries.push({ key: 'host', value: literal(true), quoted: false });
4847 }
4848 if (dep.self) {
4849 entries.push({ key: 'self', value: literal(true), quoted: false });
4850 }
4851 if (dep.skipSelf) {
4852 entries.push({ key: 'skipSelf', value: literal(true), quoted: false });
4853 }
4854 return entries.length > 0 ? literalMap(entries) : null;
4855 }
4856 function isDelegatedMetadata(meta) {
4857 return meta.delegateType !== undefined;
4858 }
4859 function isExpressionFactoryMetadata(meta) {
4860 return meta.expression !== undefined;
4861 }
4862
4863 /**
4864 * @license
4865 * Copyright Google LLC All Rights Reserved.
4866 *
4867 * Use of this source code is governed by an MIT-style license that can be
4868 * found in the LICENSE file at https://angular.io/license
4869 */
4870 function compileInjectable(meta) {
4871 let result = null;
4872 const factoryMeta = {
4873 name: meta.name,
4874 type: meta.type,
4875 internalType: meta.internalType,
4876 typeArgumentCount: meta.typeArgumentCount,
4877 deps: [],
4878 injectFn: Identifiers.inject,
4879 target: R3FactoryTarget.Injectable,
4880 };
4881 if (meta.useClass !== undefined) {
4882 // meta.useClass has two modes of operation. Either deps are specified, in which case `new` is
4883 // used to instantiate the class with dependencies injected, or deps are not specified and
4884 // the factory of the class is used to instantiate it.
4885 //
4886 // A special case exists for useClass: Type where Type is the injectable type itself and no
4887 // deps are specified, in which case 'useClass' is effectively ignored.
4888 const useClassOnSelf = meta.useClass.isEquivalent(meta.internalType);
4889 let deps = undefined;
4890 if (meta.userDeps !== undefined) {
4891 deps = meta.userDeps;
4892 }
4893 if (deps !== undefined) {
4894 // factory: () => new meta.useClass(...deps)
4895 result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { delegate: meta.useClass, delegateDeps: deps, delegateType: R3FactoryDelegateType.Class }));
4896 }
4897 else if (useClassOnSelf) {
4898 result = compileFactoryFunction(factoryMeta);
4899 }
4900 else {
4901 result = delegateToFactory(meta.type.value, meta.useClass);
4902 }
4903 }
4904 else if (meta.useFactory !== undefined) {
4905 if (meta.userDeps !== undefined) {
4906 result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { delegate: meta.useFactory, delegateDeps: meta.userDeps || [], delegateType: R3FactoryDelegateType.Function }));
4907 }
4908 else {
4909 result = {
4910 statements: [],
4911 factory: fn([], [new ReturnStatement(meta.useFactory.callFn([]))])
4912 };
4913 }
4914 }
4915 else if (meta.useValue !== undefined) {
4916 // Note: it's safe to use `meta.useValue` instead of the `USE_VALUE in meta` check used for
4917 // client code because meta.useValue is an Expression which will be defined even if the actual
4918 // value is undefined.
4919 result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { expression: meta.useValue }));
4920 }
4921 else if (meta.useExisting !== undefined) {
4922 // useExisting is an `inject` call on the existing token.
4923 result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { expression: importExpr(Identifiers.inject).callFn([meta.useExisting]) }));
4924 }
4925 else {
4926 result = delegateToFactory(meta.type.value, meta.internalType);
4927 }
4928 const token = meta.internalType;
4929 const injectableProps = { token, factory: result.factory };
4930 // Only generate providedIn property if it has a non-null value
4931 if (meta.providedIn.value !== null) {
4932 injectableProps.providedIn = meta.providedIn;
4933 }
4934 const expression = importExpr(Identifiers.ɵɵdefineInjectable).callFn([mapToMapExpression(injectableProps)]);
4935 const type = new ExpressionType(importExpr(Identifiers.InjectableDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount)]));
4936 return {
4937 expression,
4938 type,
4939 statements: result.statements,
4940 };
4941 }
4942 function delegateToFactory(type, internalType) {
4943 return {
4944 statements: [],
4945 // If types are the same, we can generate `factory: type.ɵfac`
4946 // If types are different, we have to generate a wrapper function to ensure
4947 // the internal type has been resolved (`factory: function(t) { return type.ɵfac(t); }`)
4948 factory: type.node === internalType.node ?
4949 internalType.prop('ɵfac') :
4950 fn([new FnParam('t', DYNAMIC_TYPE)], [new ReturnStatement(internalType.callMethod('ɵfac', [variable('t')]))])
4951 };
4952 }
4953
4954 /**
4955 * @license
4956 * Copyright Google LLC All Rights Reserved.
4957 *
4958 * Use of this source code is governed by an MIT-style license that can be
4959 * found in the LICENSE file at https://angular.io/license
4960 */
4961 const UNUSABLE_INTERPOLATION_REGEXPS = [
4962 /^\s*$/,
4963 /[<>]/,
4964 /^[{}]$/,
4965 /&(#|[a-z])/i,
4966 /^\/\//,
4967 ];
4968 function assertInterpolationSymbols(identifier, value) {
4969 if (value != null && !(Array.isArray(value) && value.length == 2)) {
4970 throw new Error(`Expected '${identifier}' to be an array, [start, end].`);
4971 }
4972 else if (value != null) {
4973 const start = value[0];
4974 const end = value[1];
4975 // Check for unusable interpolation symbols
4976 UNUSABLE_INTERPOLATION_REGEXPS.forEach(regexp => {
4977 if (regexp.test(start) || regexp.test(end)) {
4978 throw new Error(`['${start}', '${end}'] contains unusable interpolation symbol.`);
4979 }
4980 });
4981 }
4982 }
4983
4984 /**
4985 * @license
4986 * Copyright Google LLC All Rights Reserved.
4987 *
4988 * Use of this source code is governed by an MIT-style license that can be
4989 * found in the LICENSE file at https://angular.io/license
4990 */
4991 class InterpolationConfig {
4992 constructor(start, end) {
4993 this.start = start;
4994 this.end = end;
4995 }
4996 static fromArray(markers) {
4997 if (!markers) {
4998 return DEFAULT_INTERPOLATION_CONFIG;
4999 }
5000 assertInterpolationSymbols('interpolation', markers);
5001 return new InterpolationConfig(markers[0], markers[1]);
5002 }
5003 }
5004 const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}');
5005
5006 /**
5007 * @license
5008 * Copyright Google LLC All Rights Reserved.
5009 *
5010 * Use of this source code is governed by an MIT-style license that can be
5011 * found in the LICENSE file at https://angular.io/license
5012 */
5013 /**
5014 * In TypeScript, tagged template functions expect a "template object", which is an array of
5015 * "cooked" strings plus a `raw` property that contains an array of "raw" strings. This is
5016 * typically constructed with a function called `__makeTemplateObject(cooked, raw)`, but it may not
5017 * be available in all environments.
5018 *
5019 * This is a JavaScript polyfill that uses __makeTemplateObject when it's available, but otherwise
5020 * creates an inline helper with the same functionality.
5021 *
5022 * In the inline function, if `Object.defineProperty` is available we use that to attach the `raw`
5023 * array.
5024 */
5025 const makeTemplateObjectPolyfill = '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})';
5026 class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
5027 constructor() {
5028 super(false);
5029 }
5030 visitDeclareClassStmt(stmt, ctx) {
5031 ctx.pushClass(stmt);
5032 this._visitClassConstructor(stmt, ctx);
5033 if (stmt.parent != null) {
5034 ctx.print(stmt, `${stmt.name}.prototype = Object.create(`);
5035 stmt.parent.visitExpression(this, ctx);
5036 ctx.println(stmt, `.prototype);`);
5037 }
5038 stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx));
5039 stmt.methods.forEach((method) => this._visitClassMethod(stmt, method, ctx));
5040 ctx.popClass();
5041 return null;
5042 }
5043 _visitClassConstructor(stmt, ctx) {
5044 ctx.print(stmt, `function ${stmt.name}(`);
5045 if (stmt.constructorMethod != null) {
5046 this._visitParams(stmt.constructorMethod.params, ctx);
5047 }
5048 ctx.println(stmt, `) {`);
5049 ctx.incIndent();
5050 if (stmt.constructorMethod != null) {
5051 if (stmt.constructorMethod.body.length > 0) {
5052 ctx.println(stmt, `var self = this;`);
5053 this.visitAllStatements(stmt.constructorMethod.body, ctx);
5054 }
5055 }
5056 ctx.decIndent();
5057 ctx.println(stmt, `}`);
5058 }
5059 _visitClassGetter(stmt, getter, ctx) {
5060 ctx.println(stmt, `Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`);
5061 ctx.incIndent();
5062 if (getter.body.length > 0) {
5063 ctx.println(stmt, `var self = this;`);
5064 this.visitAllStatements(getter.body, ctx);
5065 }
5066 ctx.decIndent();
5067 ctx.println(stmt, `}});`);
5068 }
5069 _visitClassMethod(stmt, method, ctx) {
5070 ctx.print(stmt, `${stmt.name}.prototype.${method.name} = function(`);
5071 this._visitParams(method.params, ctx);
5072 ctx.println(stmt, `) {`);
5073 ctx.incIndent();
5074 if (method.body.length > 0) {
5075 ctx.println(stmt, `var self = this;`);
5076 this.visitAllStatements(method.body, ctx);
5077 }
5078 ctx.decIndent();
5079 ctx.println(stmt, `};`);
5080 }
5081 visitWrappedNodeExpr(ast, ctx) {
5082 throw new Error('Cannot emit a WrappedNodeExpr in Javascript.');
5083 }
5084 visitReadVarExpr(ast, ctx) {
5085 if (ast.builtin === BuiltinVar.This) {
5086 ctx.print(ast, 'self');
5087 }
5088 else if (ast.builtin === BuiltinVar.Super) {
5089 throw new Error(`'super' needs to be handled at a parent ast node, not at the variable level!`);
5090 }
5091 else {
5092 super.visitReadVarExpr(ast, ctx);
5093 }
5094 return null;
5095 }
5096 visitDeclareVarStmt(stmt, ctx) {
5097 ctx.print(stmt, `var ${stmt.name}`);
5098 if (stmt.value) {
5099 ctx.print(stmt, ' = ');
5100 stmt.value.visitExpression(this, ctx);
5101 }
5102 ctx.println(stmt, `;`);
5103 return null;
5104 }
5105 visitCastExpr(ast, ctx) {
5106 ast.value.visitExpression(this, ctx);
5107 return null;
5108 }
5109 visitInvokeFunctionExpr(expr, ctx) {
5110 const fnExpr = expr.fn;
5111 if (fnExpr instanceof ReadVarExpr && fnExpr.builtin === BuiltinVar.Super) {
5112 ctx.currentClass.parent.visitExpression(this, ctx);
5113 ctx.print(expr, `.call(this`);
5114 if (expr.args.length > 0) {
5115 ctx.print(expr, `, `);
5116 this.visitAllExpressions(expr.args, ctx, ',');
5117 }
5118 ctx.print(expr, `)`);
5119 }
5120 else {
5121 super.visitInvokeFunctionExpr(expr, ctx);
5122 }
5123 return null;
5124 }
5125 visitTaggedTemplateExpr(ast, ctx) {
5126 // The following convoluted piece of code is effectively the downlevelled equivalent of
5127 // ```
5128 // tag`...`
5129 // ```
5130 // which is effectively like:
5131 // ```
5132 // tag(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
5133 // ```
5134 const elements = ast.template.elements;
5135 ast.tag.visitExpression(this, ctx);
5136 ctx.print(ast, `(${makeTemplateObjectPolyfill}(`);
5137 ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.text, false)).join(', ')}], `);
5138 ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.rawText, false)).join(', ')}])`);
5139 ast.template.expressions.forEach(expression => {
5140 ctx.print(ast, ', ');
5141 expression.visitExpression(this, ctx);
5142 });
5143 ctx.print(ast, ')');
5144 return null;
5145 }
5146 visitFunctionExpr(ast, ctx) {
5147 ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`);
5148 this._visitParams(ast.params, ctx);
5149 ctx.println(ast, `) {`);
5150 ctx.incIndent();
5151 this.visitAllStatements(ast.statements, ctx);
5152 ctx.decIndent();
5153 ctx.print(ast, `}`);
5154 return null;
5155 }
5156 visitDeclareFunctionStmt(stmt, ctx) {
5157 ctx.print(stmt, `function ${stmt.name}(`);
5158 this._visitParams(stmt.params, ctx);
5159 ctx.println(stmt, `) {`);
5160 ctx.incIndent();
5161 this.visitAllStatements(stmt.statements, ctx);
5162 ctx.decIndent();
5163 ctx.println(stmt, `}`);
5164 return null;
5165 }
5166 visitTryCatchStmt(stmt, ctx) {
5167 ctx.println(stmt, `try {`);
5168 ctx.incIndent();
5169 this.visitAllStatements(stmt.bodyStmts, ctx);
5170 ctx.decIndent();
5171 ctx.println(stmt, `} catch (${CATCH_ERROR_VAR$1.name}) {`);
5172 ctx.incIndent();
5173 const catchStmts = [CATCH_STACK_VAR$1.set(CATCH_ERROR_VAR$1.prop('stack')).toDeclStmt(null, [
5174 StmtModifier.Final
5175 ])].concat(stmt.catchStmts);
5176 this.visitAllStatements(catchStmts, ctx);
5177 ctx.decIndent();
5178 ctx.println(stmt, `}`);
5179 return null;
5180 }
5181 visitLocalizedString(ast, ctx) {
5182 // The following convoluted piece of code is effectively the downlevelled equivalent of
5183 // ```
5184 // $localize `...`
5185 // ```
5186 // which is effectively like:
5187 // ```
5188 // $localize(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
5189 // ```
5190 ctx.print(ast, `$localize(${makeTemplateObjectPolyfill}(`);
5191 const parts = [ast.serializeI18nHead()];
5192 for (let i = 1; i < ast.messageParts.length; i++) {
5193 parts.push(ast.serializeI18nTemplatePart(i));
5194 }
5195 ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.cooked, false)).join(', ')}], `);
5196 ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.raw, false)).join(', ')}])`);
5197 ast.expressions.forEach(expression => {
5198 ctx.print(ast, ', ');
5199 expression.visitExpression(this, ctx);
5200 });
5201 ctx.print(ast, ')');
5202 return null;
5203 }
5204 _visitParams(params, ctx) {
5205 this.visitAllObjects(param => ctx.print(null, param.name), params, ctx, ',');
5206 }
5207 getBuiltinMethodName(method) {
5208 let name;
5209 switch (method) {
5210 case BuiltinMethod.ConcatArray:
5211 name = 'concat';
5212 break;
5213 case BuiltinMethod.SubscribeObservable:
5214 name = 'subscribe';
5215 break;
5216 case BuiltinMethod.Bind:
5217 name = 'bind';
5218 break;
5219 default:
5220 throw new Error(`Unknown builtin method: ${method}`);
5221 }
5222 return name;
5223 }
5224 }
5225
5226 /**
5227 * @license
5228 * Copyright Google LLC All Rights Reserved.
5229 *
5230 * Use of this source code is governed by an MIT-style license that can be
5231 * found in the LICENSE file at https://angular.io/license
5232 */
5233 /**
5234 * The Trusted Types policy, or null if Trusted Types are not
5235 * enabled/supported, or undefined if the policy has not been created yet.
5236 */
5237 let policy;
5238 /**
5239 * Returns the Trusted Types policy, or null if Trusted Types are not
5240 * enabled/supported. The first call to this function will create the policy.
5241 */
5242 function getPolicy() {
5243 if (policy === undefined) {
5244 policy = null;
5245 if (_global.trustedTypes) {
5246 try {
5247 policy =
5248 _global.trustedTypes.createPolicy('angular#unsafe-jit', {
5249 createScript: (s) => s,
5250 });
5251 }
5252 catch (_a) {
5253 // trustedTypes.createPolicy throws if called with a name that is
5254 // already registered, even in report-only mode. Until the API changes,
5255 // catch the error not to break the applications functionally. In such
5256 // cases, the code will fall back to using strings.
5257 }
5258 }
5259 }
5260 return policy;
5261 }
5262 /**
5263 * Unsafely promote a string to a TrustedScript, falling back to strings when
5264 * Trusted Types are not available.
5265 * @security In particular, it must be assured that the provided string will
5266 * never cause an XSS vulnerability if used in a context that will be
5267 * interpreted and executed as a script by a browser, e.g. when calling eval.
5268 */
5269 function trustedScriptFromString(script) {
5270 var _a;
5271 return ((_a = getPolicy()) === null || _a === void 0 ? void 0 : _a.createScript(script)) || script;
5272 }
5273 /**
5274 * Unsafely call the Function constructor with the given string arguments. It
5275 * is only available in development mode, and should be stripped out of
5276 * production code.
5277 * @security This is a security-sensitive function; any use of this function
5278 * must go through security review. In particular, it must be assured that it
5279 * is only called from the JIT compiler, as use in other code can lead to XSS
5280 * vulnerabilities.
5281 */
5282 function newTrustedFunctionForJIT(...args) {
5283 if (!_global.trustedTypes) {
5284 // In environments that don't support Trusted Types, fall back to the most
5285 // straightforward implementation:
5286 return new Function(...args);
5287 }
5288 // Chrome currently does not support passing TrustedScript to the Function
5289 // constructor. The following implements the workaround proposed on the page
5290 // below, where the Chromium bug is also referenced:
5291 // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
5292 const fnArgs = args.slice(0, -1).join(',');
5293 const fnBody = args.pop().toString();
5294 const body = `(function anonymous(${fnArgs}
5295) { ${fnBody}
5296})`;
5297 // Using eval directly confuses the compiler and prevents this module from
5298 // being stripped out of JS binaries even if not used. The global['eval']
5299 // indirection fixes that.
5300 const fn = _global['eval'](trustedScriptFromString(body));
5301 // To completely mimic the behavior of calling "new Function", two more
5302 // things need to happen:
5303 // 1. Stringifying the resulting function should return its source code
5304 fn.toString = () => body;
5305 // 2. When calling the resulting function, `this` should refer to `global`
5306 return fn.bind(_global);
5307 // When Trusted Types support in Function constructors is widely available,
5308 // the implementation of this function can be simplified to:
5309 // return new Function(...args.map(a => trustedScriptFromString(a)));
5310 }
5311
5312 /**
5313 * @license
5314 * Copyright Google LLC All Rights Reserved.
5315 *
5316 * Use of this source code is governed by an MIT-style license that can be
5317 * found in the LICENSE file at https://angular.io/license
5318 */
5319 /**
5320 * A helper class to manage the evaluation of JIT generated code.
5321 */
5322 class JitEvaluator {
5323 /**
5324 *
5325 * @param sourceUrl The URL of the generated code.
5326 * @param statements An array of Angular statement AST nodes to be evaluated.
5327 * @param reflector A helper used when converting the statements to executable code.
5328 * @param createSourceMaps If true then create a source-map for the generated code and include it
5329 * inline as a source-map comment.
5330 * @returns A map of all the variables in the generated code.
5331 */
5332 evaluateStatements(sourceUrl, statements, reflector, createSourceMaps) {
5333 const converter = new JitEmitterVisitor(reflector);
5334 const ctx = EmitterVisitorContext.createRoot();
5335 // Ensure generated code is in strict mode
5336 if (statements.length > 0 && !isUseStrictStatement(statements[0])) {
5337 statements = [
5338 literal('use strict').toStmt(),
5339 ...statements,
5340 ];
5341 }
5342 converter.visitAllStatements(statements, ctx);
5343 converter.createReturnStmt(ctx);
5344 return this.evaluateCode(sourceUrl, ctx, converter.getArgs(), createSourceMaps);
5345 }
5346 /**
5347 * Evaluate a piece of JIT generated code.
5348 * @param sourceUrl The URL of this generated code.
5349 * @param ctx A context object that contains an AST of the code to be evaluated.
5350 * @param vars A map containing the names and values of variables that the evaluated code might
5351 * reference.
5352 * @param createSourceMap If true then create a source-map for the generated code and include it
5353 * inline as a source-map comment.
5354 * @returns The result of evaluating the code.
5355 */
5356 evaluateCode(sourceUrl, ctx, vars, createSourceMap) {
5357 let fnBody = `"use strict";${ctx.toSource()}\n//# sourceURL=${sourceUrl}`;
5358 const fnArgNames = [];
5359 const fnArgValues = [];
5360 for (const argName in vars) {
5361 fnArgValues.push(vars[argName]);
5362 fnArgNames.push(argName);
5363 }
5364 if (createSourceMap) {
5365 // using `new Function(...)` generates a header, 1 line of no arguments, 2 lines otherwise
5366 // E.g. ```
5367 // function anonymous(a,b,c
5368 // /**/) { ... }```
5369 // We don't want to hard code this fact, so we auto detect it via an empty function first.
5370 const emptyFn = newTrustedFunctionForJIT(...fnArgNames.concat('return null;')).toString();
5371 const headerLines = emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - 1;
5372 fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, headerLines).toJsComment()}`;
5373 }
5374 const fn = newTrustedFunctionForJIT(...fnArgNames.concat(fnBody));
5375 return this.executeFunction(fn, fnArgValues);
5376 }
5377 /**
5378 * Execute a JIT generated function by calling it.
5379 *
5380 * This method can be overridden in tests to capture the functions that are generated
5381 * by this `JitEvaluator` class.
5382 *
5383 * @param fn A function to execute.
5384 * @param args The arguments to pass to the function being executed.
5385 * @returns The return value of the executed function.
5386 */
5387 executeFunction(fn, args) {
5388 return fn(...args);
5389 }
5390 }
5391 /**
5392 * An Angular AST visitor that converts AST nodes into executable JavaScript code.
5393 */
5394 class JitEmitterVisitor extends AbstractJsEmitterVisitor {
5395 constructor(reflector) {
5396 super();
5397 this.reflector = reflector;
5398 this._evalArgNames = [];
5399 this._evalArgValues = [];
5400 this._evalExportedVars = [];
5401 }
5402 createReturnStmt(ctx) {
5403 const stmt = new ReturnStatement(new LiteralMapExpr(this._evalExportedVars.map(resultVar => new LiteralMapEntry(resultVar, variable(resultVar), false))));
5404 stmt.visitStatement(this, ctx);
5405 }
5406 getArgs() {
5407 const result = {};
5408 for (let i = 0; i < this._evalArgNames.length; i++) {
5409 result[this._evalArgNames[i]] = this._evalArgValues[i];
5410 }
5411 return result;
5412 }
5413 visitExternalExpr(ast, ctx) {
5414 this._emitReferenceToExternal(ast, this.reflector.resolveExternalReference(ast.value), ctx);
5415 return null;
5416 }
5417 visitWrappedNodeExpr(ast, ctx) {
5418 this._emitReferenceToExternal(ast, ast.node, ctx);
5419 return null;
5420 }
5421 visitDeclareVarStmt(stmt, ctx) {
5422 if (stmt.hasModifier(StmtModifier.Exported)) {
5423 this._evalExportedVars.push(stmt.name);
5424 }
5425 return super.visitDeclareVarStmt(stmt, ctx);
5426 }
5427 visitDeclareFunctionStmt(stmt, ctx) {
5428 if (stmt.hasModifier(StmtModifier.Exported)) {
5429 this._evalExportedVars.push(stmt.name);
5430 }
5431 return super.visitDeclareFunctionStmt(stmt, ctx);
5432 }
5433 visitDeclareClassStmt(stmt, ctx) {
5434 if (stmt.hasModifier(StmtModifier.Exported)) {
5435 this._evalExportedVars.push(stmt.name);
5436 }
5437 return super.visitDeclareClassStmt(stmt, ctx);
5438 }
5439 _emitReferenceToExternal(ast, value, ctx) {
5440 let id = this._evalArgValues.indexOf(value);
5441 if (id === -1) {
5442 id = this._evalArgValues.length;
5443 this._evalArgValues.push(value);
5444 const name = identifierName({ reference: value }) || 'val';
5445 this._evalArgNames.push(`jit_${name}_${id}`);
5446 }
5447 ctx.print(ast, this._evalArgNames[id]);
5448 }
5449 }
5450 function isUseStrictStatement(statement) {
5451 return statement.isEquivalent(literal('use strict').toStmt());
5452 }
5453
5454 /**
5455 * @license
5456 * Copyright Google LLC All Rights Reserved.
5457 *
5458 * Use of this source code is governed by an MIT-style license that can be
5459 * found in the LICENSE file at https://angular.io/license
5460 */
5461 const $EOF = 0;
5462 const $BSPACE = 8;
5463 const $TAB = 9;
5464 const $LF = 10;
5465 const $VTAB = 11;
5466 const $FF = 12;
5467 const $CR = 13;
5468 const $SPACE = 32;
5469 const $BANG = 33;
5470 const $DQ = 34;
5471 const $HASH = 35;
5472 const $$ = 36;
5473 const $PERCENT = 37;
5474 const $AMPERSAND = 38;
5475 const $SQ = 39;
5476 const $LPAREN = 40;
5477 const $RPAREN = 41;
5478 const $STAR = 42;
5479 const $PLUS = 43;
5480 const $COMMA = 44;
5481 const $MINUS = 45;
5482 const $PERIOD = 46;
5483 const $SLASH = 47;
5484 const $COLON = 58;
5485 const $SEMICOLON = 59;
5486 const $LT = 60;
5487 const $EQ = 61;
5488 const $GT = 62;
5489 const $QUESTION = 63;
5490 const $0 = 48;
5491 const $7 = 55;
5492 const $9 = 57;
5493 const $A = 65;
5494 const $E = 69;
5495 const $F = 70;
5496 const $X = 88;
5497 const $Z = 90;
5498 const $LBRACKET = 91;
5499 const $BACKSLASH = 92;
5500 const $RBRACKET = 93;
5501 const $CARET = 94;
5502 const $_ = 95;
5503 const $a = 97;
5504 const $b = 98;
5505 const $e = 101;
5506 const $f = 102;
5507 const $n = 110;
5508 const $r = 114;
5509 const $t = 116;
5510 const $u = 117;
5511 const $v = 118;
5512 const $x = 120;
5513 const $z = 122;
5514 const $LBRACE = 123;
5515 const $BAR = 124;
5516 const $RBRACE = 125;
5517 const $NBSP = 160;
5518 const $BT = 96;
5519 function isWhitespace(code) {
5520 return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
5521 }
5522 function isDigit(code) {
5523 return $0 <= code && code <= $9;
5524 }
5525 function isAsciiLetter(code) {
5526 return code >= $a && code <= $z || code >= $A && code <= $Z;
5527 }
5528 function isAsciiHexDigit(code) {
5529 return code >= $a && code <= $f || code >= $A && code <= $F || isDigit(code);
5530 }
5531 function isNewLine(code) {
5532 return code === $LF || code === $CR;
5533 }
5534 function isOctalDigit(code) {
5535 return $0 <= code && code <= $7;
5536 }
5537
5538 /**
5539 * @license
5540 * Copyright Google LLC All Rights Reserved.
5541 *
5542 * Use of this source code is governed by an MIT-style license that can be
5543 * found in the LICENSE file at https://angular.io/license
5544 */
5545 class ParseLocation {
5546 constructor(file, offset, line, col) {
5547 this.file = file;
5548 this.offset = offset;
5549 this.line = line;
5550 this.col = col;
5551 }
5552 toString() {
5553 return this.offset != null ? `${this.file.url}@${this.line}:${this.col}` : this.file.url;
5554 }
5555 moveBy(delta) {
5556 const source = this.file.content;
5557 const len = source.length;
5558 let offset = this.offset;
5559 let line = this.line;
5560 let col = this.col;
5561 while (offset > 0 && delta < 0) {
5562 offset--;
5563 delta++;
5564 const ch = source.charCodeAt(offset);
5565 if (ch == $LF) {
5566 line--;
5567 const priorLine = source.substr(0, offset - 1).lastIndexOf(String.fromCharCode($LF));
5568 col = priorLine > 0 ? offset - priorLine : offset;
5569 }
5570 else {
5571 col--;
5572 }
5573 }
5574 while (offset < len && delta > 0) {
5575 const ch = source.charCodeAt(offset);
5576 offset++;
5577 delta--;
5578 if (ch == $LF) {
5579 line++;
5580 col = 0;
5581 }
5582 else {
5583 col++;
5584 }
5585 }
5586 return new ParseLocation(this.file, offset, line, col);
5587 }
5588 // Return the source around the location
5589 // Up to `maxChars` or `maxLines` on each side of the location
5590 getContext(maxChars, maxLines) {
5591 const content = this.file.content;
5592 let startOffset = this.offset;
5593 if (startOffset != null) {
5594 if (startOffset > content.length - 1) {
5595 startOffset = content.length - 1;
5596 }
5597 let endOffset = startOffset;
5598 let ctxChars = 0;
5599 let ctxLines = 0;
5600 while (ctxChars < maxChars && startOffset > 0) {
5601 startOffset--;
5602 ctxChars++;
5603 if (content[startOffset] == '\n') {
5604 if (++ctxLines == maxLines) {
5605 break;
5606 }
5607 }
5608 }
5609 ctxChars = 0;
5610 ctxLines = 0;
5611 while (ctxChars < maxChars && endOffset < content.length - 1) {
5612 endOffset++;
5613 ctxChars++;
5614 if (content[endOffset] == '\n') {
5615 if (++ctxLines == maxLines) {
5616 break;
5617 }
5618 }
5619 }
5620 return {
5621 before: content.substring(startOffset, this.offset),
5622 after: content.substring(this.offset, endOffset + 1),
5623 };
5624 }
5625 return null;
5626 }
5627 }
5628 class ParseSourceFile {
5629 constructor(content, url) {
5630 this.content = content;
5631 this.url = url;
5632 }
5633 }
5634 class ParseSourceSpan {
5635 /**
5636 * Create an object that holds information about spans of tokens/nodes captured during
5637 * lexing/parsing of text.
5638 *
5639 * @param start
5640 * The location of the start of the span (having skipped leading trivia).
5641 * Skipping leading trivia makes source-spans more "user friendly", since things like HTML
5642 * elements will appear to begin at the start of the opening tag, rather than at the start of any
5643 * leading trivia, which could include newlines.
5644 *
5645 * @param end
5646 * The location of the end of the span.
5647 *
5648 * @param fullStart
5649 * The start of the token without skipping the leading trivia.
5650 * This is used by tooling that splits tokens further, such as extracting Angular interpolations
5651 * from text tokens. Such tooling creates new source-spans relative to the original token's
5652 * source-span. If leading trivia characters have been skipped then the new source-spans may be
5653 * incorrectly offset.
5654 *
5655 * @param details
5656 * Additional information (such as identifier names) that should be associated with the span.
5657 */
5658 constructor(start, end, fullStart = start, details = null) {
5659 this.start = start;
5660 this.end = end;
5661 this.fullStart = fullStart;
5662 this.details = details;
5663 }
5664 toString() {
5665 return this.start.file.content.substring(this.start.offset, this.end.offset);
5666 }
5667 }
5668 var ParseErrorLevel;
5669 (function (ParseErrorLevel) {
5670 ParseErrorLevel[ParseErrorLevel["WARNING"] = 0] = "WARNING";
5671 ParseErrorLevel[ParseErrorLevel["ERROR"] = 1] = "ERROR";
5672 })(ParseErrorLevel || (ParseErrorLevel = {}));
5673 class ParseError {
5674 constructor(span, msg, level = ParseErrorLevel.ERROR) {
5675 this.span = span;
5676 this.msg = msg;
5677 this.level = level;
5678 }
5679 contextualMessage() {
5680 const ctx = this.span.start.getContext(100, 3);
5681 return ctx ? `${this.msg} ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` :
5682 this.msg;
5683 }
5684 toString() {
5685 const details = this.span.details ? `, ${this.span.details}` : '';
5686 return `${this.contextualMessage()}: ${this.span.start}${details}`;
5687 }
5688 }
5689 /**
5690 * Generates Source Span object for a given R3 Type for JIT mode.
5691 *
5692 * @param kind Component or Directive.
5693 * @param typeName name of the Component or Directive.
5694 * @param sourceUrl reference to Component or Directive source.
5695 * @returns instance of ParseSourceSpan that represent a given Component or Directive.
5696 */
5697 function r3JitTypeSourceSpan(kind, typeName, sourceUrl) {
5698 const sourceFileName = `in ${kind} ${typeName} in ${sourceUrl}`;
5699 const sourceFile = new ParseSourceFile('', sourceFileName);
5700 return new ParseSourceSpan(new ParseLocation(sourceFile, -1, -1, -1), new ParseLocation(sourceFile, -1, -1, -1));
5701 }
5702
5703 /**
5704 * @license
5705 * Copyright Google LLC All Rights Reserved.
5706 *
5707 * Use of this source code is governed by an MIT-style license that can be
5708 * found in the LICENSE file at https://angular.io/license
5709 */
5710 /**
5711 * Implementation of `CompileReflector` which resolves references to @angular/core
5712 * symbols at runtime, according to a consumer-provided mapping.
5713 *
5714 * Only supports `resolveExternalReference`, all other methods throw.
5715 */
5716 class R3JitReflector {
5717 constructor(context) {
5718 this.context = context;
5719 }
5720 resolveExternalReference(ref) {
5721 // This reflector only handles @angular/core imports.
5722 if (ref.moduleName !== '@angular/core') {
5723 throw new Error(`Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.`);
5724 }
5725 if (!this.context.hasOwnProperty(ref.name)) {
5726 throw new Error(`No value provided for @angular/core symbol '${ref.name}'.`);
5727 }
5728 return this.context[ref.name];
5729 }
5730 parameters(typeOrFunc) {
5731 throw new Error('Not implemented.');
5732 }
5733 annotations(typeOrFunc) {
5734 throw new Error('Not implemented.');
5735 }
5736 shallowAnnotations(typeOrFunc) {
5737 throw new Error('Not implemented.');
5738 }
5739 tryAnnotations(typeOrFunc) {
5740 throw new Error('Not implemented.');
5741 }
5742 propMetadata(typeOrFunc) {
5743 throw new Error('Not implemented.');
5744 }
5745 hasLifecycleHook(type, lcProperty) {
5746 throw new Error('Not implemented.');
5747 }
5748 guards(typeOrFunc) {
5749 throw new Error('Not implemented.');
5750 }
5751 componentModuleUrl(type, cmpMetadata) {
5752 throw new Error('Not implemented.');
5753 }
5754 }
5755
5756 /**
5757 * @license
5758 * Copyright Google LLC All Rights Reserved.
5759 *
5760 * Use of this source code is governed by an MIT-style license that can be
5761 * found in the LICENSE file at https://angular.io/license
5762 */
5763 function mapLiteral(obj, quoted = false) {
5764 return literalMap(Object.keys(obj).map(key => ({
5765 key,
5766 quoted,
5767 value: obj[key],
5768 })));
5769 }
5770
5771 /**
5772 * @license
5773 * Copyright Google LLC All Rights Reserved.
5774 *
5775 * Use of this source code is governed by an MIT-style license that can be
5776 * found in the LICENSE file at https://angular.io/license
5777 */
5778 /**
5779 * Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`.
5780 */
5781 function compileNgModule(meta) {
5782 const { internalType, type: moduleType, bootstrap, declarations, imports, exports, schemas, containsForwardDecls, emitInline, id } = meta;
5783 const additionalStatements = [];
5784 const definitionMap = { type: internalType };
5785 // Only generate the keys in the metadata if the arrays have values.
5786 if (bootstrap.length) {
5787 definitionMap.bootstrap = refsToArray(bootstrap, containsForwardDecls);
5788 }
5789 // If requested to emit scope information inline, pass the declarations, imports and exports to
5790 // the `ɵɵdefineNgModule` call. The JIT compilation uses this.
5791 if (emitInline) {
5792 if (declarations.length) {
5793 definitionMap.declarations = refsToArray(declarations, containsForwardDecls);
5794 }
5795 if (imports.length) {
5796 definitionMap.imports = refsToArray(imports, containsForwardDecls);
5797 }
5798 if (exports.length) {
5799 definitionMap.exports = refsToArray(exports, containsForwardDecls);
5800 }
5801 }
5802 // If not emitting inline, the scope information is not passed into `ɵɵdefineNgModule` as it would
5803 // prevent tree-shaking of the declarations, imports and exports references.
5804 else {
5805 const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta);
5806 if (setNgModuleScopeCall !== null) {
5807 additionalStatements.push(setNgModuleScopeCall);
5808 }
5809 }
5810 if (schemas && schemas.length) {
5811 definitionMap.schemas = literalArr(schemas.map(ref => ref.value));
5812 }
5813 if (id) {
5814 definitionMap.id = id;
5815 }
5816 const expression = importExpr(Identifiers$1.defineNgModule).callFn([mapToMapExpression(definitionMap)]);
5817 const type = new ExpressionType(importExpr(Identifiers$1.NgModuleDefWithMeta, [
5818 new ExpressionType(moduleType.type), tupleTypeOf(declarations), tupleTypeOf(imports),
5819 tupleTypeOf(exports)
5820 ]));
5821 return { expression, type, additionalStatements };
5822 }
5823 /**
5824 * Generates a function call to `ɵɵsetNgModuleScope` with all necessary information so that the
5825 * transitive module scope can be computed during runtime in JIT mode. This call is marked pure
5826 * such that the references to declarations, imports and exports may be elided causing these
5827 * symbols to become tree-shakeable.
5828 */
5829 function generateSetNgModuleScopeCall(meta) {
5830 const { adjacentType: moduleType, declarations, imports, exports, containsForwardDecls } = meta;
5831 const scopeMap = {};
5832 if (declarations.length) {
5833 scopeMap.declarations = refsToArray(declarations, containsForwardDecls);
5834 }
5835 if (imports.length) {
5836 scopeMap.imports = refsToArray(imports, containsForwardDecls);
5837 }
5838 if (exports.length) {
5839 scopeMap.exports = refsToArray(exports, containsForwardDecls);
5840 }
5841 if (Object.keys(scopeMap).length === 0) {
5842 return null;
5843 }
5844 // setNgModuleScope(...)
5845 const fnCall = new InvokeFunctionExpr(
5846 /* fn */ importExpr(Identifiers$1.setNgModuleScope),
5847 /* args */ [moduleType, mapToMapExpression(scopeMap)]);
5848 // (ngJitMode guard) && setNgModuleScope(...)
5849 const guardedCall = jitOnlyGuardedExpression(fnCall);
5850 // function() { (ngJitMode guard) && setNgModuleScope(...); }
5851 const iife = new FunctionExpr(
5852 /* params */ [],
5853 /* statements */ [guardedCall.toStmt()]);
5854 // (function() { (ngJitMode guard) && setNgModuleScope(...); })()
5855 const iifeCall = new InvokeFunctionExpr(
5856 /* fn */ iife,
5857 /* args */ []);
5858 return iifeCall.toStmt();
5859 }
5860 function compileInjector(meta) {
5861 const result = compileFactoryFunction({
5862 name: meta.name,
5863 type: meta.type,
5864 internalType: meta.internalType,
5865 typeArgumentCount: 0,
5866 deps: meta.deps,
5867 injectFn: Identifiers$1.inject,
5868 target: R3FactoryTarget.NgModule,
5869 });
5870 const definitionMap = {
5871 factory: result.factory,
5872 };
5873 if (meta.providers !== null) {
5874 definitionMap.providers = meta.providers;
5875 }
5876 if (meta.imports.length > 0) {
5877 definitionMap.imports = literalArr(meta.imports);
5878 }
5879 const expression = importExpr(Identifiers$1.defineInjector).callFn([mapToMapExpression(definitionMap)]);
5880 const type = new ExpressionType(importExpr(Identifiers$1.InjectorDef, [new ExpressionType(meta.type.type)]));
5881 return { expression, type, statements: result.statements };
5882 }
5883 function tupleTypeOf(exp) {
5884 const types = exp.map(ref => typeofExpr(ref.type));
5885 return exp.length > 0 ? expressionType(literalArr(types)) : NONE_TYPE;
5886 }
5887 function refsToArray(refs, shouldForwardDeclare) {
5888 const values = literalArr(refs.map(ref => ref.value));
5889 return shouldForwardDeclare ? fn([], [new ReturnStatement(values)]) : values;
5890 }
5891
5892 /**
5893 * @license
5894 * Copyright Google LLC All Rights Reserved.
5895 *
5896 * Use of this source code is governed by an MIT-style license that can be
5897 * found in the LICENSE file at https://angular.io/license
5898 */
5899 function compilePipeFromMetadata(metadata) {
5900 const definitionMapValues = [];
5901 // e.g. `name: 'myPipe'`
5902 definitionMapValues.push({ key: 'name', value: literal(metadata.pipeName), quoted: false });
5903 // e.g. `type: MyPipe`
5904 definitionMapValues.push({ key: 'type', value: metadata.type.value, quoted: false });
5905 // e.g. `pure: true`
5906 definitionMapValues.push({ key: 'pure', value: literal(metadata.pure), quoted: false });
5907 const expression = importExpr(Identifiers$1.definePipe).callFn([literalMap(definitionMapValues)]);
5908 const type = new ExpressionType(importExpr(Identifiers$1.PipeDefWithMeta, [
5909 typeWithParameters(metadata.type.type, metadata.typeArgumentCount),
5910 new ExpressionType(new LiteralExpr(metadata.pipeName)),
5911 ]));
5912 return { expression, type };
5913 }
5914
5915 /**
5916 * @license
5917 * Copyright Google LLC All Rights Reserved.
5918 *
5919 * Use of this source code is governed by an MIT-style license that can be
5920 * found in the LICENSE file at https://angular.io/license
5921 */
5922 class ParserError {
5923 constructor(message, input, errLocation, ctxLocation) {
5924 this.input = input;
5925 this.errLocation = errLocation;
5926 this.ctxLocation = ctxLocation;
5927 this.message = `Parser Error: ${message} ${errLocation} [${input}] in ${ctxLocation}`;
5928 }
5929 }
5930 class ParseSpan {
5931 constructor(start, end) {
5932 this.start = start;
5933 this.end = end;
5934 }
5935 toAbsolute(absoluteOffset) {
5936 return new AbsoluteSourceSpan(absoluteOffset + this.start, absoluteOffset + this.end);
5937 }
5938 }
5939 class AST {
5940 constructor(span,
5941 /**
5942 * Absolute location of the expression AST in a source code file.
5943 */
5944 sourceSpan) {
5945 this.span = span;
5946 this.sourceSpan = sourceSpan;
5947 }
5948 visit(visitor, context = null) {
5949 return null;
5950 }
5951 toString() {
5952 return 'AST';
5953 }
5954 }
5955 class ASTWithName extends AST {
5956 constructor(span, sourceSpan, nameSpan) {
5957 super(span, sourceSpan);
5958 this.nameSpan = nameSpan;
5959 }
5960 }
5961 /**
5962 * Represents a quoted expression of the form:
5963 *
5964 * quote = prefix `:` uninterpretedExpression
5965 * prefix = identifier
5966 * uninterpretedExpression = arbitrary string
5967 *
5968 * A quoted expression is meant to be pre-processed by an AST transformer that
5969 * converts it into another AST that no longer contains quoted expressions.
5970 * It is meant to allow third-party developers to extend Angular template
5971 * expression language. The `uninterpretedExpression` part of the quote is
5972 * therefore not interpreted by the Angular's own expression parser.
5973 */
5974 class Quote extends AST {
5975 constructor(span, sourceSpan, prefix, uninterpretedExpression, location) {
5976 super(span, sourceSpan);
5977 this.prefix = prefix;
5978 this.uninterpretedExpression = uninterpretedExpression;
5979 this.location = location;
5980 }
5981 visit(visitor, context = null) {
5982 return visitor.visitQuote(this, context);
5983 }
5984 toString() {
5985 return 'Quote';
5986 }
5987 }
5988 class EmptyExpr extends AST {
5989 visit(visitor, context = null) {
5990 // do nothing
5991 }
5992 }
5993 class ImplicitReceiver extends AST {
5994 visit(visitor, context = null) {
5995 return visitor.visitImplicitReceiver(this, context);
5996 }
5997 }
5998 /**
5999 * Receiver when something is accessed through `this` (e.g. `this.foo`). Note that this class
6000 * inherits from `ImplicitReceiver`, because accessing something through `this` is treated the
6001 * same as accessing it implicitly inside of an Angular template (e.g. `[attr.title]="this.title"`
6002 * is the same as `[attr.title]="title"`.). Inheriting allows for the `this` accesses to be treated
6003 * the same as implicit ones, except for a couple of exceptions like `$event` and `$any`.
6004 * TODO: we should find a way for this class not to extend from `ImplicitReceiver` in the future.
6005 */
6006 class ThisReceiver extends ImplicitReceiver {
6007 visit(visitor, context = null) {
6008 var _a;
6009 return (_a = visitor.visitThisReceiver) === null || _a === void 0 ? void 0 : _a.call(visitor, this, context);
6010 }
6011 }
6012 /**
6013 * Multiple expressions separated by a semicolon.
6014 */
6015 class Chain extends AST {
6016 constructor(span, sourceSpan, expressions) {
6017 super(span, sourceSpan);
6018 this.expressions = expressions;
6019 }
6020 visit(visitor, context = null) {
6021 return visitor.visitChain(this, context);
6022 }
6023 }
6024 class Conditional extends AST {
6025 constructor(span, sourceSpan, condition, trueExp, falseExp) {
6026 super(span, sourceSpan);
6027 this.condition = condition;
6028 this.trueExp = trueExp;
6029 this.falseExp = falseExp;
6030 }
6031 visit(visitor, context = null) {
6032 return visitor.visitConditional(this, context);
6033 }
6034 }
6035 class PropertyRead extends ASTWithName {
6036 constructor(span, sourceSpan, nameSpan, receiver, name) {
6037 super(span, sourceSpan, nameSpan);
6038 this.receiver = receiver;
6039 this.name = name;
6040 }
6041 visit(visitor, context = null) {
6042 return visitor.visitPropertyRead(this, context);
6043 }
6044 }
6045 class PropertyWrite extends ASTWithName {
6046 constructor(span, sourceSpan, nameSpan, receiver, name, value) {
6047 super(span, sourceSpan, nameSpan);
6048 this.receiver = receiver;
6049 this.name = name;
6050 this.value = value;
6051 }
6052 visit(visitor, context = null) {
6053 return visitor.visitPropertyWrite(this, context);
6054 }
6055 }
6056 class SafePropertyRead extends ASTWithName {
6057 constructor(span, sourceSpan, nameSpan, receiver, name) {
6058 super(span, sourceSpan, nameSpan);
6059 this.receiver = receiver;
6060 this.name = name;
6061 }
6062 visit(visitor, context = null) {
6063 return visitor.visitSafePropertyRead(this, context);
6064 }
6065 }
6066 class KeyedRead extends AST {
6067 constructor(span, sourceSpan, obj, key) {
6068 super(span, sourceSpan);
6069 this.obj = obj;
6070 this.key = key;
6071 }
6072 visit(visitor, context = null) {
6073 return visitor.visitKeyedRead(this, context);
6074 }
6075 }
6076 class KeyedWrite extends AST {
6077 constructor(span, sourceSpan, obj, key, value) {
6078 super(span, sourceSpan);
6079 this.obj = obj;
6080 this.key = key;
6081 this.value = value;
6082 }
6083 visit(visitor, context = null) {
6084 return visitor.visitKeyedWrite(this, context);
6085 }
6086 }
6087 class BindingPipe extends ASTWithName {
6088 constructor(span, sourceSpan, exp, name, args, nameSpan) {
6089 super(span, sourceSpan, nameSpan);
6090 this.exp = exp;
6091 this.name = name;
6092 this.args = args;
6093 }
6094 visit(visitor, context = null) {
6095 return visitor.visitPipe(this, context);
6096 }
6097 }
6098 class LiteralPrimitive extends AST {
6099 constructor(span, sourceSpan, value) {
6100 super(span, sourceSpan);
6101 this.value = value;
6102 }
6103 visit(visitor, context = null) {
6104 return visitor.visitLiteralPrimitive(this, context);
6105 }
6106 }
6107 class LiteralArray extends AST {
6108 constructor(span, sourceSpan, expressions) {
6109 super(span, sourceSpan);
6110 this.expressions = expressions;
6111 }
6112 visit(visitor, context = null) {
6113 return visitor.visitLiteralArray(this, context);
6114 }
6115 }
6116 class LiteralMap extends AST {
6117 constructor(span, sourceSpan, keys, values) {
6118 super(span, sourceSpan);
6119 this.keys = keys;
6120 this.values = values;
6121 }
6122 visit(visitor, context = null) {
6123 return visitor.visitLiteralMap(this, context);
6124 }
6125 }
6126 class Interpolation extends AST {
6127 constructor(span, sourceSpan, strings, expressions) {
6128 super(span, sourceSpan);
6129 this.strings = strings;
6130 this.expressions = expressions;
6131 }
6132 visit(visitor, context = null) {
6133 return visitor.visitInterpolation(this, context);
6134 }
6135 }
6136 class Binary extends AST {
6137 constructor(span, sourceSpan, operation, left, right) {
6138 super(span, sourceSpan);
6139 this.operation = operation;
6140 this.left = left;
6141 this.right = right;
6142 }
6143 visit(visitor, context = null) {
6144 return visitor.visitBinary(this, context);
6145 }
6146 }
6147 /**
6148 * For backwards compatibility reasons, `Unary` inherits from `Binary` and mimics the binary AST
6149 * node that was originally used. This inheritance relation can be deleted in some future major,
6150 * after consumers have been given a chance to fully support Unary.
6151 */
6152 class Unary extends Binary {
6153 /**
6154 * During the deprecation period this constructor is private, to avoid consumers from creating
6155 * a `Unary` with the fallback properties for `Binary`.
6156 */
6157 constructor(span, sourceSpan, operator, expr, binaryOp, binaryLeft, binaryRight) {
6158 super(span, sourceSpan, binaryOp, binaryLeft, binaryRight);
6159 this.operator = operator;
6160 this.expr = expr;
6161 }
6162 /**
6163 * Creates a unary minus expression "-x", represented as `Binary` using "0 - x".
6164 */
6165 static createMinus(span, sourceSpan, expr) {
6166 return new Unary(span, sourceSpan, '-', expr, '-', new LiteralPrimitive(span, sourceSpan, 0), expr);
6167 }
6168 /**
6169 * Creates a unary plus expression "+x", represented as `Binary` using "x - 0".
6170 */
6171 static createPlus(span, sourceSpan, expr) {
6172 return new Unary(span, sourceSpan, '+', expr, '-', expr, new LiteralPrimitive(span, sourceSpan, 0));
6173 }
6174 visit(visitor, context = null) {
6175 if (visitor.visitUnary !== undefined) {
6176 return visitor.visitUnary(this, context);
6177 }
6178 return visitor.visitBinary(this, context);
6179 }
6180 }
6181 class PrefixNot extends AST {
6182 constructor(span, sourceSpan, expression) {
6183 super(span, sourceSpan);
6184 this.expression = expression;
6185 }
6186 visit(visitor, context = null) {
6187 return visitor.visitPrefixNot(this, context);
6188 }
6189 }
6190 class NonNullAssert extends AST {
6191 constructor(span, sourceSpan, expression) {
6192 super(span, sourceSpan);
6193 this.expression = expression;
6194 }
6195 visit(visitor, context = null) {
6196 return visitor.visitNonNullAssert(this, context);
6197 }
6198 }
6199 class MethodCall extends ASTWithName {
6200 constructor(span, sourceSpan, nameSpan, receiver, name, args) {
6201 super(span, sourceSpan, nameSpan);
6202 this.receiver = receiver;
6203 this.name = name;
6204 this.args = args;
6205 }
6206 visit(visitor, context = null) {
6207 return visitor.visitMethodCall(this, context);
6208 }
6209 }
6210 class SafeMethodCall extends ASTWithName {
6211 constructor(span, sourceSpan, nameSpan, receiver, name, args) {
6212 super(span, sourceSpan, nameSpan);
6213 this.receiver = receiver;
6214 this.name = name;
6215 this.args = args;
6216 }
6217 visit(visitor, context = null) {
6218 return visitor.visitSafeMethodCall(this, context);
6219 }
6220 }
6221 class FunctionCall extends AST {
6222 constructor(span, sourceSpan, target, args) {
6223 super(span, sourceSpan);
6224 this.target = target;
6225 this.args = args;
6226 }
6227 visit(visitor, context = null) {
6228 return visitor.visitFunctionCall(this, context);
6229 }
6230 }
6231 /**
6232 * Records the absolute position of a text span in a source file, where `start` and `end` are the
6233 * starting and ending byte offsets, respectively, of the text span in a source file.
6234 */
6235 class AbsoluteSourceSpan {
6236 constructor(start, end) {
6237 this.start = start;
6238 this.end = end;
6239 }
6240 }
6241 class ASTWithSource extends AST {
6242 constructor(ast, source, location, absoluteOffset, errors) {
6243 super(new ParseSpan(0, source === null ? 0 : source.length), new AbsoluteSourceSpan(absoluteOffset, source === null ? absoluteOffset : absoluteOffset + source.length));
6244 this.ast = ast;
6245 this.source = source;
6246 this.location = location;
6247 this.errors = errors;
6248 }
6249 visit(visitor, context = null) {
6250 if (visitor.visitASTWithSource) {
6251 return visitor.visitASTWithSource(this, context);
6252 }
6253 return this.ast.visit(visitor, context);
6254 }
6255 toString() {
6256 return `${this.source} in ${this.location}`;
6257 }
6258 }
6259 class VariableBinding {
6260 /**
6261 * @param sourceSpan entire span of the binding.
6262 * @param key name of the LHS along with its span.
6263 * @param value optional value for the RHS along with its span.
6264 */
6265 constructor(sourceSpan, key, value) {
6266 this.sourceSpan = sourceSpan;
6267 this.key = key;
6268 this.value = value;
6269 }
6270 }
6271 class ExpressionBinding {
6272 /**
6273 * @param sourceSpan entire span of the binding.
6274 * @param key binding name, like ngForOf, ngForTrackBy, ngIf, along with its
6275 * span. Note that the length of the span may not be the same as
6276 * `key.source.length`. For example,
6277 * 1. key.source = ngFor, key.span is for "ngFor"
6278 * 2. key.source = ngForOf, key.span is for "of"
6279 * 3. key.source = ngForTrackBy, key.span is for "trackBy"
6280 * @param value optional expression for the RHS.
6281 */
6282 constructor(sourceSpan, key, value) {
6283 this.sourceSpan = sourceSpan;
6284 this.key = key;
6285 this.value = value;
6286 }
6287 }
6288 class RecursiveAstVisitor {
6289 visit(ast, context) {
6290 // The default implementation just visits every node.
6291 // Classes that extend RecursiveAstVisitor should override this function
6292 // to selectively visit the specified node.
6293 ast.visit(this, context);
6294 }
6295 visitUnary(ast, context) {
6296 this.visit(ast.expr, context);
6297 }
6298 visitBinary(ast, context) {
6299 this.visit(ast.left, context);
6300 this.visit(ast.right, context);
6301 }
6302 visitChain(ast, context) {
6303 this.visitAll(ast.expressions, context);
6304 }
6305 visitConditional(ast, context) {
6306 this.visit(ast.condition, context);
6307 this.visit(ast.trueExp, context);
6308 this.visit(ast.falseExp, context);
6309 }
6310 visitPipe(ast, context) {
6311 this.visit(ast.exp, context);
6312 this.visitAll(ast.args, context);
6313 }
6314 visitFunctionCall(ast, context) {
6315 if (ast.target) {
6316 this.visit(ast.target, context);
6317 }
6318 this.visitAll(ast.args, context);
6319 }
6320 visitImplicitReceiver(ast, context) { }
6321 visitThisReceiver(ast, context) { }
6322 visitInterpolation(ast, context) {
6323 this.visitAll(ast.expressions, context);
6324 }
6325 visitKeyedRead(ast, context) {
6326 this.visit(ast.obj, context);
6327 this.visit(ast.key, context);
6328 }
6329 visitKeyedWrite(ast, context) {
6330 this.visit(ast.obj, context);
6331 this.visit(ast.key, context);
6332 this.visit(ast.value, context);
6333 }
6334 visitLiteralArray(ast, context) {
6335 this.visitAll(ast.expressions, context);
6336 }
6337 visitLiteralMap(ast, context) {
6338 this.visitAll(ast.values, context);
6339 }
6340 visitLiteralPrimitive(ast, context) { }
6341 visitMethodCall(ast, context) {
6342 this.visit(ast.receiver, context);
6343 this.visitAll(ast.args, context);
6344 }
6345 visitPrefixNot(ast, context) {
6346 this.visit(ast.expression, context);
6347 }
6348 visitNonNullAssert(ast, context) {
6349 this.visit(ast.expression, context);
6350 }
6351 visitPropertyRead(ast, context) {
6352 this.visit(ast.receiver, context);
6353 }
6354 visitPropertyWrite(ast, context) {
6355 this.visit(ast.receiver, context);
6356 this.visit(ast.value, context);
6357 }
6358 visitSafePropertyRead(ast, context) {
6359 this.visit(ast.receiver, context);
6360 }
6361 visitSafeMethodCall(ast, context) {
6362 this.visit(ast.receiver, context);
6363 this.visitAll(ast.args, context);
6364 }
6365 visitQuote(ast, context) { }
6366 // This is not part of the AstVisitor interface, just a helper method
6367 visitAll(asts, context) {
6368 for (const ast of asts) {
6369 this.visit(ast, context);
6370 }
6371 }
6372 }
6373 class AstTransformer {
6374 visitImplicitReceiver(ast, context) {
6375 return ast;
6376 }
6377 visitThisReceiver(ast, context) {
6378 return ast;
6379 }
6380 visitInterpolation(ast, context) {
6381 return new Interpolation(ast.span, ast.sourceSpan, ast.strings, this.visitAll(ast.expressions));
6382 }
6383 visitLiteralPrimitive(ast, context) {
6384 return new LiteralPrimitive(ast.span, ast.sourceSpan, ast.value);
6385 }
6386 visitPropertyRead(ast, context) {
6387 return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name);
6388 }
6389 visitPropertyWrite(ast, context) {
6390 return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, ast.value.visit(this));
6391 }
6392 visitSafePropertyRead(ast, context) {
6393 return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name);
6394 }
6395 visitMethodCall(ast, context) {
6396 return new MethodCall(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
6397 }
6398 visitSafeMethodCall(ast, context) {
6399 return new SafeMethodCall(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
6400 }
6401 visitFunctionCall(ast, context) {
6402 return new FunctionCall(ast.span, ast.sourceSpan, ast.target.visit(this), this.visitAll(ast.args));
6403 }
6404 visitLiteralArray(ast, context) {
6405 return new LiteralArray(ast.span, ast.sourceSpan, this.visitAll(ast.expressions));
6406 }
6407 visitLiteralMap(ast, context) {
6408 return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, this.visitAll(ast.values));
6409 }
6410 visitUnary(ast, context) {
6411 switch (ast.operator) {
6412 case '+':
6413 return Unary.createPlus(ast.span, ast.sourceSpan, ast.expr.visit(this));
6414 case '-':
6415 return Unary.createMinus(ast.span, ast.sourceSpan, ast.expr.visit(this));
6416 default:
6417 throw new Error(`Unknown unary operator ${ast.operator}`);
6418 }
6419 }
6420 visitBinary(ast, context) {
6421 return new Binary(ast.span, ast.sourceSpan, ast.operation, ast.left.visit(this), ast.right.visit(this));
6422 }
6423 visitPrefixNot(ast, context) {
6424 return new PrefixNot(ast.span, ast.sourceSpan, ast.expression.visit(this));
6425 }
6426 visitNonNullAssert(ast, context) {
6427 return new NonNullAssert(ast.span, ast.sourceSpan, ast.expression.visit(this));
6428 }
6429 visitConditional(ast, context) {
6430 return new Conditional(ast.span, ast.sourceSpan, ast.condition.visit(this), ast.trueExp.visit(this), ast.falseExp.visit(this));
6431 }
6432 visitPipe(ast, context) {
6433 return new BindingPipe(ast.span, ast.sourceSpan, ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.nameSpan);
6434 }
6435 visitKeyedRead(ast, context) {
6436 return new KeyedRead(ast.span, ast.sourceSpan, ast.obj.visit(this), ast.key.visit(this));
6437 }
6438 visitKeyedWrite(ast, context) {
6439 return new KeyedWrite(ast.span, ast.sourceSpan, ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
6440 }
6441 visitAll(asts) {
6442 const res = [];
6443 for (let i = 0; i < asts.length; ++i) {
6444 res[i] = asts[i].visit(this);
6445 }
6446 return res;
6447 }
6448 visitChain(ast, context) {
6449 return new Chain(ast.span, ast.sourceSpan, this.visitAll(ast.expressions));
6450 }
6451 visitQuote(ast, context) {
6452 return new Quote(ast.span, ast.sourceSpan, ast.prefix, ast.uninterpretedExpression, ast.location);
6453 }
6454 }
6455 // A transformer that only creates new nodes if the transformer makes a change or
6456 // a change is made a child node.
6457 class AstMemoryEfficientTransformer {
6458 visitImplicitReceiver(ast, context) {
6459 return ast;
6460 }
6461 visitThisReceiver(ast, context) {
6462 return ast;
6463 }
6464 visitInterpolation(ast, context) {
6465 const expressions = this.visitAll(ast.expressions);
6466 if (expressions !== ast.expressions)
6467 return new Interpolation(ast.span, ast.sourceSpan, ast.strings, expressions);
6468 return ast;
6469 }
6470 visitLiteralPrimitive(ast, context) {
6471 return ast;
6472 }
6473 visitPropertyRead(ast, context) {
6474 const receiver = ast.receiver.visit(this);
6475 if (receiver !== ast.receiver) {
6476 return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name);
6477 }
6478 return ast;
6479 }
6480 visitPropertyWrite(ast, context) {
6481 const receiver = ast.receiver.visit(this);
6482 const value = ast.value.visit(this);
6483 if (receiver !== ast.receiver || value !== ast.value) {
6484 return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, value);
6485 }
6486 return ast;
6487 }
6488 visitSafePropertyRead(ast, context) {
6489 const receiver = ast.receiver.visit(this);
6490 if (receiver !== ast.receiver) {
6491 return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name);
6492 }
6493 return ast;
6494 }
6495 visitMethodCall(ast, context) {
6496 const receiver = ast.receiver.visit(this);
6497 const args = this.visitAll(ast.args);
6498 if (receiver !== ast.receiver || args !== ast.args) {
6499 return new MethodCall(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args);
6500 }
6501 return ast;
6502 }
6503 visitSafeMethodCall(ast, context) {
6504 const receiver = ast.receiver.visit(this);
6505 const args = this.visitAll(ast.args);
6506 if (receiver !== ast.receiver || args !== ast.args) {
6507 return new SafeMethodCall(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args);
6508 }
6509 return ast;
6510 }
6511 visitFunctionCall(ast, context) {
6512 const target = ast.target && ast.target.visit(this);
6513 const args = this.visitAll(ast.args);
6514 if (target !== ast.target || args !== ast.args) {
6515 return new FunctionCall(ast.span, ast.sourceSpan, target, args);
6516 }
6517 return ast;
6518 }
6519 visitLiteralArray(ast, context) {
6520 const expressions = this.visitAll(ast.expressions);
6521 if (expressions !== ast.expressions) {
6522 return new LiteralArray(ast.span, ast.sourceSpan, expressions);
6523 }
6524 return ast;
6525 }
6526 visitLiteralMap(ast, context) {
6527 const values = this.visitAll(ast.values);
6528 if (values !== ast.values) {
6529 return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, values);
6530 }
6531 return ast;
6532 }
6533 visitUnary(ast, context) {
6534 const expr = ast.expr.visit(this);
6535 if (expr !== ast.expr) {
6536 switch (ast.operator) {
6537 case '+':
6538 return Unary.createPlus(ast.span, ast.sourceSpan, expr);
6539 case '-':
6540 return Unary.createMinus(ast.span, ast.sourceSpan, expr);
6541 default:
6542 throw new Error(`Unknown unary operator ${ast.operator}`);
6543 }
6544 }
6545 return ast;
6546 }
6547 visitBinary(ast, context) {
6548 const left = ast.left.visit(this);
6549 const right = ast.right.visit(this);
6550 if (left !== ast.left || right !== ast.right) {
6551 return new Binary(ast.span, ast.sourceSpan, ast.operation, left, right);
6552 }
6553 return ast;
6554 }
6555 visitPrefixNot(ast, context) {
6556 const expression = ast.expression.visit(this);
6557 if (expression !== ast.expression) {
6558 return new PrefixNot(ast.span, ast.sourceSpan, expression);
6559 }
6560 return ast;
6561 }
6562 visitNonNullAssert(ast, context) {
6563 const expression = ast.expression.visit(this);
6564 if (expression !== ast.expression) {
6565 return new NonNullAssert(ast.span, ast.sourceSpan, expression);
6566 }
6567 return ast;
6568 }
6569 visitConditional(ast, context) {
6570 const condition = ast.condition.visit(this);
6571 const trueExp = ast.trueExp.visit(this);
6572 const falseExp = ast.falseExp.visit(this);
6573 if (condition !== ast.condition || trueExp !== ast.trueExp || falseExp !== ast.falseExp) {
6574 return new Conditional(ast.span, ast.sourceSpan, condition, trueExp, falseExp);
6575 }
6576 return ast;
6577 }
6578 visitPipe(ast, context) {
6579 const exp = ast.exp.visit(this);
6580 const args = this.visitAll(ast.args);
6581 if (exp !== ast.exp || args !== ast.args) {
6582 return new BindingPipe(ast.span, ast.sourceSpan, exp, ast.name, args, ast.nameSpan);
6583 }
6584 return ast;
6585 }
6586 visitKeyedRead(ast, context) {
6587 const obj = ast.obj.visit(this);
6588 const key = ast.key.visit(this);
6589 if (obj !== ast.obj || key !== ast.key) {
6590 return new KeyedRead(ast.span, ast.sourceSpan, obj, key);
6591 }
6592 return ast;
6593 }
6594 visitKeyedWrite(ast, context) {
6595 const obj = ast.obj.visit(this);
6596 const key = ast.key.visit(this);
6597 const value = ast.value.visit(this);
6598 if (obj !== ast.obj || key !== ast.key || value !== ast.value) {
6599 return new KeyedWrite(ast.span, ast.sourceSpan, obj, key, value);
6600 }
6601 return ast;
6602 }
6603 visitAll(asts) {
6604 const res = [];
6605 let modified = false;
6606 for (let i = 0; i < asts.length; ++i) {
6607 const original = asts[i];
6608 const value = original.visit(this);
6609 res[i] = value;
6610 modified = modified || value !== original;
6611 }
6612 return modified ? res : asts;
6613 }
6614 visitChain(ast, context) {
6615 const expressions = this.visitAll(ast.expressions);
6616 if (expressions !== ast.expressions) {
6617 return new Chain(ast.span, ast.sourceSpan, expressions);
6618 }
6619 return ast;
6620 }
6621 visitQuote(ast, context) {
6622 return ast;
6623 }
6624 }
6625 // Bindings
6626 class ParsedProperty {
6627 constructor(name, expression, type,
6628 // TODO(FW-2095): `keySpan` should really be required but allows `undefined` so VE does
6629 // not need to be updated. Make `keySpan` required when VE is removed.
6630 sourceSpan, keySpan, valueSpan) {
6631 this.name = name;
6632 this.expression = expression;
6633 this.type = type;
6634 this.sourceSpan = sourceSpan;
6635 this.keySpan = keySpan;
6636 this.valueSpan = valueSpan;
6637 this.isLiteral = this.type === ParsedPropertyType.LITERAL_ATTR;
6638 this.isAnimation = this.type === ParsedPropertyType.ANIMATION;
6639 }
6640 }
6641 var ParsedPropertyType;
6642 (function (ParsedPropertyType) {
6643 ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT";
6644 ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR";
6645 ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION";
6646 })(ParsedPropertyType || (ParsedPropertyType = {}));
6647 class ParsedEvent {
6648 // Regular events have a target
6649 // Animation events have a phase
6650 constructor(name, targetOrPhase, type, handler, sourceSpan,
6651 // TODO(FW-2095): keySpan should be required but was made optional to avoid changing VE
6652 handlerSpan, keySpan) {
6653 this.name = name;
6654 this.targetOrPhase = targetOrPhase;
6655 this.type = type;
6656 this.handler = handler;
6657 this.sourceSpan = sourceSpan;
6658 this.handlerSpan = handlerSpan;
6659 this.keySpan = keySpan;
6660 }
6661 }
6662 /**
6663 * ParsedVariable represents a variable declaration in a microsyntax expression.
6664 */
6665 class ParsedVariable {
6666 constructor(name, value, sourceSpan, keySpan, valueSpan) {
6667 this.name = name;
6668 this.value = value;
6669 this.sourceSpan = sourceSpan;
6670 this.keySpan = keySpan;
6671 this.valueSpan = valueSpan;
6672 }
6673 }
6674 class BoundElementProperty {
6675 constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan) {
6676 this.name = name;
6677 this.type = type;
6678 this.securityContext = securityContext;
6679 this.value = value;
6680 this.unit = unit;
6681 this.sourceSpan = sourceSpan;
6682 this.keySpan = keySpan;
6683 this.valueSpan = valueSpan;
6684 }
6685 }
6686
6687 /**
6688 * @license
6689 * Copyright Google LLC All Rights Reserved.
6690 *
6691 * Use of this source code is governed by an MIT-style license that can be
6692 * found in the LICENSE file at https://angular.io/license
6693 */
6694 class EventHandlerVars {
6695 }
6696 EventHandlerVars.event = variable('$event');
6697 class ConvertActionBindingResult {
6698 constructor(
6699 /**
6700 * Render2 compatible statements,
6701 */
6702 stmts,
6703 /**
6704 * Variable name used with render2 compatible statements.
6705 */
6706 allowDefault) {
6707 this.stmts = stmts;
6708 this.allowDefault = allowDefault;
6709 /**
6710 * This is bit of a hack. It converts statements which render2 expects to statements which are
6711 * expected by render3.
6712 *
6713 * Example: `<div click="doSomething($event)">` will generate:
6714 *
6715 * Render3:
6716 * ```
6717 * const pd_b:any = ((<any>ctx.doSomething($event)) !== false);
6718 * return pd_b;
6719 * ```
6720 *
6721 * but render2 expects:
6722 * ```
6723 * return ctx.doSomething($event);
6724 * ```
6725 */
6726 // TODO(misko): remove this hack once we no longer support ViewEngine.
6727 this.render3Stmts = stmts.map((statement) => {
6728 if (statement instanceof DeclareVarStmt && statement.name == allowDefault.name &&
6729 statement.value instanceof BinaryOperatorExpr) {
6730 const lhs = statement.value.lhs;
6731 return new ReturnStatement(lhs.value);
6732 }
6733 return statement;
6734 });
6735 }
6736 }
6737 /**
6738 * Converts the given expression AST into an executable output AST, assuming the expression is
6739 * used in an action binding (e.g. an event handler).
6740 */
6741 function convertActionBinding(localResolver, implicitReceiver, action, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses, globals) {
6742 if (!localResolver) {
6743 localResolver = new DefaultLocalResolver(globals);
6744 }
6745 const actionWithoutBuiltins = convertPropertyBindingBuiltins({
6746 createLiteralArrayConverter: (argCount) => {
6747 // Note: no caching for literal arrays in actions.
6748 return (args) => literalArr(args);
6749 },
6750 createLiteralMapConverter: (keys) => {
6751 // Note: no caching for literal maps in actions.
6752 return (values) => {
6753 const entries = keys.map((k, i) => ({
6754 key: k.key,
6755 value: values[i],
6756 quoted: k.quoted,
6757 }));
6758 return literalMap(entries);
6759 };
6760 },
6761 createPipeConverter: (name) => {
6762 throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
6763 }
6764 }, action);
6765 const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses);
6766 const actionStmts = [];
6767 flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
6768 prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
6769 if (visitor.usesImplicitReceiver) {
6770 localResolver.notifyImplicitReceiverUse();
6771 }
6772 const lastIndex = actionStmts.length - 1;
6773 let preventDefaultVar = null;
6774 if (lastIndex >= 0) {
6775 const lastStatement = actionStmts[lastIndex];
6776 const returnExpr = convertStmtIntoExpression(lastStatement);
6777 if (returnExpr) {
6778 // Note: We need to cast the result of the method call to dynamic,
6779 // as it might be a void method!
6780 preventDefaultVar = createPreventDefaultVar(bindingId);
6781 actionStmts[lastIndex] =
6782 preventDefaultVar.set(returnExpr.cast(DYNAMIC_TYPE).notIdentical(literal(false)))
6783 .toDeclStmt(null, [StmtModifier.Final]);
6784 }
6785 }
6786 return new ConvertActionBindingResult(actionStmts, preventDefaultVar);
6787 }
6788 function convertPropertyBindingBuiltins(converterFactory, ast) {
6789 return convertBuiltins(converterFactory, ast);
6790 }
6791 class ConvertPropertyBindingResult {
6792 constructor(stmts, currValExpr) {
6793 this.stmts = stmts;
6794 this.currValExpr = currValExpr;
6795 }
6796 }
6797 var BindingForm;
6798 (function (BindingForm) {
6799 // The general form of binding expression, supports all expressions.
6800 BindingForm[BindingForm["General"] = 0] = "General";
6801 // Try to generate a simple binding (no temporaries or statements)
6802 // otherwise generate a general binding
6803 BindingForm[BindingForm["TrySimple"] = 1] = "TrySimple";
6804 // Inlines assignment of temporaries into the generated expression. The result may still
6805 // have statements attached for declarations of temporary variables.
6806 // This is the only relevant form for Ivy, the other forms are only used in ViewEngine.
6807 BindingForm[BindingForm["Expression"] = 2] = "Expression";
6808 })(BindingForm || (BindingForm = {}));
6809 /**
6810 * Converts the given expression AST into an executable output AST, assuming the expression
6811 * is used in property binding. The expression has to be preprocessed via
6812 * `convertPropertyBindingBuiltins`.
6813 */
6814 function convertPropertyBinding(localResolver, implicitReceiver, expressionWithoutBuiltins, bindingId, form, interpolationFunction) {
6815 if (!localResolver) {
6816 localResolver = new DefaultLocalResolver();
6817 }
6818 const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
6819 const outputExpr = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
6820 const stmts = getStatementsFromVisitor(visitor, bindingId);
6821 if (visitor.usesImplicitReceiver) {
6822 localResolver.notifyImplicitReceiverUse();
6823 }
6824 if (visitor.temporaryCount === 0 && form == BindingForm.TrySimple) {
6825 return new ConvertPropertyBindingResult([], outputExpr);
6826 }
6827 else if (form === BindingForm.Expression) {
6828 return new ConvertPropertyBindingResult(stmts, outputExpr);
6829 }
6830 const currValExpr = createCurrValueExpr(bindingId);
6831 stmts.push(currValExpr.set(outputExpr).toDeclStmt(DYNAMIC_TYPE, [StmtModifier.Final]));
6832 return new ConvertPropertyBindingResult(stmts, currValExpr);
6833 }
6834 /**
6835 * Given some expression, such as a binding or interpolation expression, and a context expression to
6836 * look values up on, visit each facet of the given expression resolving values from the context
6837 * expression such that a list of arguments can be derived from the found values that can be used as
6838 * arguments to an external update instruction.
6839 *
6840 * @param localResolver The resolver to use to look up expressions by name appropriately
6841 * @param contextVariableExpression The expression representing the context variable used to create
6842 * the final argument expressions
6843 * @param expressionWithArgumentsToExtract The expression to visit to figure out what values need to
6844 * be resolved and what arguments list to build.
6845 * @param bindingId A name prefix used to create temporary variable names if they're needed for the
6846 * arguments generated
6847 * @returns An array of expressions that can be passed as arguments to instruction expressions like
6848 * `o.importExpr(R3.propertyInterpolate).callFn(result)`
6849 */
6850 function convertUpdateArguments(localResolver, contextVariableExpression, expressionWithArgumentsToExtract, bindingId) {
6851 const visitor = new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, undefined);
6852 const outputExpr = expressionWithArgumentsToExtract.visit(visitor, _Mode.Expression);
6853 if (visitor.usesImplicitReceiver) {
6854 localResolver.notifyImplicitReceiverUse();
6855 }
6856 const stmts = getStatementsFromVisitor(visitor, bindingId);
6857 // Removing the first argument, because it was a length for ViewEngine, not Ivy.
6858 let args = outputExpr.args.slice(1);
6859 if (expressionWithArgumentsToExtract instanceof Interpolation) {
6860 // If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the
6861 // args returned to just the value, because we're going to pass it to a special instruction.
6862 const strings = expressionWithArgumentsToExtract.strings;
6863 if (args.length === 3 && strings[0] === '' && strings[1] === '') {
6864 // Single argument interpolate instructions.
6865 args = [args[1]];
6866 }
6867 else if (args.length >= 19) {
6868 // 19 or more arguments must be passed to the `interpolateV`-style instructions, which accept
6869 // an array of arguments
6870 args = [literalArr(args)];
6871 }
6872 }
6873 return { stmts, args };
6874 }
6875 function getStatementsFromVisitor(visitor, bindingId) {
6876 const stmts = [];
6877 for (let i = 0; i < visitor.temporaryCount; i++) {
6878 stmts.push(temporaryDeclaration(bindingId, i));
6879 }
6880 return stmts;
6881 }
6882 function convertBuiltins(converterFactory, ast) {
6883 const visitor = new _BuiltinAstConverter(converterFactory);
6884 return ast.visit(visitor);
6885 }
6886 function temporaryName(bindingId, temporaryNumber) {
6887 return `tmp_${bindingId}_${temporaryNumber}`;
6888 }
6889 function temporaryDeclaration(bindingId, temporaryNumber) {
6890 return new DeclareVarStmt(temporaryName(bindingId, temporaryNumber), NULL_EXPR);
6891 }
6892 function prependTemporaryDecls(temporaryCount, bindingId, statements) {
6893 for (let i = temporaryCount - 1; i >= 0; i--) {
6894 statements.unshift(temporaryDeclaration(bindingId, i));
6895 }
6896 }
6897 var _Mode;
6898 (function (_Mode) {
6899 _Mode[_Mode["Statement"] = 0] = "Statement";
6900 _Mode[_Mode["Expression"] = 1] = "Expression";
6901 })(_Mode || (_Mode = {}));
6902 function ensureStatementMode(mode, ast) {
6903 if (mode !== _Mode.Statement) {
6904 throw new Error(`Expected a statement, but saw ${ast}`);
6905 }
6906 }
6907 function ensureExpressionMode(mode, ast) {
6908 if (mode !== _Mode.Expression) {
6909 throw new Error(`Expected an expression, but saw ${ast}`);
6910 }
6911 }
6912 function convertToStatementIfNeeded(mode, expr) {
6913 if (mode === _Mode.Statement) {
6914 return expr.toStmt();
6915 }
6916 else {
6917 return expr;
6918 }
6919 }
6920 class _BuiltinAstConverter extends AstTransformer {
6921 constructor(_converterFactory) {
6922 super();
6923 this._converterFactory = _converterFactory;
6924 }
6925 visitPipe(ast, context) {
6926 const args = [ast.exp, ...ast.args].map(ast => ast.visit(this, context));
6927 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createPipeConverter(ast.name, args.length));
6928 }
6929 visitLiteralArray(ast, context) {
6930 const args = ast.expressions.map(ast => ast.visit(this, context));
6931 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralArrayConverter(ast.expressions.length));
6932 }
6933 visitLiteralMap(ast, context) {
6934 const args = ast.values.map(ast => ast.visit(this, context));
6935 return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralMapConverter(ast.keys));
6936 }
6937 }
6938 class _AstToIrVisitor {
6939 constructor(_localResolver, _implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses) {
6940 this._localResolver = _localResolver;
6941 this._implicitReceiver = _implicitReceiver;
6942 this.bindingId = bindingId;
6943 this.interpolationFunction = interpolationFunction;
6944 this.baseSourceSpan = baseSourceSpan;
6945 this.implicitReceiverAccesses = implicitReceiverAccesses;
6946 this._nodeMap = new Map();
6947 this._resultMap = new Map();
6948 this._currentTemporary = 0;
6949 this.temporaryCount = 0;
6950 this.usesImplicitReceiver = false;
6951 }
6952 visitUnary(ast, mode) {
6953 let op;
6954 switch (ast.operator) {
6955 case '+':
6956 op = UnaryOperator.Plus;
6957 break;
6958 case '-':
6959 op = UnaryOperator.Minus;
6960 break;
6961 default:
6962 throw new Error(`Unsupported operator ${ast.operator}`);
6963 }
6964 return convertToStatementIfNeeded(mode, new UnaryOperatorExpr(op, this._visit(ast.expr, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
6965 }
6966 visitBinary(ast, mode) {
6967 let op;
6968 switch (ast.operation) {
6969 case '+':
6970 op = BinaryOperator.Plus;
6971 break;
6972 case '-':
6973 op = BinaryOperator.Minus;
6974 break;
6975 case '*':
6976 op = BinaryOperator.Multiply;
6977 break;
6978 case '/':
6979 op = BinaryOperator.Divide;
6980 break;
6981 case '%':
6982 op = BinaryOperator.Modulo;
6983 break;
6984 case '&&':
6985 op = BinaryOperator.And;
6986 break;
6987 case '||':
6988 op = BinaryOperator.Or;
6989 break;
6990 case '==':
6991 op = BinaryOperator.Equals;
6992 break;
6993 case '!=':
6994 op = BinaryOperator.NotEquals;
6995 break;
6996 case '===':
6997 op = BinaryOperator.Identical;
6998 break;
6999 case '!==':
7000 op = BinaryOperator.NotIdentical;
7001 break;
7002 case '<':
7003 op = BinaryOperator.Lower;
7004 break;
7005 case '>':
7006 op = BinaryOperator.Bigger;
7007 break;
7008 case '<=':
7009 op = BinaryOperator.LowerEquals;
7010 break;
7011 case '>=':
7012 op = BinaryOperator.BiggerEquals;
7013 break;
7014 default:
7015 throw new Error(`Unsupported operation ${ast.operation}`);
7016 }
7017 return convertToStatementIfNeeded(mode, new BinaryOperatorExpr(op, this._visit(ast.left, _Mode.Expression), this._visit(ast.right, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
7018 }
7019 visitChain(ast, mode) {
7020 ensureStatementMode(mode, ast);
7021 return this.visitAll(ast.expressions, mode);
7022 }
7023 visitConditional(ast, mode) {
7024 const value = this._visit(ast.condition, _Mode.Expression);
7025 return convertToStatementIfNeeded(mode, value.conditional(this._visit(ast.trueExp, _Mode.Expression), this._visit(ast.falseExp, _Mode.Expression), this.convertSourceSpan(ast.span)));
7026 }
7027 visitPipe(ast, mode) {
7028 throw new Error(`Illegal state: Pipes should have been converted into functions. Pipe: ${ast.name}`);
7029 }
7030 visitFunctionCall(ast, mode) {
7031 const convertedArgs = this.visitAll(ast.args, _Mode.Expression);
7032 let fnResult;
7033 if (ast instanceof BuiltinFunctionCall) {
7034 fnResult = ast.converter(convertedArgs);
7035 }
7036 else {
7037 fnResult = this._visit(ast.target, _Mode.Expression)
7038 .callFn(convertedArgs, this.convertSourceSpan(ast.span));
7039 }
7040 return convertToStatementIfNeeded(mode, fnResult);
7041 }
7042 visitImplicitReceiver(ast, mode) {
7043 ensureExpressionMode(mode, ast);
7044 this.usesImplicitReceiver = true;
7045 return this._implicitReceiver;
7046 }
7047 visitThisReceiver(ast, mode) {
7048 return this.visitImplicitReceiver(ast, mode);
7049 }
7050 visitInterpolation(ast, mode) {
7051 ensureExpressionMode(mode, ast);
7052 const args = [literal(ast.expressions.length)];
7053 for (let i = 0; i < ast.strings.length - 1; i++) {
7054 args.push(literal(ast.strings[i]));
7055 args.push(this._visit(ast.expressions[i], _Mode.Expression));
7056 }
7057 args.push(literal(ast.strings[ast.strings.length - 1]));
7058 if (this.interpolationFunction) {
7059 return this.interpolationFunction(args);
7060 }
7061 return ast.expressions.length <= 9 ?
7062 importExpr(Identifiers.inlineInterpolate).callFn(args) :
7063 importExpr(Identifiers.interpolate).callFn([
7064 args[0], literalArr(args.slice(1), undefined, this.convertSourceSpan(ast.span))
7065 ]);
7066 }
7067 visitKeyedRead(ast, mode) {
7068 const leftMostSafe = this.leftMostSafeNode(ast);
7069 if (leftMostSafe) {
7070 return this.convertSafeAccess(ast, leftMostSafe, mode);
7071 }
7072 else {
7073 return convertToStatementIfNeeded(mode, this._visit(ast.obj, _Mode.Expression).key(this._visit(ast.key, _Mode.Expression)));
7074 }
7075 }
7076 visitKeyedWrite(ast, mode) {
7077 const obj = this._visit(ast.obj, _Mode.Expression);
7078 const key = this._visit(ast.key, _Mode.Expression);
7079 const value = this._visit(ast.value, _Mode.Expression);
7080 return convertToStatementIfNeeded(mode, obj.key(key).set(value));
7081 }
7082 visitLiteralArray(ast, mode) {
7083 throw new Error(`Illegal State: literal arrays should have been converted into functions`);
7084 }
7085 visitLiteralMap(ast, mode) {
7086 throw new Error(`Illegal State: literal maps should have been converted into functions`);
7087 }
7088 visitLiteralPrimitive(ast, mode) {
7089 // For literal values of null, undefined, true, or false allow type interference
7090 // to infer the type.
7091 const type = ast.value === null || ast.value === undefined || ast.value === true || ast.value === true ?
7092 INFERRED_TYPE :
7093 undefined;
7094 return convertToStatementIfNeeded(mode, literal(ast.value, type, this.convertSourceSpan(ast.span)));
7095 }
7096 _getLocal(name, receiver) {
7097 var _a;
7098 if (((_a = this._localResolver.globals) === null || _a === void 0 ? void 0 : _a.has(name)) && receiver instanceof ThisReceiver) {
7099 return null;
7100 }
7101 return this._localResolver.getLocal(name);
7102 }
7103 visitMethodCall(ast, mode) {
7104 if (ast.receiver instanceof ImplicitReceiver &&
7105 !(ast.receiver instanceof ThisReceiver) && ast.name === '$any') {
7106 const args = this.visitAll(ast.args, _Mode.Expression);
7107 if (args.length != 1) {
7108 throw new Error(`Invalid call to $any, expected 1 argument but received ${args.length || 'none'}`);
7109 }
7110 return args[0].cast(DYNAMIC_TYPE, this.convertSourceSpan(ast.span));
7111 }
7112 const leftMostSafe = this.leftMostSafeNode(ast);
7113 if (leftMostSafe) {
7114 return this.convertSafeAccess(ast, leftMostSafe, mode);
7115 }
7116 else {
7117 const args = this.visitAll(ast.args, _Mode.Expression);
7118 const prevUsesImplicitReceiver = this.usesImplicitReceiver;
7119 let result = null;
7120 const receiver = this._visit(ast.receiver, _Mode.Expression);
7121 if (receiver === this._implicitReceiver) {
7122 const varExpr = this._getLocal(ast.name, ast.receiver);
7123 if (varExpr) {
7124 // Restore the previous "usesImplicitReceiver" state since the implicit
7125 // receiver has been replaced with a resolved local expression.
7126 this.usesImplicitReceiver = prevUsesImplicitReceiver;
7127 result = varExpr.callFn(args);
7128 this.addImplicitReceiverAccess(ast.name);
7129 }
7130 }
7131 if (result == null) {
7132 result = receiver.callMethod(ast.name, args, this.convertSourceSpan(ast.span));
7133 }
7134 return convertToStatementIfNeeded(mode, result);
7135 }
7136 }
7137 visitPrefixNot(ast, mode) {
7138 return convertToStatementIfNeeded(mode, not(this._visit(ast.expression, _Mode.Expression)));
7139 }
7140 visitNonNullAssert(ast, mode) {
7141 return convertToStatementIfNeeded(mode, assertNotNull(this._visit(ast.expression, _Mode.Expression)));
7142 }
7143 visitPropertyRead(ast, mode) {
7144 const leftMostSafe = this.leftMostSafeNode(ast);
7145 if (leftMostSafe) {
7146 return this.convertSafeAccess(ast, leftMostSafe, mode);
7147 }
7148 else {
7149 let result = null;
7150 const prevUsesImplicitReceiver = this.usesImplicitReceiver;
7151 const receiver = this._visit(ast.receiver, _Mode.Expression);
7152 if (receiver === this._implicitReceiver) {
7153 result = this._getLocal(ast.name, ast.receiver);
7154 if (result) {
7155 // Restore the previous "usesImplicitReceiver" state since the implicit
7156 // receiver has been replaced with a resolved local expression.
7157 this.usesImplicitReceiver = prevUsesImplicitReceiver;
7158 this.addImplicitReceiverAccess(ast.name);
7159 }
7160 }
7161 if (result == null) {
7162 result = receiver.prop(ast.name);
7163 }
7164 return convertToStatementIfNeeded(mode, result);
7165 }
7166 }
7167 visitPropertyWrite(ast, mode) {
7168 const receiver = this._visit(ast.receiver, _Mode.Expression);
7169 const prevUsesImplicitReceiver = this.usesImplicitReceiver;
7170 let varExpr = null;
7171 if (receiver === this._implicitReceiver) {
7172 const localExpr = this._getLocal(ast.name, ast.receiver);
7173 if (localExpr) {
7174 if (localExpr instanceof ReadPropExpr) {
7175 // If the local variable is a property read expression, it's a reference
7176 // to a 'context.property' value and will be used as the target of the
7177 // write expression.
7178 varExpr = localExpr;
7179 // Restore the previous "usesImplicitReceiver" state since the implicit
7180 // receiver has been replaced with a resolved local expression.
7181 this.usesImplicitReceiver = prevUsesImplicitReceiver;
7182 this.addImplicitReceiverAccess(ast.name);
7183 }
7184 else {
7185 // Otherwise it's an error.
7186 const receiver = ast.name;
7187 const value = (ast.value instanceof PropertyRead) ? ast.value.name : undefined;
7188 throw new Error(`Cannot assign value "${value}" to template variable "${receiver}". Template variables are read-only.`);
7189 }
7190 }
7191 }
7192 // If no local expression could be produced, use the original receiver's
7193 // property as the target.
7194 if (varExpr === null) {
7195 varExpr = receiver.prop(ast.name);
7196 }
7197 return convertToStatementIfNeeded(mode, varExpr.set(this._visit(ast.value, _Mode.Expression)));
7198 }
7199 visitSafePropertyRead(ast, mode) {
7200 return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
7201 }
7202 visitSafeMethodCall(ast, mode) {
7203 return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
7204 }
7205 visitAll(asts, mode) {
7206 return asts.map(ast => this._visit(ast, mode));
7207 }
7208 visitQuote(ast, mode) {
7209 throw new Error(`Quotes are not supported for evaluation!
7210 Statement: ${ast.uninterpretedExpression} located at ${ast.location}`);
7211 }
7212 _visit(ast, mode) {
7213 const result = this._resultMap.get(ast);
7214 if (result)
7215 return result;
7216 return (this._nodeMap.get(ast) || ast).visit(this, mode);
7217 }
7218 convertSafeAccess(ast, leftMostSafe, mode) {
7219 // If the expression contains a safe access node on the left it needs to be converted to
7220 // an expression that guards the access to the member by checking the receiver for blank. As
7221 // execution proceeds from left to right, the left most part of the expression must be guarded
7222 // first but, because member access is left associative, the right side of the expression is at
7223 // the top of the AST. The desired result requires lifting a copy of the left part of the
7224 // expression up to test it for blank before generating the unguarded version.
7225 // Consider, for example the following expression: a?.b.c?.d.e
7226 // This results in the ast:
7227 // .
7228 // / \
7229 // ?. e
7230 // / \
7231 // . d
7232 // / \
7233 // ?. c
7234 // / \
7235 // a b
7236 // The following tree should be generated:
7237 //
7238 // /---- ? ----\
7239 // / | \
7240 // a /--- ? ---\ null
7241 // / | \
7242 // . . null
7243 // / \ / \
7244 // . c . e
7245 // / \ / \
7246 // a b . d
7247 // / \
7248 // . c
7249 // / \
7250 // a b
7251 //
7252 // Notice that the first guard condition is the left hand of the left most safe access node
7253 // which comes in as leftMostSafe to this routine.
7254 let guardedExpression = this._visit(leftMostSafe.receiver, _Mode.Expression);
7255 let temporary = undefined;
7256 if (this.needsTemporary(leftMostSafe.receiver)) {
7257 // If the expression has method calls or pipes then we need to save the result into a
7258 // temporary variable to avoid calling stateful or impure code more than once.
7259 temporary = this.allocateTemporary();
7260 // Preserve the result in the temporary variable
7261 guardedExpression = temporary.set(guardedExpression);
7262 // Ensure all further references to the guarded expression refer to the temporary instead.
7263 this._resultMap.set(leftMostSafe.receiver, temporary);
7264 }
7265 const condition = guardedExpression.isBlank();
7266 // Convert the ast to an unguarded access to the receiver's member. The map will substitute
7267 // leftMostNode with its unguarded version in the call to `this.visit()`.
7268 if (leftMostSafe instanceof SafeMethodCall) {
7269 this._nodeMap.set(leftMostSafe, new MethodCall(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name, leftMostSafe.args));
7270 }
7271 else {
7272 this._nodeMap.set(leftMostSafe, new PropertyRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name));
7273 }
7274 // Recursively convert the node now without the guarded member access.
7275 const access = this._visit(ast, _Mode.Expression);
7276 // Remove the mapping. This is not strictly required as the converter only traverses each node
7277 // once but is safer if the conversion is changed to traverse the nodes more than once.
7278 this._nodeMap.delete(leftMostSafe);
7279 // If we allocated a temporary, release it.
7280 if (temporary) {
7281 this.releaseTemporary(temporary);
7282 }
7283 // Produce the conditional
7284 return convertToStatementIfNeeded(mode, condition.conditional(literal(null), access));
7285 }
7286 // Given an expression of the form a?.b.c?.d.e then the left most safe node is
7287 // the (a?.b). The . and ?. are left associative thus can be rewritten as:
7288 // ((((a?.c).b).c)?.d).e. This returns the most deeply nested safe read or
7289 // safe method call as this needs to be transformed initially to:
7290 // a == null ? null : a.c.b.c?.d.e
7291 // then to:
7292 // a == null ? null : a.b.c == null ? null : a.b.c.d.e
7293 leftMostSafeNode(ast) {
7294 const visit = (visitor, ast) => {
7295 return (this._nodeMap.get(ast) || ast).visit(visitor);
7296 };
7297 return ast.visit({
7298 visitUnary(ast) {
7299 return null;
7300 },
7301 visitBinary(ast) {
7302 return null;
7303 },
7304 visitChain(ast) {
7305 return null;
7306 },
7307 visitConditional(ast) {
7308 return null;
7309 },
7310 visitFunctionCall(ast) {
7311 return null;
7312 },
7313 visitImplicitReceiver(ast) {
7314 return null;
7315 },
7316 visitThisReceiver(ast) {
7317 return null;
7318 },
7319 visitInterpolation(ast) {
7320 return null;
7321 },
7322 visitKeyedRead(ast) {
7323 return visit(this, ast.obj);
7324 },
7325 visitKeyedWrite(ast) {
7326 return null;
7327 },
7328 visitLiteralArray(ast) {
7329 return null;
7330 },
7331 visitLiteralMap(ast) {
7332 return null;
7333 },
7334 visitLiteralPrimitive(ast) {
7335 return null;
7336 },
7337 visitMethodCall(ast) {
7338 return visit(this, ast.receiver);
7339 },
7340 visitPipe(ast) {
7341 return null;
7342 },
7343 visitPrefixNot(ast) {
7344 return null;
7345 },
7346 visitNonNullAssert(ast) {
7347 return null;
7348 },
7349 visitPropertyRead(ast) {
7350 return visit(this, ast.receiver);
7351 },
7352 visitPropertyWrite(ast) {
7353 return null;
7354 },
7355 visitQuote(ast) {
7356 return null;
7357 },
7358 visitSafeMethodCall(ast) {
7359 return visit(this, ast.receiver) || ast;
7360 },
7361 visitSafePropertyRead(ast) {
7362 return visit(this, ast.receiver) || ast;
7363 }
7364 });
7365 }
7366 // Returns true of the AST includes a method or a pipe indicating that, if the
7367 // expression is used as the target of a safe property or method access then
7368 // the expression should be stored into a temporary variable.
7369 needsTemporary(ast) {
7370 const visit = (visitor, ast) => {
7371 return ast && (this._nodeMap.get(ast) || ast).visit(visitor);
7372 };
7373 const visitSome = (visitor, ast) => {
7374 return ast.some(ast => visit(visitor, ast));
7375 };
7376 return ast.visit({
7377 visitUnary(ast) {
7378 return visit(this, ast.expr);
7379 },
7380 visitBinary(ast) {
7381 return visit(this, ast.left) || visit(this, ast.right);
7382 },
7383 visitChain(ast) {
7384 return false;
7385 },
7386 visitConditional(ast) {
7387 return visit(this, ast.condition) || visit(this, ast.trueExp) || visit(this, ast.falseExp);
7388 },
7389 visitFunctionCall(ast) {
7390 return true;
7391 },
7392 visitImplicitReceiver(ast) {
7393 return false;
7394 },
7395 visitThisReceiver(ast) {
7396 return false;
7397 },
7398 visitInterpolation(ast) {
7399 return visitSome(this, ast.expressions);
7400 },
7401 visitKeyedRead(ast) {
7402 return false;
7403 },
7404 visitKeyedWrite(ast) {
7405 return false;
7406 },
7407 visitLiteralArray(ast) {
7408 return true;
7409 },
7410 visitLiteralMap(ast) {
7411 return true;
7412 },
7413 visitLiteralPrimitive(ast) {
7414 return false;
7415 },
7416 visitMethodCall(ast) {
7417 return true;
7418 },
7419 visitPipe(ast) {
7420 return true;
7421 },
7422 visitPrefixNot(ast) {
7423 return visit(this, ast.expression);
7424 },
7425 visitNonNullAssert(ast) {
7426 return visit(this, ast.expression);
7427 },
7428 visitPropertyRead(ast) {
7429 return false;
7430 },
7431 visitPropertyWrite(ast) {
7432 return false;
7433 },
7434 visitQuote(ast) {
7435 return false;
7436 },
7437 visitSafeMethodCall(ast) {
7438 return true;
7439 },
7440 visitSafePropertyRead(ast) {
7441 return false;
7442 }
7443 });
7444 }
7445 allocateTemporary() {
7446 const tempNumber = this._currentTemporary++;
7447 this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount);
7448 return new ReadVarExpr(temporaryName(this.bindingId, tempNumber));
7449 }
7450 releaseTemporary(temporary) {
7451 this._currentTemporary--;
7452 if (temporary.name != temporaryName(this.bindingId, this._currentTemporary)) {
7453 throw new Error(`Temporary ${temporary.name} released out of order`);
7454 }
7455 }
7456 /**
7457 * Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`.
7458 *
7459 * `ParseSpan` objects are relative to the start of the expression.
7460 * This method converts these to full `ParseSourceSpan` objects that
7461 * show where the span is within the overall source file.
7462 *
7463 * @param span the relative span to convert.
7464 * @returns a `ParseSourceSpan` for the given span or null if no
7465 * `baseSourceSpan` was provided to this class.
7466 */
7467 convertSourceSpan(span) {
7468 if (this.baseSourceSpan) {
7469 const start = this.baseSourceSpan.start.moveBy(span.start);
7470 const end = this.baseSourceSpan.start.moveBy(span.end);
7471 const fullStart = this.baseSourceSpan.fullStart.moveBy(span.start);
7472 return new ParseSourceSpan(start, end, fullStart);
7473 }
7474 else {
7475 return null;
7476 }
7477 }
7478 /** Adds the name of an AST to the list of implicit receiver accesses. */
7479 addImplicitReceiverAccess(name) {
7480 if (this.implicitReceiverAccesses) {
7481 this.implicitReceiverAccesses.add(name);
7482 }
7483 }
7484 }
7485 function flattenStatements(arg, output) {
7486 if (Array.isArray(arg)) {
7487 arg.forEach((entry) => flattenStatements(entry, output));
7488 }
7489 else {
7490 output.push(arg);
7491 }
7492 }
7493 class DefaultLocalResolver {
7494 constructor(globals) {
7495 this.globals = globals;
7496 }
7497 notifyImplicitReceiverUse() { }
7498 getLocal(name) {
7499 if (name === EventHandlerVars.event.name) {
7500 return EventHandlerVars.event;
7501 }
7502 return null;
7503 }
7504 }
7505 function createCurrValueExpr(bindingId) {
7506 return variable(`currVal_${bindingId}`); // fix syntax highlighting: `
7507 }
7508 function createPreventDefaultVar(bindingId) {
7509 return variable(`pd_${bindingId}`);
7510 }
7511 function convertStmtIntoExpression(stmt) {
7512 if (stmt instanceof ExpressionStatement) {
7513 return stmt.expr;
7514 }
7515 else if (stmt instanceof ReturnStatement) {
7516 return stmt.value;
7517 }
7518 return null;
7519 }
7520 class BuiltinFunctionCall extends FunctionCall {
7521 constructor(span, sourceSpan, args, converter) {
7522 super(span, sourceSpan, null, args);
7523 this.args = args;
7524 this.converter = converter;
7525 }
7526 }
7527
7528 /**
7529 * @license
7530 * Copyright Google LLC All Rights Reserved.
7531 *
7532 * Use of this source code is governed by an MIT-style license that can be
7533 * found in the LICENSE file at https://angular.io/license
7534 */
7535 /**
7536 * This file is a port of shadowCSS from webcomponents.js to TypeScript.
7537 *
7538 * Please make sure to keep to edits in sync with the source file.
7539 *
7540 * Source:
7541 * https://github.com/webcomponents/webcomponentsjs/blob/4efecd7e0e/src/ShadowCSS/ShadowCSS.js
7542 *
7543 * The original file level comment is reproduced below
7544 */
7545 /*
7546 This is a limited shim for ShadowDOM css styling.
7547 https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#styles
7548
7549 The intention here is to support only the styling features which can be
7550 relatively simply implemented. The goal is to allow users to avoid the
7551 most obvious pitfalls and do so without compromising performance significantly.
7552 For ShadowDOM styling that's not covered here, a set of best practices
7553 can be provided that should allow users to accomplish more complex styling.
7554
7555 The following is a list of specific ShadowDOM styling features and a brief
7556 discussion of the approach used to shim.
7557
7558 Shimmed features:
7559
7560 * :host, :host-context: ShadowDOM allows styling of the shadowRoot's host
7561 element using the :host rule. To shim this feature, the :host styles are
7562 reformatted and prefixed with a given scope name and promoted to a
7563 document level stylesheet.
7564 For example, given a scope name of .foo, a rule like this:
7565
7566 :host {
7567 background: red;
7568 }
7569 }
7570
7571 becomes:
7572
7573 .foo {
7574 background: red;
7575 }
7576
7577 * encapsulation: Styles defined within ShadowDOM, apply only to
7578 dom inside the ShadowDOM. Polymer uses one of two techniques to implement
7579 this feature.
7580
7581 By default, rules are prefixed with the host element tag name
7582 as a descendant selector. This ensures styling does not leak out of the 'top'
7583 of the element's ShadowDOM. For example,
7584
7585 div {
7586 font-weight: bold;
7587 }
7588
7589 becomes:
7590
7591 x-foo div {
7592 font-weight: bold;
7593 }
7594
7595 becomes:
7596
7597
7598 Alternatively, if WebComponents.ShadowCSS.strictStyling is set to true then
7599 selectors are scoped by adding an attribute selector suffix to each
7600 simple selector that contains the host element tag name. Each element
7601 in the element's ShadowDOM template is also given the scope attribute.
7602 Thus, these rules match only elements that have the scope attribute.
7603 For example, given a scope name of x-foo, a rule like this:
7604
7605 div {
7606 font-weight: bold;
7607 }
7608
7609 becomes:
7610
7611 div[x-foo] {
7612 font-weight: bold;
7613 }
7614
7615 Note that elements that are dynamically added to a scope must have the scope
7616 selector added to them manually.
7617
7618 * upper/lower bound encapsulation: Styles which are defined outside a
7619 shadowRoot should not cross the ShadowDOM boundary and should not apply
7620 inside a shadowRoot.
7621
7622 This styling behavior is not emulated. Some possible ways to do this that
7623 were rejected due to complexity and/or performance concerns include: (1) reset
7624 every possible property for every possible selector for a given scope name;
7625 (2) re-implement css in javascript.
7626
7627 As an alternative, users should make sure to use selectors
7628 specific to the scope in which they are working.
7629
7630 * ::distributed: This behavior is not emulated. It's often not necessary
7631 to style the contents of a specific insertion point and instead, descendants
7632 of the host element can be styled selectively. Users can also create an
7633 extra node around an insertion point and style that node's contents
7634 via descendent selectors. For example, with a shadowRoot like this:
7635
7636 <style>
7637 ::content(div) {
7638 background: red;
7639 }
7640 </style>
7641 <content></content>
7642
7643 could become:
7644
7645 <style>
7646 / *@polyfill .content-container div * /
7647 ::content(div) {
7648 background: red;
7649 }
7650 </style>
7651 <div class="content-container">
7652 <content></content>
7653 </div>
7654
7655 Note the use of @polyfill in the comment above a ShadowDOM specific style
7656 declaration. This is a directive to the styling shim to use the selector
7657 in comments in lieu of the next selector when running under polyfill.
7658 */
7659 class ShadowCss {
7660 constructor() {
7661 this.strictStyling = true;
7662 }
7663 /*
7664 * Shim some cssText with the given selector. Returns cssText that can
7665 * be included in the document via WebComponents.ShadowCSS.addCssToDocument(css).
7666 *
7667 * When strictStyling is true:
7668 * - selector is the attribute added to all elements inside the host,
7669 * - hostSelector is the attribute added to the host itself.
7670 */
7671 shimCssText(cssText, selector, hostSelector = '') {
7672 const commentsWithHash = extractCommentsWithHash(cssText);
7673 cssText = stripComments(cssText);
7674 cssText = this._insertDirectives(cssText);
7675 const scopedCssText = this._scopeCssText(cssText, selector, hostSelector);
7676 return [scopedCssText, ...commentsWithHash].join('\n');
7677 }
7678 _insertDirectives(cssText) {
7679 cssText = this._insertPolyfillDirectivesInCssText(cssText);
7680 return this._insertPolyfillRulesInCssText(cssText);
7681 }
7682 /*
7683 * Process styles to convert native ShadowDOM rules that will trip
7684 * up the css parser; we rely on decorating the stylesheet with inert rules.
7685 *
7686 * For example, we convert this rule:
7687 *
7688 * polyfill-next-selector { content: ':host menu-item'; }
7689 * ::content menu-item {
7690 *
7691 * to this:
7692 *
7693 * scopeName menu-item {
7694 *
7695 **/
7696 _insertPolyfillDirectivesInCssText(cssText) {
7697 // Difference with webcomponents.js: does not handle comments
7698 return cssText.replace(_cssContentNextSelectorRe, function (...m) {
7699 return m[2] + '{';
7700 });
7701 }
7702 /*
7703 * Process styles to add rules which will only apply under the polyfill
7704 *
7705 * For example, we convert this rule:
7706 *
7707 * polyfill-rule {
7708 * content: ':host menu-item';
7709 * ...
7710 * }
7711 *
7712 * to this:
7713 *
7714 * scopeName menu-item {...}
7715 *
7716 **/
7717 _insertPolyfillRulesInCssText(cssText) {
7718 // Difference with webcomponents.js: does not handle comments
7719 return cssText.replace(_cssContentRuleRe, (...m) => {
7720 const rule = m[0].replace(m[1], '').replace(m[2], '');
7721 return m[4] + rule;
7722 });
7723 }
7724 /* Ensure styles are scoped. Pseudo-scoping takes a rule like:
7725 *
7726 * .foo {... }
7727 *
7728 * and converts this to
7729 *
7730 * scopeName .foo { ... }
7731 */
7732 _scopeCssText(cssText, scopeSelector, hostSelector) {
7733 const unscopedRules = this._extractUnscopedRulesFromCssText(cssText);
7734 // replace :host and :host-context -shadowcsshost and -shadowcsshost respectively
7735 cssText = this._insertPolyfillHostInCssText(cssText);
7736 cssText = this._convertColonHost(cssText);
7737 cssText = this._convertColonHostContext(cssText);
7738 cssText = this._convertShadowDOMSelectors(cssText);
7739 if (scopeSelector) {
7740 cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector);
7741 }
7742 cssText = cssText + '\n' + unscopedRules;
7743 return cssText.trim();
7744 }
7745 /*
7746 * Process styles to add rules which will only apply under the polyfill
7747 * and do not process via CSSOM. (CSSOM is destructive to rules on rare
7748 * occasions, e.g. -webkit-calc on Safari.)
7749 * For example, we convert this rule:
7750 *
7751 * @polyfill-unscoped-rule {
7752 * content: 'menu-item';
7753 * ... }
7754 *
7755 * to this:
7756 *
7757 * menu-item {...}
7758 *
7759 **/
7760 _extractUnscopedRulesFromCssText(cssText) {
7761 // Difference with webcomponents.js: does not handle comments
7762 let r = '';
7763 let m;
7764 _cssContentUnscopedRuleRe.lastIndex = 0;
7765 while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) {
7766 const rule = m[0].replace(m[2], '').replace(m[1], m[4]);
7767 r += rule + '\n\n';
7768 }
7769 return r;
7770 }
7771 /*
7772 * convert a rule like :host(.foo) > .bar { }
7773 *
7774 * to
7775 *
7776 * .foo<scopeName> > .bar
7777 */
7778 _convertColonHost(cssText) {
7779 return this._convertColonRule(cssText, _cssColonHostRe, this._colonHostPartReplacer);
7780 }
7781 /*
7782 * convert a rule like :host-context(.foo) > .bar { }
7783 *
7784 * to
7785 *
7786 * .foo<scopeName> > .bar, .foo scopeName > .bar { }
7787 *
7788 * and
7789 *
7790 * :host-context(.foo:host) .bar { ... }
7791 *
7792 * to
7793 *
7794 * .foo<scopeName> .bar { ... }
7795 */
7796 _convertColonHostContext(cssText) {
7797 return this._convertColonRule(cssText, _cssColonHostContextRe, this._colonHostContextPartReplacer);
7798 }
7799 _convertColonRule(cssText, regExp, partReplacer) {
7800 // m[1] = :host(-context), m[2] = contents of (), m[3] rest of rule
7801 return cssText.replace(regExp, function (...m) {
7802 if (m[2]) {
7803 const parts = m[2].split(',');
7804 const r = [];
7805 for (let i = 0; i < parts.length; i++) {
7806 const p = parts[i].trim();
7807 if (!p)
7808 break;
7809 r.push(partReplacer(_polyfillHostNoCombinator, p, m[3]));
7810 }
7811 return r.join(',');
7812 }
7813 else {
7814 return _polyfillHostNoCombinator + m[3];
7815 }
7816 });
7817 }
7818 _colonHostContextPartReplacer(host, part, suffix) {
7819 if (part.indexOf(_polyfillHost) > -1) {
7820 return this._colonHostPartReplacer(host, part, suffix);
7821 }
7822 else {
7823 return host + part + suffix + ', ' + part + ' ' + host + suffix;
7824 }
7825 }
7826 _colonHostPartReplacer(host, part, suffix) {
7827 return host + part.replace(_polyfillHost, '') + suffix;
7828 }
7829 /*
7830 * Convert combinators like ::shadow and pseudo-elements like ::content
7831 * by replacing with space.
7832 */
7833 _convertShadowDOMSelectors(cssText) {
7834 return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText);
7835 }
7836 // change a selector like 'div' to 'name div'
7837 _scopeSelectors(cssText, scopeSelector, hostSelector) {
7838 return processRules(cssText, (rule) => {
7839 let selector = rule.selector;
7840 let content = rule.content;
7841 if (rule.selector[0] != '@') {
7842 selector =
7843 this._scopeSelector(rule.selector, scopeSelector, hostSelector, this.strictStyling);
7844 }
7845 else if (rule.selector.startsWith('@media') || rule.selector.startsWith('@supports') ||
7846 rule.selector.startsWith('@page') || rule.selector.startsWith('@document')) {
7847 content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
7848 }
7849 return new CssRule(selector, content);
7850 });
7851 }
7852 _scopeSelector(selector, scopeSelector, hostSelector, strict) {
7853 return selector.split(',')
7854 .map(part => part.trim().split(_shadowDeepSelectors))
7855 .map((deepParts) => {
7856 const [shallowPart, ...otherParts] = deepParts;
7857 const applyScope = (shallowPart) => {
7858 if (this._selectorNeedsScoping(shallowPart, scopeSelector)) {
7859 return strict ?
7860 this._applyStrictSelectorScope(shallowPart, scopeSelector, hostSelector) :
7861 this._applySelectorScope(shallowPart, scopeSelector, hostSelector);
7862 }
7863 else {
7864 return shallowPart;
7865 }
7866 };
7867 return [applyScope(shallowPart), ...otherParts].join(' ');
7868 })
7869 .join(', ');
7870 }
7871 _selectorNeedsScoping(selector, scopeSelector) {
7872 const re = this._makeScopeMatcher(scopeSelector);
7873 return !re.test(selector);
7874 }
7875 _makeScopeMatcher(scopeSelector) {
7876 const lre = /\[/g;
7877 const rre = /\]/g;
7878 scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]');
7879 return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm');
7880 }
7881 _applySelectorScope(selector, scopeSelector, hostSelector) {
7882 // Difference from webcomponents.js: scopeSelector could not be an array
7883 return this._applySimpleSelectorScope(selector, scopeSelector, hostSelector);
7884 }
7885 // scope via name and [is=name]
7886 _applySimpleSelectorScope(selector, scopeSelector, hostSelector) {
7887 // In Android browser, the lastIndex is not reset when the regex is used in String.replace()
7888 _polyfillHostRe.lastIndex = 0;
7889 if (_polyfillHostRe.test(selector)) {
7890 const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
7891 return selector
7892 .replace(_polyfillHostNoCombinatorRe, (hnc, selector) => {
7893 return selector.replace(/([^:]*)(:*)(.*)/, (_, before, colon, after) => {
7894 return before + replaceBy + colon + after;
7895 });
7896 })
7897 .replace(_polyfillHostRe, replaceBy + ' ');
7898 }
7899 return scopeSelector + ' ' + selector;
7900 }
7901 // return a selector with [name] suffix on each simple selector
7902 // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] /** @internal */
7903 _applyStrictSelectorScope(selector, scopeSelector, hostSelector) {
7904 const isRe = /\[is=([^\]]*)\]/g;
7905 scopeSelector = scopeSelector.replace(isRe, (_, ...parts) => parts[0]);
7906 const attrName = '[' + scopeSelector + ']';
7907 const _scopeSelectorPart = (p) => {
7908 let scopedP = p.trim();
7909 if (!scopedP) {
7910 return '';
7911 }
7912 if (p.indexOf(_polyfillHostNoCombinator) > -1) {
7913 scopedP = this._applySimpleSelectorScope(p, scopeSelector, hostSelector);
7914 }
7915 else {
7916 // remove :host since it should be unnecessary
7917 const t = p.replace(_polyfillHostRe, '');
7918 if (t.length > 0) {
7919 const matches = t.match(/([^:]*)(:*)(.*)/);
7920 if (matches) {
7921 scopedP = matches[1] + attrName + matches[2] + matches[3];
7922 }
7923 }
7924 }
7925 return scopedP;
7926 };
7927 const safeContent = new SafeSelector(selector);
7928 selector = safeContent.content();
7929 let scopedSelector = '';
7930 let startIndex = 0;
7931 let res;
7932 const sep = /( |>|\+|~(?!=))\s*/g;
7933 // If a selector appears before :host it should not be shimmed as it
7934 // matches on ancestor elements and not on elements in the host's shadow
7935 // `:host-context(div)` is transformed to
7936 // `-shadowcsshost-no-combinatordiv, div -shadowcsshost-no-combinator`
7937 // the `div` is not part of the component in the 2nd selectors and should not be scoped.
7938 // Historically `component-tag:host` was matching the component so we also want to preserve
7939 // this behavior to avoid breaking legacy apps (it should not match).
7940 // The behavior should be:
7941 // - `tag:host` -> `tag[h]` (this is to avoid breaking legacy apps, should not match anything)
7942 // - `tag :host` -> `tag [h]` (`tag` is not scoped because it's considered part of a
7943 // `:host-context(tag)`)
7944 const hasHost = selector.indexOf(_polyfillHostNoCombinator) > -1;
7945 // Only scope parts after the first `-shadowcsshost-no-combinator` when it is present
7946 let shouldScope = !hasHost;
7947 while ((res = sep.exec(selector)) !== null) {
7948 const separator = res[1];
7949 const part = selector.slice(startIndex, res.index).trim();
7950 shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
7951 const scopedPart = shouldScope ? _scopeSelectorPart(part) : part;
7952 scopedSelector += `${scopedPart} ${separator} `;
7953 startIndex = sep.lastIndex;
7954 }
7955 const part = selector.substring(startIndex);
7956 shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
7957 scopedSelector += shouldScope ? _scopeSelectorPart(part) : part;
7958 // replace the placeholders with their original values
7959 return safeContent.restore(scopedSelector);
7960 }
7961 _insertPolyfillHostInCssText(selector) {
7962 return selector.replace(_colonHostContextRe, _polyfillHostContext)
7963 .replace(_colonHostRe, _polyfillHost);
7964 }
7965 }
7966 class SafeSelector {
7967 constructor(selector) {
7968 this.placeholders = [];
7969 this.index = 0;
7970 // Replaces attribute selectors with placeholders.
7971 // The WS in [attr="va lue"] would otherwise be interpreted as a selector separator.
7972 selector = this._escapeRegexMatches(selector, /(\[[^\]]*\])/g);
7973 // CSS allows for certain special characters to be used in selectors if they're escaped.
7974 // E.g. `.foo:blue` won't match a class called `foo:blue`, because the colon denotes a
7975 // pseudo-class, but writing `.foo\:blue` will match, because the colon was escaped.
7976 // Replace all escape sequences (`\` followed by a character) with a placeholder so
7977 // that our handling of pseudo-selectors doesn't mess with them.
7978 selector = this._escapeRegexMatches(selector, /(\\.)/g);
7979 // Replaces the expression in `:nth-child(2n + 1)` with a placeholder.
7980 // WS and "+" would otherwise be interpreted as selector separators.
7981 this._content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => {
7982 const replaceBy = `__ph-${this.index}__`;
7983 this.placeholders.push(exp);
7984 this.index++;
7985 return pseudo + replaceBy;
7986 });
7987 }
7988 restore(content) {
7989 return content.replace(/__ph-(\d+)__/g, (_ph, index) => this.placeholders[+index]);
7990 }
7991 content() {
7992 return this._content;
7993 }
7994 /**
7995 * Replaces all of the substrings that match a regex within a
7996 * special string (e.g. `__ph-0__`, `__ph-1__`, etc).
7997 */
7998 _escapeRegexMatches(content, pattern) {
7999 return content.replace(pattern, (_, keep) => {
8000 const replaceBy = `__ph-${this.index}__`;
8001 this.placeholders.push(keep);
8002 this.index++;
8003 return replaceBy;
8004 });
8005 }
8006 }
8007 const _cssContentNextSelectorRe = /polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
8008 const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
8009 const _cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
8010 const _polyfillHost = '-shadowcsshost';
8011 // note: :host-context pre-processed to -shadowcsshostcontext.
8012 const _polyfillHostContext = '-shadowcsscontext';
8013 const _parenSuffix = ')(?:\\((' +
8014 '(?:\\([^)(]*\\)|[^)(]*)+?' +
8015 ')\\))?([^,{]*)';
8016 const _cssColonHostRe = new RegExp('(' + _polyfillHost + _parenSuffix, 'gim');
8017 const _cssColonHostContextRe = new RegExp('(' + _polyfillHostContext + _parenSuffix, 'gim');
8018 const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';
8019 const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/;
8020 const _shadowDOMSelectorsRe = [
8021 /::shadow/g,
8022 /::content/g,
8023 // Deprecated selectors
8024 /\/shadow-deep\//g,
8025 /\/shadow\//g,
8026 ];
8027 // The deep combinator is deprecated in the CSS spec
8028 // Support for `>>>`, `deep`, `::ng-deep` is then also deprecated and will be removed in the future.
8029 // see https://github.com/angular/angular/pull/17677
8030 const _shadowDeepSelectors = /(?:>>>)|(?:\/deep\/)|(?:::ng-deep)/g;
8031 const _selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$';
8032 const _polyfillHostRe = /-shadowcsshost/gim;
8033 const _colonHostRe = /:host/gim;
8034 const _colonHostContextRe = /:host-context/gim;
8035 const _commentRe = /\/\*\s*[\s\S]*?\*\//g;
8036 function stripComments(input) {
8037 return input.replace(_commentRe, '');
8038 }
8039 const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=[\s\S]+?\*\//g;
8040 function extractCommentsWithHash(input) {
8041 return input.match(_commentWithHashRe) || [];
8042 }
8043 const BLOCK_PLACEHOLDER = '%BLOCK%';
8044 const QUOTE_PLACEHOLDER = '%QUOTED%';
8045 const _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g;
8046 const _quotedRe = /%QUOTED%/g;
8047 const CONTENT_PAIRS = new Map([['{', '}']]);
8048 const QUOTE_PAIRS = new Map([[`"`, `"`], [`'`, `'`]]);
8049 class CssRule {
8050 constructor(selector, content) {
8051 this.selector = selector;
8052 this.content = content;
8053 }
8054 }
8055 function processRules(input, ruleCallback) {
8056 const inputWithEscapedQuotes = escapeBlocks(input, QUOTE_PAIRS, QUOTE_PLACEHOLDER);
8057 const inputWithEscapedBlocks = escapeBlocks(inputWithEscapedQuotes.escapedString, CONTENT_PAIRS, BLOCK_PLACEHOLDER);
8058 let nextBlockIndex = 0;
8059 let nextQuoteIndex = 0;
8060 return inputWithEscapedBlocks.escapedString
8061 .replace(_ruleRe, (...m) => {
8062 const selector = m[2];
8063 let content = '';
8064 let suffix = m[4];
8065 let contentPrefix = '';
8066 if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) {
8067 content = inputWithEscapedBlocks.blocks[nextBlockIndex++];
8068 suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1);
8069 contentPrefix = '{';
8070 }
8071 const rule = ruleCallback(new CssRule(selector, content));
8072 return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`;
8073 })
8074 .replace(_quotedRe, () => inputWithEscapedQuotes.blocks[nextQuoteIndex++]);
8075 }
8076 class StringWithEscapedBlocks {
8077 constructor(escapedString, blocks) {
8078 this.escapedString = escapedString;
8079 this.blocks = blocks;
8080 }
8081 }
8082 function escapeBlocks(input, charPairs, placeholder) {
8083 const resultParts = [];
8084 const escapedBlocks = [];
8085 let openCharCount = 0;
8086 let nonBlockStartIndex = 0;
8087 let blockStartIndex = -1;
8088 let openChar;
8089 let closeChar;
8090 for (let i = 0; i < input.length; i++) {
8091 const char = input[i];
8092 if (char === '\\') {
8093 i++;
8094 }
8095 else if (char === closeChar) {
8096 openCharCount--;
8097 if (openCharCount === 0) {
8098 escapedBlocks.push(input.substring(blockStartIndex, i));
8099 resultParts.push(placeholder);
8100 nonBlockStartIndex = i;
8101 blockStartIndex = -1;
8102 openChar = closeChar = undefined;
8103 }
8104 }
8105 else if (char === openChar) {
8106 openCharCount++;
8107 }
8108 else if (openCharCount === 0 && charPairs.has(char)) {
8109 openChar = char;
8110 closeChar = charPairs.get(char);
8111 openCharCount = 1;
8112 blockStartIndex = i + 1;
8113 resultParts.push(input.substring(nonBlockStartIndex, blockStartIndex));
8114 }
8115 }
8116 if (blockStartIndex !== -1) {
8117 escapedBlocks.push(input.substring(blockStartIndex));
8118 resultParts.push(placeholder);
8119 }
8120 else {
8121 resultParts.push(input.substring(nonBlockStartIndex));
8122 }
8123 return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks);
8124 }
8125
8126 /**
8127 * @license
8128 * Copyright Google LLC All Rights Reserved.
8129 *
8130 * Use of this source code is governed by an MIT-style license that can be
8131 * found in the LICENSE file at https://angular.io/license
8132 */
8133 const COMPONENT_VARIABLE = '%COMP%';
8134 const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
8135 const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
8136
8137 /**
8138 * @license
8139 * Copyright Google LLC All Rights Reserved.
8140 *
8141 * Use of this source code is governed by an MIT-style license that can be
8142 * found in the LICENSE file at https://angular.io/license
8143 */
8144 class NodeWithI18n {
8145 constructor(sourceSpan, i18n) {
8146 this.sourceSpan = sourceSpan;
8147 this.i18n = i18n;
8148 }
8149 }
8150 class Text$2 extends NodeWithI18n {
8151 constructor(value, sourceSpan, i18n) {
8152 super(sourceSpan, i18n);
8153 this.value = value;
8154 }
8155 visit(visitor, context) {
8156 return visitor.visitText(this, context);
8157 }
8158 }
8159 class Expansion extends NodeWithI18n {
8160 constructor(switchValue, type, cases, sourceSpan, switchValueSourceSpan, i18n) {
8161 super(sourceSpan, i18n);
8162 this.switchValue = switchValue;
8163 this.type = type;
8164 this.cases = cases;
8165 this.switchValueSourceSpan = switchValueSourceSpan;
8166 }
8167 visit(visitor, context) {
8168 return visitor.visitExpansion(this, context);
8169 }
8170 }
8171 class ExpansionCase {
8172 constructor(value, expression, sourceSpan, valueSourceSpan, expSourceSpan) {
8173 this.value = value;
8174 this.expression = expression;
8175 this.sourceSpan = sourceSpan;
8176 this.valueSourceSpan = valueSourceSpan;
8177 this.expSourceSpan = expSourceSpan;
8178 }
8179 visit(visitor, context) {
8180 return visitor.visitExpansionCase(this, context);
8181 }
8182 }
8183 class Attribute extends NodeWithI18n {
8184 constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) {
8185 super(sourceSpan, i18n);
8186 this.name = name;
8187 this.value = value;
8188 this.keySpan = keySpan;
8189 this.valueSpan = valueSpan;
8190 }
8191 visit(visitor, context) {
8192 return visitor.visitAttribute(this, context);
8193 }
8194 }
8195 class Element$1 extends NodeWithI18n {
8196 constructor(name, attrs, children, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) {
8197 super(sourceSpan, i18n);
8198 this.name = name;
8199 this.attrs = attrs;
8200 this.children = children;
8201 this.startSourceSpan = startSourceSpan;
8202 this.endSourceSpan = endSourceSpan;
8203 }
8204 visit(visitor, context) {
8205 return visitor.visitElement(this, context);
8206 }
8207 }
8208 class Comment {
8209 constructor(value, sourceSpan) {
8210 this.value = value;
8211 this.sourceSpan = sourceSpan;
8212 }
8213 visit(visitor, context) {
8214 return visitor.visitComment(this, context);
8215 }
8216 }
8217 function visitAll$1(visitor, nodes, context = null) {
8218 const result = [];
8219 const visit = visitor.visit ?
8220 (ast) => visitor.visit(ast, context) || ast.visit(visitor, context) :
8221 (ast) => ast.visit(visitor, context);
8222 nodes.forEach(ast => {
8223 const astResult = visit(ast);
8224 if (astResult) {
8225 result.push(astResult);
8226 }
8227 });
8228 return result;
8229 }
8230
8231 /**
8232 * @license
8233 * Copyright Google LLC All Rights Reserved.
8234 *
8235 * Use of this source code is governed by an MIT-style license that can be
8236 * found in the LICENSE file at https://angular.io/license
8237 */
8238 var TokenType;
8239 (function (TokenType) {
8240 TokenType[TokenType["TAG_OPEN_START"] = 0] = "TAG_OPEN_START";
8241 TokenType[TokenType["TAG_OPEN_END"] = 1] = "TAG_OPEN_END";
8242 TokenType[TokenType["TAG_OPEN_END_VOID"] = 2] = "TAG_OPEN_END_VOID";
8243 TokenType[TokenType["TAG_CLOSE"] = 3] = "TAG_CLOSE";
8244 TokenType[TokenType["INCOMPLETE_TAG_OPEN"] = 4] = "INCOMPLETE_TAG_OPEN";
8245 TokenType[TokenType["TEXT"] = 5] = "TEXT";
8246 TokenType[TokenType["ESCAPABLE_RAW_TEXT"] = 6] = "ESCAPABLE_RAW_TEXT";
8247 TokenType[TokenType["RAW_TEXT"] = 7] = "RAW_TEXT";
8248 TokenType[TokenType["COMMENT_START"] = 8] = "COMMENT_START";
8249 TokenType[TokenType["COMMENT_END"] = 9] = "COMMENT_END";
8250 TokenType[TokenType["CDATA_START"] = 10] = "CDATA_START";
8251 TokenType[TokenType["CDATA_END"] = 11] = "CDATA_END";
8252 TokenType[TokenType["ATTR_NAME"] = 12] = "ATTR_NAME";
8253 TokenType[TokenType["ATTR_QUOTE"] = 13] = "ATTR_QUOTE";
8254 TokenType[TokenType["ATTR_VALUE"] = 14] = "ATTR_VALUE";
8255 TokenType[TokenType["DOC_TYPE"] = 15] = "DOC_TYPE";
8256 TokenType[TokenType["EXPANSION_FORM_START"] = 16] = "EXPANSION_FORM_START";
8257 TokenType[TokenType["EXPANSION_CASE_VALUE"] = 17] = "EXPANSION_CASE_VALUE";
8258 TokenType[TokenType["EXPANSION_CASE_EXP_START"] = 18] = "EXPANSION_CASE_EXP_START";
8259 TokenType[TokenType["EXPANSION_CASE_EXP_END"] = 19] = "EXPANSION_CASE_EXP_END";
8260 TokenType[TokenType["EXPANSION_FORM_END"] = 20] = "EXPANSION_FORM_END";
8261 TokenType[TokenType["EOF"] = 21] = "EOF";
8262 })(TokenType || (TokenType = {}));
8263 class Token {
8264 constructor(type, parts, sourceSpan) {
8265 this.type = type;
8266 this.parts = parts;
8267 this.sourceSpan = sourceSpan;
8268 }
8269 }
8270 class TokenError extends ParseError {
8271 constructor(errorMsg, tokenType, span) {
8272 super(span, errorMsg);
8273 this.tokenType = tokenType;
8274 }
8275 }
8276 class TokenizeResult {
8277 constructor(tokens, errors, nonNormalizedIcuExpressions) {
8278 this.tokens = tokens;
8279 this.errors = errors;
8280 this.nonNormalizedIcuExpressions = nonNormalizedIcuExpressions;
8281 }
8282 }
8283 function tokenize(source, url, getTagDefinition, options = {}) {
8284 const tokenizer = new _Tokenizer(new ParseSourceFile(source, url), getTagDefinition, options);
8285 tokenizer.tokenize();
8286 return new TokenizeResult(mergeTextTokens(tokenizer.tokens), tokenizer.errors, tokenizer.nonNormalizedIcuExpressions);
8287 }
8288 const _CR_OR_CRLF_REGEXP = /\r\n?/g;
8289 function _unexpectedCharacterErrorMsg(charCode) {
8290 const char = charCode === $EOF ? 'EOF' : String.fromCharCode(charCode);
8291 return `Unexpected character "${char}"`;
8292 }
8293 function _unknownEntityErrorMsg(entitySrc) {
8294 return `Unknown entity "${entitySrc}" - use the "&#<decimal>;" or "&#x<hex>;" syntax`;
8295 }
8296 function _unparsableEntityErrorMsg(type, entityStr) {
8297 return `Unable to parse entity "${entityStr}" - ${type} character reference entities must end with ";"`;
8298 }
8299 var CharacterReferenceType;
8300 (function (CharacterReferenceType) {
8301 CharacterReferenceType["HEX"] = "hexadecimal";
8302 CharacterReferenceType["DEC"] = "decimal";
8303 })(CharacterReferenceType || (CharacterReferenceType = {}));
8304 class _ControlFlowError {
8305 constructor(error) {
8306 this.error = error;
8307 }
8308 }
8309 // See https://www.w3.org/TR/html51/syntax.html#writing-html-documents
8310 class _Tokenizer {
8311 /**
8312 * @param _file The html source file being tokenized.
8313 * @param _getTagDefinition A function that will retrieve a tag definition for a given tag name.
8314 * @param options Configuration of the tokenization.
8315 */
8316 constructor(_file, _getTagDefinition, options) {
8317 this._getTagDefinition = _getTagDefinition;
8318 this._currentTokenStart = null;
8319 this._currentTokenType = null;
8320 this._expansionCaseStack = [];
8321 this._inInterpolation = false;
8322 this.tokens = [];
8323 this.errors = [];
8324 this.nonNormalizedIcuExpressions = [];
8325 this._tokenizeIcu = options.tokenizeExpansionForms || false;
8326 this._interpolationConfig = options.interpolationConfig || DEFAULT_INTERPOLATION_CONFIG;
8327 this._leadingTriviaCodePoints =
8328 options.leadingTriviaChars && options.leadingTriviaChars.map(c => c.codePointAt(0) || 0);
8329 const range = options.range || { endPos: _file.content.length, startPos: 0, startLine: 0, startCol: 0 };
8330 this._cursor = options.escapedString ? new EscapedCharacterCursor(_file, range) :
8331 new PlainCharacterCursor(_file, range);
8332 this._preserveLineEndings = options.preserveLineEndings || false;
8333 this._escapedString = options.escapedString || false;
8334 this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
8335 try {
8336 this._cursor.init();
8337 }
8338 catch (e) {
8339 this.handleError(e);
8340 }
8341 }
8342 _processCarriageReturns(content) {
8343 if (this._preserveLineEndings) {
8344 return content;
8345 }
8346 // https://www.w3.org/TR/html51/syntax.html#preprocessing-the-input-stream
8347 // In order to keep the original position in the source, we can not
8348 // pre-process it.
8349 // Instead CRs are processed right before instantiating the tokens.
8350 return content.replace(_CR_OR_CRLF_REGEXP, '\n');
8351 }
8352 tokenize() {
8353 while (this._cursor.peek() !== $EOF) {
8354 const start = this._cursor.clone();
8355 try {
8356 if (this._attemptCharCode($LT)) {
8357 if (this._attemptCharCode($BANG)) {
8358 if (this._attemptCharCode($LBRACKET)) {
8359 this._consumeCdata(start);
8360 }
8361 else if (this._attemptCharCode($MINUS)) {
8362 this._consumeComment(start);
8363 }
8364 else {
8365 this._consumeDocType(start);
8366 }
8367 }
8368 else if (this._attemptCharCode($SLASH)) {
8369 this._consumeTagClose(start);
8370 }
8371 else {
8372 this._consumeTagOpen(start);
8373 }
8374 }
8375 else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {
8376 this._consumeText();
8377 }
8378 }
8379 catch (e) {
8380 this.handleError(e);
8381 }
8382 }
8383 this._beginToken(TokenType.EOF);
8384 this._endToken([]);
8385 }
8386 /**
8387 * @returns whether an ICU token has been created
8388 * @internal
8389 */
8390 _tokenizeExpansionForm() {
8391 if (this.isExpansionFormStart()) {
8392 this._consumeExpansionFormStart();
8393 return true;
8394 }
8395 if (isExpansionCaseStart(this._cursor.peek()) && this._isInExpansionForm()) {
8396 this._consumeExpansionCaseStart();
8397 return true;
8398 }
8399 if (this._cursor.peek() === $RBRACE) {
8400 if (this._isInExpansionCase()) {
8401 this._consumeExpansionCaseEnd();
8402 return true;
8403 }
8404 if (this._isInExpansionForm()) {
8405 this._consumeExpansionFormEnd();
8406 return true;
8407 }
8408 }
8409 return false;
8410 }
8411 _beginToken(type, start = this._cursor.clone()) {
8412 this._currentTokenStart = start;
8413 this._currentTokenType = type;
8414 }
8415 _endToken(parts, end) {
8416 if (this._currentTokenStart === null) {
8417 throw new TokenError('Programming error - attempted to end a token when there was no start to the token', this._currentTokenType, this._cursor.getSpan(end));
8418 }
8419 if (this._currentTokenType === null) {
8420 throw new TokenError('Programming error - attempted to end a token which has no token type', null, this._cursor.getSpan(this._currentTokenStart));
8421 }
8422 const token = new Token(this._currentTokenType, parts, this._cursor.getSpan(this._currentTokenStart, this._leadingTriviaCodePoints));
8423 this.tokens.push(token);
8424 this._currentTokenStart = null;
8425 this._currentTokenType = null;
8426 return token;
8427 }
8428 _createError(msg, span) {
8429 if (this._isInExpansionForm()) {
8430 msg += ` (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)`;
8431 }
8432 const error = new TokenError(msg, this._currentTokenType, span);
8433 this._currentTokenStart = null;
8434 this._currentTokenType = null;
8435 return new _ControlFlowError(error);
8436 }
8437 handleError(e) {
8438 if (e instanceof CursorError) {
8439 e = this._createError(e.msg, this._cursor.getSpan(e.cursor));
8440 }
8441 if (e instanceof _ControlFlowError) {
8442 this.errors.push(e.error);
8443 }
8444 else {
8445 throw e;
8446 }
8447 }
8448 _attemptCharCode(charCode) {
8449 if (this._cursor.peek() === charCode) {
8450 this._cursor.advance();
8451 return true;
8452 }
8453 return false;
8454 }
8455 _attemptCharCodeCaseInsensitive(charCode) {
8456 if (compareCharCodeCaseInsensitive(this._cursor.peek(), charCode)) {
8457 this._cursor.advance();
8458 return true;
8459 }
8460 return false;
8461 }
8462 _requireCharCode(charCode) {
8463 const location = this._cursor.clone();
8464 if (!this._attemptCharCode(charCode)) {
8465 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
8466 }
8467 }
8468 _attemptStr(chars) {
8469 const len = chars.length;
8470 if (this._cursor.charsLeft() < len) {
8471 return false;
8472 }
8473 const initialPosition = this._cursor.clone();
8474 for (let i = 0; i < len; i++) {
8475 if (!this._attemptCharCode(chars.charCodeAt(i))) {
8476 // If attempting to parse the string fails, we want to reset the parser
8477 // to where it was before the attempt
8478 this._cursor = initialPosition;
8479 return false;
8480 }
8481 }
8482 return true;
8483 }
8484 _attemptStrCaseInsensitive(chars) {
8485 for (let i = 0; i < chars.length; i++) {
8486 if (!this._attemptCharCodeCaseInsensitive(chars.charCodeAt(i))) {
8487 return false;
8488 }
8489 }
8490 return true;
8491 }
8492 _requireStr(chars) {
8493 const location = this._cursor.clone();
8494 if (!this._attemptStr(chars)) {
8495 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
8496 }
8497 }
8498 _attemptCharCodeUntilFn(predicate) {
8499 while (!predicate(this._cursor.peek())) {
8500 this._cursor.advance();
8501 }
8502 }
8503 _requireCharCodeUntilFn(predicate, len) {
8504 const start = this._cursor.clone();
8505 this._attemptCharCodeUntilFn(predicate);
8506 if (this._cursor.diff(start) < len) {
8507 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
8508 }
8509 }
8510 _attemptUntilChar(char) {
8511 while (this._cursor.peek() !== char) {
8512 this._cursor.advance();
8513 }
8514 }
8515 _readChar(decodeEntities) {
8516 if (decodeEntities && this._cursor.peek() === $AMPERSAND) {
8517 return this._decodeEntity();
8518 }
8519 else {
8520 // Don't rely upon reading directly from `_input` as the actual char value
8521 // may have been generated from an escape sequence.
8522 const char = String.fromCodePoint(this._cursor.peek());
8523 this._cursor.advance();
8524 return char;
8525 }
8526 }
8527 _decodeEntity() {
8528 const start = this._cursor.clone();
8529 this._cursor.advance();
8530 if (this._attemptCharCode($HASH)) {
8531 const isHex = this._attemptCharCode($x) || this._attemptCharCode($X);
8532 const codeStart = this._cursor.clone();
8533 this._attemptCharCodeUntilFn(isDigitEntityEnd);
8534 if (this._cursor.peek() != $SEMICOLON) {
8535 // Advance cursor to include the peeked character in the string provided to the error
8536 // message.
8537 this._cursor.advance();
8538 const entityType = isHex ? CharacterReferenceType.HEX : CharacterReferenceType.DEC;
8539 throw this._createError(_unparsableEntityErrorMsg(entityType, this._cursor.getChars(start)), this._cursor.getSpan());
8540 }
8541 const strNum = this._cursor.getChars(codeStart);
8542 this._cursor.advance();
8543 try {
8544 const charCode = parseInt(strNum, isHex ? 16 : 10);
8545 return String.fromCharCode(charCode);
8546 }
8547 catch (_a) {
8548 throw this._createError(_unknownEntityErrorMsg(this._cursor.getChars(start)), this._cursor.getSpan());
8549 }
8550 }
8551 else {
8552 const nameStart = this._cursor.clone();
8553 this._attemptCharCodeUntilFn(isNamedEntityEnd);
8554 if (this._cursor.peek() != $SEMICOLON) {
8555 this._cursor = nameStart;
8556 return '&';
8557 }
8558 const name = this._cursor.getChars(nameStart);
8559 this._cursor.advance();
8560 const char = NAMED_ENTITIES[name];
8561 if (!char) {
8562 throw this._createError(_unknownEntityErrorMsg(name), this._cursor.getSpan(start));
8563 }
8564 return char;
8565 }
8566 }
8567 _consumeRawText(decodeEntities, endMarkerPredicate) {
8568 this._beginToken(decodeEntities ? TokenType.ESCAPABLE_RAW_TEXT : TokenType.RAW_TEXT);
8569 const parts = [];
8570 while (true) {
8571 const tagCloseStart = this._cursor.clone();
8572 const foundEndMarker = endMarkerPredicate();
8573 this._cursor = tagCloseStart;
8574 if (foundEndMarker) {
8575 break;
8576 }
8577 parts.push(this._readChar(decodeEntities));
8578 }
8579 return this._endToken([this._processCarriageReturns(parts.join(''))]);
8580 }
8581 _consumeComment(start) {
8582 this._beginToken(TokenType.COMMENT_START, start);
8583 this._requireCharCode($MINUS);
8584 this._endToken([]);
8585 this._consumeRawText(false, () => this._attemptStr('-->'));
8586 this._beginToken(TokenType.COMMENT_END);
8587 this._requireStr('-->');
8588 this._endToken([]);
8589 }
8590 _consumeCdata(start) {
8591 this._beginToken(TokenType.CDATA_START, start);
8592 this._requireStr('CDATA[');
8593 this._endToken([]);
8594 this._consumeRawText(false, () => this._attemptStr(']]>'));
8595 this._beginToken(TokenType.CDATA_END);
8596 this._requireStr(']]>');
8597 this._endToken([]);
8598 }
8599 _consumeDocType(start) {
8600 this._beginToken(TokenType.DOC_TYPE, start);
8601 const contentStart = this._cursor.clone();
8602 this._attemptUntilChar($GT);
8603 const content = this._cursor.getChars(contentStart);
8604 this._cursor.advance();
8605 this._endToken([content]);
8606 }
8607 _consumePrefixAndName() {
8608 const nameOrPrefixStart = this._cursor.clone();
8609 let prefix = '';
8610 while (this._cursor.peek() !== $COLON && !isPrefixEnd(this._cursor.peek())) {
8611 this._cursor.advance();
8612 }
8613 let nameStart;
8614 if (this._cursor.peek() === $COLON) {
8615 prefix = this._cursor.getChars(nameOrPrefixStart);
8616 this._cursor.advance();
8617 nameStart = this._cursor.clone();
8618 }
8619 else {
8620 nameStart = nameOrPrefixStart;
8621 }
8622 this._requireCharCodeUntilFn(isNameEnd, prefix === '' ? 0 : 1);
8623 const name = this._cursor.getChars(nameStart);
8624 return [prefix, name];
8625 }
8626 _consumeTagOpen(start) {
8627 let tagName;
8628 let prefix;
8629 let openTagToken;
8630 try {
8631 if (!isAsciiLetter(this._cursor.peek())) {
8632 throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
8633 }
8634 openTagToken = this._consumeTagOpenStart(start);
8635 prefix = openTagToken.parts[0];
8636 tagName = openTagToken.parts[1];
8637 this._attemptCharCodeUntilFn(isNotWhitespace);
8638 while (this._cursor.peek() !== $SLASH && this._cursor.peek() !== $GT &&
8639 this._cursor.peek() !== $LT) {
8640 this._consumeAttributeName();
8641 this._attemptCharCodeUntilFn(isNotWhitespace);
8642 if (this._attemptCharCode($EQ)) {
8643 this._attemptCharCodeUntilFn(isNotWhitespace);
8644 this._consumeAttributeValue();
8645 }
8646 this._attemptCharCodeUntilFn(isNotWhitespace);
8647 }
8648 this._consumeTagOpenEnd();
8649 }
8650 catch (e) {
8651 if (e instanceof _ControlFlowError) {
8652 if (openTagToken) {
8653 // We errored before we could close the opening tag, so it is incomplete.
8654 openTagToken.type = TokenType.INCOMPLETE_TAG_OPEN;
8655 }
8656 else {
8657 // When the start tag is invalid, assume we want a "<" as text.
8658 // Back to back text tokens are merged at the end.
8659 this._beginToken(TokenType.TEXT, start);
8660 this._endToken(['<']);
8661 }
8662 return;
8663 }
8664 throw e;
8665 }
8666 const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix);
8667 if (contentTokenType === TagContentType.RAW_TEXT) {
8668 this._consumeRawTextWithTagClose(prefix, tagName, false);
8669 }
8670 else if (contentTokenType === TagContentType.ESCAPABLE_RAW_TEXT) {
8671 this._consumeRawTextWithTagClose(prefix, tagName, true);
8672 }
8673 }
8674 _consumeRawTextWithTagClose(prefix, tagName, decodeEntities) {
8675 this._consumeRawText(decodeEntities, () => {
8676 if (!this._attemptCharCode($LT))
8677 return false;
8678 if (!this._attemptCharCode($SLASH))
8679 return false;
8680 this._attemptCharCodeUntilFn(isNotWhitespace);
8681 if (!this._attemptStrCaseInsensitive(tagName))
8682 return false;
8683 this._attemptCharCodeUntilFn(isNotWhitespace);
8684 return this._attemptCharCode($GT);
8685 });
8686 this._beginToken(TokenType.TAG_CLOSE);
8687 this._requireCharCodeUntilFn(code => code === $GT, 3);
8688 this._cursor.advance(); // Consume the `>`
8689 this._endToken([prefix, tagName]);
8690 }
8691 _consumeTagOpenStart(start) {
8692 this._beginToken(TokenType.TAG_OPEN_START, start);
8693 const parts = this._consumePrefixAndName();
8694 return this._endToken(parts);
8695 }
8696 _consumeAttributeName() {
8697 const attrNameStart = this._cursor.peek();
8698 if (attrNameStart === $SQ || attrNameStart === $DQ) {
8699 throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());
8700 }
8701 this._beginToken(TokenType.ATTR_NAME);
8702 const prefixAndName = this._consumePrefixAndName();
8703 this._endToken(prefixAndName);
8704 }
8705 _consumeAttributeValue() {
8706 let value;
8707 if (this._cursor.peek() === $SQ || this._cursor.peek() === $DQ) {
8708 this._beginToken(TokenType.ATTR_QUOTE);
8709 const quoteChar = this._cursor.peek();
8710 this._cursor.advance();
8711 this._endToken([String.fromCodePoint(quoteChar)]);
8712 this._beginToken(TokenType.ATTR_VALUE);
8713 const parts = [];
8714 while (this._cursor.peek() !== quoteChar) {
8715 parts.push(this._readChar(true));
8716 }
8717 value = parts.join('');
8718 this._endToken([this._processCarriageReturns(value)]);
8719 this._beginToken(TokenType.ATTR_QUOTE);
8720 this._cursor.advance();
8721 this._endToken([String.fromCodePoint(quoteChar)]);
8722 }
8723 else {
8724 this._beginToken(TokenType.ATTR_VALUE);
8725 const valueStart = this._cursor.clone();
8726 this._requireCharCodeUntilFn(isNameEnd, 1);
8727 value = this._cursor.getChars(valueStart);
8728 this._endToken([this._processCarriageReturns(value)]);
8729 }
8730 }
8731 _consumeTagOpenEnd() {
8732 const tokenType = this._attemptCharCode($SLASH) ? TokenType.TAG_OPEN_END_VOID : TokenType.TAG_OPEN_END;
8733 this._beginToken(tokenType);
8734 this._requireCharCode($GT);
8735 this._endToken([]);
8736 }
8737 _consumeTagClose(start) {
8738 this._beginToken(TokenType.TAG_CLOSE, start);
8739 this._attemptCharCodeUntilFn(isNotWhitespace);
8740 const prefixAndName = this._consumePrefixAndName();
8741 this._attemptCharCodeUntilFn(isNotWhitespace);
8742 this._requireCharCode($GT);
8743 this._endToken(prefixAndName);
8744 }
8745 _consumeExpansionFormStart() {
8746 this._beginToken(TokenType.EXPANSION_FORM_START);
8747 this._requireCharCode($LBRACE);
8748 this._endToken([]);
8749 this._expansionCaseStack.push(TokenType.EXPANSION_FORM_START);
8750 this._beginToken(TokenType.RAW_TEXT);
8751 const condition = this._readUntil($COMMA);
8752 const normalizedCondition = this._processCarriageReturns(condition);
8753 if (this._i18nNormalizeLineEndingsInICUs) {
8754 // We explicitly want to normalize line endings for this text.
8755 this._endToken([normalizedCondition]);
8756 }
8757 else {
8758 // We are not normalizing line endings.
8759 const conditionToken = this._endToken([condition]);
8760 if (normalizedCondition !== condition) {
8761 this.nonNormalizedIcuExpressions.push(conditionToken);
8762 }
8763 }
8764 this._requireCharCode($COMMA);
8765 this._attemptCharCodeUntilFn(isNotWhitespace);
8766 this._beginToken(TokenType.RAW_TEXT);
8767 const type = this._readUntil($COMMA);
8768 this._endToken([type]);
8769 this._requireCharCode($COMMA);
8770 this._attemptCharCodeUntilFn(isNotWhitespace);
8771 }
8772 _consumeExpansionCaseStart() {
8773 this._beginToken(TokenType.EXPANSION_CASE_VALUE);
8774 const value = this._readUntil($LBRACE).trim();
8775 this._endToken([value]);
8776 this._attemptCharCodeUntilFn(isNotWhitespace);
8777 this._beginToken(TokenType.EXPANSION_CASE_EXP_START);
8778 this._requireCharCode($LBRACE);
8779 this._endToken([]);
8780 this._attemptCharCodeUntilFn(isNotWhitespace);
8781 this._expansionCaseStack.push(TokenType.EXPANSION_CASE_EXP_START);
8782 }
8783 _consumeExpansionCaseEnd() {
8784 this._beginToken(TokenType.EXPANSION_CASE_EXP_END);
8785 this._requireCharCode($RBRACE);
8786 this._endToken([]);
8787 this._attemptCharCodeUntilFn(isNotWhitespace);
8788 this._expansionCaseStack.pop();
8789 }
8790 _consumeExpansionFormEnd() {
8791 this._beginToken(TokenType.EXPANSION_FORM_END);
8792 this._requireCharCode($RBRACE);
8793 this._endToken([]);
8794 this._expansionCaseStack.pop();
8795 }
8796 _consumeText() {
8797 const start = this._cursor.clone();
8798 this._beginToken(TokenType.TEXT, start);
8799 const parts = [];
8800 do {
8801 if (this._interpolationConfig && this._attemptStr(this._interpolationConfig.start)) {
8802 parts.push(this._interpolationConfig.start);
8803 this._inInterpolation = true;
8804 }
8805 else if (this._interpolationConfig && this._inInterpolation &&
8806 this._attemptStr(this._interpolationConfig.end)) {
8807 parts.push(this._interpolationConfig.end);
8808 this._inInterpolation = false;
8809 }
8810 else {
8811 parts.push(this._readChar(true));
8812 }
8813 } while (!this._isTextEnd());
8814 this._endToken([this._processCarriageReturns(parts.join(''))]);
8815 }
8816 _isTextEnd() {
8817 if (this._cursor.peek() === $LT || this._cursor.peek() === $EOF) {
8818 return true;
8819 }
8820 if (this._tokenizeIcu && !this._inInterpolation) {
8821 if (this.isExpansionFormStart()) {
8822 // start of an expansion form
8823 return true;
8824 }
8825 if (this._cursor.peek() === $RBRACE && this._isInExpansionCase()) {
8826 // end of and expansion case
8827 return true;
8828 }
8829 }
8830 return false;
8831 }
8832 _readUntil(char) {
8833 const start = this._cursor.clone();
8834 this._attemptUntilChar(char);
8835 return this._cursor.getChars(start);
8836 }
8837 _isInExpansionCase() {
8838 return this._expansionCaseStack.length > 0 &&
8839 this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
8840 TokenType.EXPANSION_CASE_EXP_START;
8841 }
8842 _isInExpansionForm() {
8843 return this._expansionCaseStack.length > 0 &&
8844 this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
8845 TokenType.EXPANSION_FORM_START;
8846 }
8847 isExpansionFormStart() {
8848 if (this._cursor.peek() !== $LBRACE) {
8849 return false;
8850 }
8851 if (this._interpolationConfig) {
8852 const start = this._cursor.clone();
8853 const isInterpolation = this._attemptStr(this._interpolationConfig.start);
8854 this._cursor = start;
8855 return !isInterpolation;
8856 }
8857 return true;
8858 }
8859 }
8860 function isNotWhitespace(code) {
8861 return !isWhitespace(code) || code === $EOF;
8862 }
8863 function isNameEnd(code) {
8864 return isWhitespace(code) || code === $GT || code === $LT ||
8865 code === $SLASH || code === $SQ || code === $DQ || code === $EQ;
8866 }
8867 function isPrefixEnd(code) {
8868 return (code < $a || $z < code) && (code < $A || $Z < code) &&
8869 (code < $0 || code > $9);
8870 }
8871 function isDigitEntityEnd(code) {
8872 return code == $SEMICOLON || code == $EOF || !isAsciiHexDigit(code);
8873 }
8874 function isNamedEntityEnd(code) {
8875 return code == $SEMICOLON || code == $EOF || !isAsciiLetter(code);
8876 }
8877 function isExpansionCaseStart(peek) {
8878 return peek !== $RBRACE;
8879 }
8880 function compareCharCodeCaseInsensitive(code1, code2) {
8881 return toUpperCaseCharCode(code1) == toUpperCaseCharCode(code2);
8882 }
8883 function toUpperCaseCharCode(code) {
8884 return code >= $a && code <= $z ? code - $a + $A : code;
8885 }
8886 function mergeTextTokens(srcTokens) {
8887 const dstTokens = [];
8888 let lastDstToken = undefined;
8889 for (let i = 0; i < srcTokens.length; i++) {
8890 const token = srcTokens[i];
8891 if (lastDstToken && lastDstToken.type == TokenType.TEXT && token.type == TokenType.TEXT) {
8892 lastDstToken.parts[0] += token.parts[0];
8893 lastDstToken.sourceSpan.end = token.sourceSpan.end;
8894 }
8895 else {
8896 lastDstToken = token;
8897 dstTokens.push(lastDstToken);
8898 }
8899 }
8900 return dstTokens;
8901 }
8902 class PlainCharacterCursor {
8903 constructor(fileOrCursor, range) {
8904 if (fileOrCursor instanceof PlainCharacterCursor) {
8905 this.file = fileOrCursor.file;
8906 this.input = fileOrCursor.input;
8907 this.end = fileOrCursor.end;
8908 const state = fileOrCursor.state;
8909 // Note: avoid using `{...fileOrCursor.state}` here as that has a severe performance penalty.
8910 // In ES5 bundles the object spread operator is translated into the `__assign` helper, which
8911 // is not optimized by VMs as efficiently as a raw object literal. Since this constructor is
8912 // called in tight loops, this difference matters.
8913 this.state = {
8914 peek: state.peek,
8915 offset: state.offset,
8916 line: state.line,
8917 column: state.column,
8918 };
8919 }
8920 else {
8921 if (!range) {
8922 throw new Error('Programming error: the range argument must be provided with a file argument.');
8923 }
8924 this.file = fileOrCursor;
8925 this.input = fileOrCursor.content;
8926 this.end = range.endPos;
8927 this.state = {
8928 peek: -1,
8929 offset: range.startPos,
8930 line: range.startLine,
8931 column: range.startCol,
8932 };
8933 }
8934 }
8935 clone() {
8936 return new PlainCharacterCursor(this);
8937 }
8938 peek() {
8939 return this.state.peek;
8940 }
8941 charsLeft() {
8942 return this.end - this.state.offset;
8943 }
8944 diff(other) {
8945 return this.state.offset - other.state.offset;
8946 }
8947 advance() {
8948 this.advanceState(this.state);
8949 }
8950 init() {
8951 this.updatePeek(this.state);
8952 }
8953 getSpan(start, leadingTriviaCodePoints) {
8954 start = start || this;
8955 let fullStart = start;
8956 if (leadingTriviaCodePoints) {
8957 while (this.diff(start) > 0 && leadingTriviaCodePoints.indexOf(start.peek()) !== -1) {
8958 if (fullStart === start) {
8959 start = start.clone();
8960 }
8961 start.advance();
8962 }
8963 }
8964 const startLocation = this.locationFromCursor(start);
8965 const endLocation = this.locationFromCursor(this);
8966 const fullStartLocation = fullStart !== start ? this.locationFromCursor(fullStart) : startLocation;
8967 return new ParseSourceSpan(startLocation, endLocation, fullStartLocation);
8968 }
8969 getChars(start) {
8970 return this.input.substring(start.state.offset, this.state.offset);
8971 }
8972 charAt(pos) {
8973 return this.input.charCodeAt(pos);
8974 }
8975 advanceState(state) {
8976 if (state.offset >= this.end) {
8977 this.state = state;
8978 throw new CursorError('Unexpected character "EOF"', this);
8979 }
8980 const currentChar = this.charAt(state.offset);
8981 if (currentChar === $LF) {
8982 state.line++;
8983 state.column = 0;
8984 }
8985 else if (!isNewLine(currentChar)) {
8986 state.column++;
8987 }
8988 state.offset++;
8989 this.updatePeek(state);
8990 }
8991 updatePeek(state) {
8992 state.peek = state.offset >= this.end ? $EOF : this.charAt(state.offset);
8993 }
8994 locationFromCursor(cursor) {
8995 return new ParseLocation(cursor.file, cursor.state.offset, cursor.state.line, cursor.state.column);
8996 }
8997 }
8998 class EscapedCharacterCursor extends PlainCharacterCursor {
8999 constructor(fileOrCursor, range) {
9000 if (fileOrCursor instanceof EscapedCharacterCursor) {
9001 super(fileOrCursor);
9002 this.internalState = Object.assign({}, fileOrCursor.internalState);
9003 }
9004 else {
9005 super(fileOrCursor, range);
9006 this.internalState = this.state;
9007 }
9008 }
9009 advance() {
9010 this.state = this.internalState;
9011 super.advance();
9012 this.processEscapeSequence();
9013 }
9014 init() {
9015 super.init();
9016 this.processEscapeSequence();
9017 }
9018 clone() {
9019 return new EscapedCharacterCursor(this);
9020 }
9021 getChars(start) {
9022 const cursor = start.clone();
9023 let chars = '';
9024 while (cursor.internalState.offset < this.internalState.offset) {
9025 chars += String.fromCodePoint(cursor.peek());
9026 cursor.advance();
9027 }
9028 return chars;
9029 }
9030 /**
9031 * Process the escape sequence that starts at the current position in the text.
9032 *
9033 * This method is called to ensure that `peek` has the unescaped value of escape sequences.
9034 */
9035 processEscapeSequence() {
9036 const peek = () => this.internalState.peek;
9037 if (peek() === $BACKSLASH) {
9038 // We have hit an escape sequence so we need the internal state to become independent
9039 // of the external state.
9040 this.internalState = Object.assign({}, this.state);
9041 // Move past the backslash
9042 this.advanceState(this.internalState);
9043 // First check for standard control char sequences
9044 if (peek() === $n) {
9045 this.state.peek = $LF;
9046 }
9047 else if (peek() === $r) {
9048 this.state.peek = $CR;
9049 }
9050 else if (peek() === $v) {
9051 this.state.peek = $VTAB;
9052 }
9053 else if (peek() === $t) {
9054 this.state.peek = $TAB;
9055 }
9056 else if (peek() === $b) {
9057 this.state.peek = $BSPACE;
9058 }
9059 else if (peek() === $f) {
9060 this.state.peek = $FF;
9061 }
9062 // Now consider more complex sequences
9063 else if (peek() === $u) {
9064 // Unicode code-point sequence
9065 this.advanceState(this.internalState); // advance past the `u` char
9066 if (peek() === $LBRACE) {
9067 // Variable length Unicode, e.g. `\x{123}`
9068 this.advanceState(this.internalState); // advance past the `{` char
9069 // Advance past the variable number of hex digits until we hit a `}` char
9070 const digitStart = this.clone();
9071 let length = 0;
9072 while (peek() !== $RBRACE) {
9073 this.advanceState(this.internalState);
9074 length++;
9075 }
9076 this.state.peek = this.decodeHexDigits(digitStart, length);
9077 }
9078 else {
9079 // Fixed length Unicode, e.g. `\u1234`
9080 const digitStart = this.clone();
9081 this.advanceState(this.internalState);
9082 this.advanceState(this.internalState);
9083 this.advanceState(this.internalState);
9084 this.state.peek = this.decodeHexDigits(digitStart, 4);
9085 }
9086 }
9087 else if (peek() === $x) {
9088 // Hex char code, e.g. `\x2F`
9089 this.advanceState(this.internalState); // advance past the `x` char
9090 const digitStart = this.clone();
9091 this.advanceState(this.internalState);
9092 this.state.peek = this.decodeHexDigits(digitStart, 2);
9093 }
9094 else if (isOctalDigit(peek())) {
9095 // Octal char code, e.g. `\012`,
9096 let octal = '';
9097 let length = 0;
9098 let previous = this.clone();
9099 while (isOctalDigit(peek()) && length < 3) {
9100 previous = this.clone();
9101 octal += String.fromCodePoint(peek());
9102 this.advanceState(this.internalState);
9103 length++;
9104 }
9105 this.state.peek = parseInt(octal, 8);
9106 // Backup one char
9107 this.internalState = previous.internalState;
9108 }
9109 else if (isNewLine(this.internalState.peek)) {
9110 // Line continuation `\` followed by a new line
9111 this.advanceState(this.internalState); // advance over the newline
9112 this.state = this.internalState;
9113 }
9114 else {
9115 // If none of the `if` blocks were executed then we just have an escaped normal character.
9116 // In that case we just, effectively, skip the backslash from the character.
9117 this.state.peek = this.internalState.peek;
9118 }
9119 }
9120 }
9121 decodeHexDigits(start, length) {
9122 const hex = this.input.substr(start.internalState.offset, length);
9123 const charCode = parseInt(hex, 16);
9124 if (!isNaN(charCode)) {
9125 return charCode;
9126 }
9127 else {
9128 start.state = start.internalState;
9129 throw new CursorError('Invalid hexadecimal escape sequence', start);
9130 }
9131 }
9132 }
9133 class CursorError {
9134 constructor(msg, cursor) {
9135 this.msg = msg;
9136 this.cursor = cursor;
9137 }
9138 }
9139
9140 /**
9141 * @license
9142 * Copyright Google LLC All Rights Reserved.
9143 *
9144 * Use of this source code is governed by an MIT-style license that can be
9145 * found in the LICENSE file at https://angular.io/license
9146 */
9147 class TreeError extends ParseError {
9148 constructor(elementName, span, msg) {
9149 super(span, msg);
9150 this.elementName = elementName;
9151 }
9152 static create(elementName, span, msg) {
9153 return new TreeError(elementName, span, msg);
9154 }
9155 }
9156 class ParseTreeResult {
9157 constructor(rootNodes, errors) {
9158 this.rootNodes = rootNodes;
9159 this.errors = errors;
9160 }
9161 }
9162 class Parser {
9163 constructor(getTagDefinition) {
9164 this.getTagDefinition = getTagDefinition;
9165 }
9166 parse(source, url, options) {
9167 const tokenizeResult = tokenize(source, url, this.getTagDefinition, options);
9168 const parser = new _TreeBuilder(tokenizeResult.tokens, this.getTagDefinition);
9169 parser.build();
9170 return new ParseTreeResult(parser.rootNodes, tokenizeResult.errors.concat(parser.errors));
9171 }
9172 }
9173 class _TreeBuilder {
9174 constructor(tokens, getTagDefinition) {
9175 this.tokens = tokens;
9176 this.getTagDefinition = getTagDefinition;
9177 this._index = -1;
9178 this._elementStack = [];
9179 this.rootNodes = [];
9180 this.errors = [];
9181 this._advance();
9182 }
9183 build() {
9184 while (this._peek.type !== TokenType.EOF) {
9185 if (this._peek.type === TokenType.TAG_OPEN_START ||
9186 this._peek.type === TokenType.INCOMPLETE_TAG_OPEN) {
9187 this._consumeStartTag(this._advance());
9188 }
9189 else if (this._peek.type === TokenType.TAG_CLOSE) {
9190 this._consumeEndTag(this._advance());
9191 }
9192 else if (this._peek.type === TokenType.CDATA_START) {
9193 this._closeVoidElement();
9194 this._consumeCdata(this._advance());
9195 }
9196 else if (this._peek.type === TokenType.COMMENT_START) {
9197 this._closeVoidElement();
9198 this._consumeComment(this._advance());
9199 }
9200 else if (this._peek.type === TokenType.TEXT || this._peek.type === TokenType.RAW_TEXT ||
9201 this._peek.type === TokenType.ESCAPABLE_RAW_TEXT) {
9202 this._closeVoidElement();
9203 this._consumeText(this._advance());
9204 }
9205 else if (this._peek.type === TokenType.EXPANSION_FORM_START) {
9206 this._consumeExpansion(this._advance());
9207 }
9208 else {
9209 // Skip all other tokens...
9210 this._advance();
9211 }
9212 }
9213 }
9214 _advance() {
9215 const prev = this._peek;
9216 if (this._index < this.tokens.length - 1) {
9217 // Note: there is always an EOF token at the end
9218 this._index++;
9219 }
9220 this._peek = this.tokens[this._index];
9221 return prev;
9222 }
9223 _advanceIf(type) {
9224 if (this._peek.type === type) {
9225 return this._advance();
9226 }
9227 return null;
9228 }
9229 _consumeCdata(_startToken) {
9230 this._consumeText(this._advance());
9231 this._advanceIf(TokenType.CDATA_END);
9232 }
9233 _consumeComment(token) {
9234 const text = this._advanceIf(TokenType.RAW_TEXT);
9235 this._advanceIf(TokenType.COMMENT_END);
9236 const value = text != null ? text.parts[0].trim() : null;
9237 this._addToParent(new Comment(value, token.sourceSpan));
9238 }
9239 _consumeExpansion(token) {
9240 const switchValue = this._advance();
9241 const type = this._advance();
9242 const cases = [];
9243 // read =
9244 while (this._peek.type === TokenType.EXPANSION_CASE_VALUE) {
9245 const expCase = this._parseExpansionCase();
9246 if (!expCase)
9247 return; // error
9248 cases.push(expCase);
9249 }
9250 // read the final }
9251 if (this._peek.type !== TokenType.EXPANSION_FORM_END) {
9252 this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '}'.`));
9253 return;
9254 }
9255 const sourceSpan = new ParseSourceSpan(token.sourceSpan.start, this._peek.sourceSpan.end, token.sourceSpan.fullStart);
9256 this._addToParent(new Expansion(switchValue.parts[0], type.parts[0], cases, sourceSpan, switchValue.sourceSpan));
9257 this._advance();
9258 }
9259 _parseExpansionCase() {
9260 const value = this._advance();
9261 // read {
9262 if (this._peek.type !== TokenType.EXPANSION_CASE_EXP_START) {
9263 this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '{'.`));
9264 return null;
9265 }
9266 // read until }
9267 const start = this._advance();
9268 const exp = this._collectExpansionExpTokens(start);
9269 if (!exp)
9270 return null;
9271 const end = this._advance();
9272 exp.push(new Token(TokenType.EOF, [], end.sourceSpan));
9273 // parse everything in between { and }
9274 const expansionCaseParser = new _TreeBuilder(exp, this.getTagDefinition);
9275 expansionCaseParser.build();
9276 if (expansionCaseParser.errors.length > 0) {
9277 this.errors = this.errors.concat(expansionCaseParser.errors);
9278 return null;
9279 }
9280 const sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end, value.sourceSpan.fullStart);
9281 const expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end, start.sourceSpan.fullStart);
9282 return new ExpansionCase(value.parts[0], expansionCaseParser.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan);
9283 }
9284 _collectExpansionExpTokens(start) {
9285 const exp = [];
9286 const expansionFormStack = [TokenType.EXPANSION_CASE_EXP_START];
9287 while (true) {
9288 if (this._peek.type === TokenType.EXPANSION_FORM_START ||
9289 this._peek.type === TokenType.EXPANSION_CASE_EXP_START) {
9290 expansionFormStack.push(this._peek.type);
9291 }
9292 if (this._peek.type === TokenType.EXPANSION_CASE_EXP_END) {
9293 if (lastOnStack(expansionFormStack, TokenType.EXPANSION_CASE_EXP_START)) {
9294 expansionFormStack.pop();
9295 if (expansionFormStack.length == 0)
9296 return exp;
9297 }
9298 else {
9299 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
9300 return null;
9301 }
9302 }
9303 if (this._peek.type === TokenType.EXPANSION_FORM_END) {
9304 if (lastOnStack(expansionFormStack, TokenType.EXPANSION_FORM_START)) {
9305 expansionFormStack.pop();
9306 }
9307 else {
9308 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
9309 return null;
9310 }
9311 }
9312 if (this._peek.type === TokenType.EOF) {
9313 this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
9314 return null;
9315 }
9316 exp.push(this._advance());
9317 }
9318 }
9319 _consumeText(token) {
9320 let text = token.parts[0];
9321 if (text.length > 0 && text[0] == '\n') {
9322 const parent = this._getParentElement();
9323 if (parent != null && parent.children.length == 0 &&
9324 this.getTagDefinition(parent.name).ignoreFirstLf) {
9325 text = text.substring(1);
9326 }
9327 }
9328 if (text.length > 0) {
9329 this._addToParent(new Text$2(text, token.sourceSpan));
9330 }
9331 }
9332 _closeVoidElement() {
9333 const el = this._getParentElement();
9334 if (el && this.getTagDefinition(el.name).isVoid) {
9335 this._elementStack.pop();
9336 }
9337 }
9338 _consumeStartTag(startTagToken) {
9339 const [prefix, name] = startTagToken.parts;
9340 const attrs = [];
9341 while (this._peek.type === TokenType.ATTR_NAME) {
9342 attrs.push(this._consumeAttr(this._advance()));
9343 }
9344 const fullName = this._getElementFullName(prefix, name, this._getParentElement());
9345 let selfClosing = false;
9346 // Note: There could have been a tokenizer error
9347 // so that we don't get a token for the end tag...
9348 if (this._peek.type === TokenType.TAG_OPEN_END_VOID) {
9349 this._advance();
9350 selfClosing = true;
9351 const tagDef = this.getTagDefinition(fullName);
9352 if (!(tagDef.canSelfClose || getNsPrefix(fullName) !== null || tagDef.isVoid)) {
9353 this.errors.push(TreeError.create(fullName, startTagToken.sourceSpan, `Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`));
9354 }
9355 }
9356 else if (this._peek.type === TokenType.TAG_OPEN_END) {
9357 this._advance();
9358 selfClosing = false;
9359 }
9360 const end = this._peek.sourceSpan.fullStart;
9361 const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
9362 // Create a separate `startSpan` because `span` will be modified when there is an `end` span.
9363 const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
9364 const el = new Element$1(fullName, attrs, [], span, startSpan, undefined);
9365 this._pushElement(el);
9366 if (selfClosing) {
9367 // Elements that are self-closed have their `endSourceSpan` set to the full span, as the
9368 // element start tag also represents the end tag.
9369 this._popElement(fullName, span);
9370 }
9371 else if (startTagToken.type === TokenType.INCOMPLETE_TAG_OPEN) {
9372 // We already know the opening tag is not complete, so it is unlikely it has a corresponding
9373 // close tag. Let's optimistically parse it as a full element and emit an error.
9374 this._popElement(fullName, null);
9375 this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
9376 }
9377 }
9378 _pushElement(el) {
9379 const parentEl = this._getParentElement();
9380 if (parentEl && this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) {
9381 this._elementStack.pop();
9382 }
9383 this._addToParent(el);
9384 this._elementStack.push(el);
9385 }
9386 _consumeEndTag(endTagToken) {
9387 const fullName = this._getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
9388 if (this.getTagDefinition(fullName).isVoid) {
9389 this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`));
9390 }
9391 else if (!this._popElement(fullName, endTagToken.sourceSpan)) {
9392 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`;
9393 this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg));
9394 }
9395 }
9396 /**
9397 * Closes the nearest element with the tag name `fullName` in the parse tree.
9398 * `endSourceSpan` is the span of the closing tag, or null if the element does
9399 * not have a closing tag (for example, this happens when an incomplete
9400 * opening tag is recovered).
9401 */
9402 _popElement(fullName, endSourceSpan) {
9403 for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) {
9404 const el = this._elementStack[stackIndex];
9405 if (el.name == fullName) {
9406 // Record the parse span with the element that is being closed. Any elements that are
9407 // removed from the element stack at this point are closed implicitly, so they won't get
9408 // an end source span (as there is no explicit closing element).
9409 el.endSourceSpan = endSourceSpan;
9410 el.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : el.sourceSpan.end;
9411 this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex);
9412 return true;
9413 }
9414 if (!this.getTagDefinition(el.name).closedByParent) {
9415 return false;
9416 }
9417 }
9418 return false;
9419 }
9420 _consumeAttr(attrName) {
9421 const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
9422 let end = attrName.sourceSpan.end;
9423 let value = '';
9424 let valueSpan = undefined;
9425 if (this._peek.type === TokenType.ATTR_QUOTE) {
9426 this._advance();
9427 }
9428 if (this._peek.type === TokenType.ATTR_VALUE) {
9429 const valueToken = this._advance();
9430 value = valueToken.parts[0];
9431 end = valueToken.sourceSpan.end;
9432 valueSpan = valueToken.sourceSpan;
9433 }
9434 if (this._peek.type === TokenType.ATTR_QUOTE) {
9435 const quoteToken = this._advance();
9436 end = quoteToken.sourceSpan.end;
9437 }
9438 const keySpan = new ParseSourceSpan(attrName.sourceSpan.start, attrName.sourceSpan.end);
9439 return new Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, end, attrName.sourceSpan.fullStart), keySpan, valueSpan);
9440 }
9441 _getParentElement() {
9442 return this._elementStack.length > 0 ? this._elementStack[this._elementStack.length - 1] : null;
9443 }
9444 _addToParent(node) {
9445 const parent = this._getParentElement();
9446 if (parent != null) {
9447 parent.children.push(node);
9448 }
9449 else {
9450 this.rootNodes.push(node);
9451 }
9452 }
9453 _getElementFullName(prefix, localName, parentElement) {
9454 if (prefix === '') {
9455 prefix = this.getTagDefinition(localName).implicitNamespacePrefix || '';
9456 if (prefix === '' && parentElement != null) {
9457 const parentTagName = splitNsName(parentElement.name)[1];
9458 const parentTagDefinition = this.getTagDefinition(parentTagName);
9459 if (!parentTagDefinition.preventNamespaceInheritance) {
9460 prefix = getNsPrefix(parentElement.name);
9461 }
9462 }
9463 }
9464 return mergeNsAndName(prefix, localName);
9465 }
9466 }
9467 function lastOnStack(stack, element) {
9468 return stack.length > 0 && stack[stack.length - 1] === element;
9469 }
9470
9471 /**
9472 * @license
9473 * Copyright Google LLC All Rights Reserved.
9474 *
9475 * Use of this source code is governed by an MIT-style license that can be
9476 * found in the LICENSE file at https://angular.io/license
9477 */
9478 class HtmlParser extends Parser {
9479 constructor() {
9480 super(getHtmlTagDefinition);
9481 }
9482 parse(source, url, options) {
9483 return super.parse(source, url, options);
9484 }
9485 }
9486
9487 /**
9488 * @license
9489 * Copyright Google LLC All Rights Reserved.
9490 *
9491 * Use of this source code is governed by an MIT-style license that can be
9492 * found in the LICENSE file at https://angular.io/license
9493 */
9494 const PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces';
9495 const SKIP_WS_TRIM_TAGS = new Set(['pre', 'template', 'textarea', 'script', 'style']);
9496 // Equivalent to \s with \u00a0 (non-breaking space) excluded.
9497 // Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
9498 const WS_CHARS = ' \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff';
9499 const NO_WS_REGEXP = new RegExp(`[^${WS_CHARS}]`);
9500 const WS_REPLACE_REGEXP = new RegExp(`[${WS_CHARS}]{2,}`, 'g');
9501 function hasPreserveWhitespacesAttr(attrs) {
9502 return attrs.some((attr) => attr.name === PRESERVE_WS_ATTR_NAME);
9503 }
9504 /**
9505 * Angular Dart introduced &ngsp; as a placeholder for non-removable space, see:
9506 * https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32
9507 * In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character
9508 * and later on replaced by a space. We are re-implementing the same idea here.
9509 */
9510 function replaceNgsp(value) {
9511 // lexer is replacing the &ngsp; pseudo-entity with NGSP_UNICODE
9512 return value.replace(new RegExp(NGSP_UNICODE, 'g'), ' ');
9513 }
9514 /**
9515 * This visitor can walk HTML parse tree and remove / trim text nodes using the following rules:
9516 * - consider spaces, tabs and new lines as whitespace characters;
9517 * - drop text nodes consisting of whitespace characters only;
9518 * - for all other text nodes replace consecutive whitespace characters with one space;
9519 * - convert &ngsp; pseudo-entity to a single space;
9520 *
9521 * Removal and trimming of whitespaces have positive performance impact (less code to generate
9522 * while compiling templates, faster view creation). At the same time it can be "destructive"
9523 * in some cases (whitespaces can influence layout). Because of the potential of breaking layout
9524 * this visitor is not activated by default in Angular 5 and people need to explicitly opt-in for
9525 * whitespace removal. The default option for whitespace removal will be revisited in Angular 6
9526 * and might be changed to "on" by default.
9527 */
9528 class WhitespaceVisitor {
9529 visitElement(element, context) {
9530 if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
9531 // don't descent into elements where we need to preserve whitespaces
9532 // but still visit all attributes to eliminate one used as a market to preserve WS
9533 return new Element$1(element.name, visitAll$1(this, element.attrs), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
9534 }
9535 return new Element$1(element.name, element.attrs, visitAllWithSiblings(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
9536 }
9537 visitAttribute(attribute, context) {
9538 return attribute.name !== PRESERVE_WS_ATTR_NAME ? attribute : null;
9539 }
9540 visitText(text, context) {
9541 const isNotBlank = text.value.match(NO_WS_REGEXP);
9542 const hasExpansionSibling = context &&
9543 (context.prev instanceof Expansion || context.next instanceof Expansion);
9544 if (isNotBlank || hasExpansionSibling) {
9545 return new Text$2(replaceNgsp(text.value).replace(WS_REPLACE_REGEXP, ' '), text.sourceSpan, text.i18n);
9546 }
9547 return null;
9548 }
9549 visitComment(comment, context) {
9550 return comment;
9551 }
9552 visitExpansion(expansion, context) {
9553 return expansion;
9554 }
9555 visitExpansionCase(expansionCase, context) {
9556 return expansionCase;
9557 }
9558 }
9559 function visitAllWithSiblings(visitor, nodes) {
9560 const result = [];
9561 nodes.forEach((ast, i) => {
9562 const context = { prev: nodes[i - 1], next: nodes[i + 1] };
9563 const astResult = ast.visit(visitor, context);
9564 if (astResult) {
9565 result.push(astResult);
9566 }
9567 });
9568 return result;
9569 }
9570
9571 /**
9572 * @license
9573 * Copyright Google LLC All Rights Reserved.
9574 *
9575 * Use of this source code is governed by an MIT-style license that can be
9576 * found in the LICENSE file at https://angular.io/license
9577 */
9578 var ProviderAstType;
9579 (function (ProviderAstType) {
9580 ProviderAstType[ProviderAstType["PublicService"] = 0] = "PublicService";
9581 ProviderAstType[ProviderAstType["PrivateService"] = 1] = "PrivateService";
9582 ProviderAstType[ProviderAstType["Component"] = 2] = "Component";
9583 ProviderAstType[ProviderAstType["Directive"] = 3] = "Directive";
9584 ProviderAstType[ProviderAstType["Builtin"] = 4] = "Builtin";
9585 })(ProviderAstType || (ProviderAstType = {}));
9586
9587 /**
9588 * @license
9589 * Copyright Google LLC All Rights Reserved.
9590 *
9591 * Use of this source code is governed by an MIT-style license that can be
9592 * found in the LICENSE file at https://angular.io/license
9593 */
9594 function isStyleUrlResolvable(url) {
9595 if (url == null || url.length === 0 || url[0] == '/')
9596 return false;
9597 const schemeMatch = url.match(URL_WITH_SCHEMA_REGEXP);
9598 return schemeMatch === null || schemeMatch[1] == 'package' || schemeMatch[1] == 'asset';
9599 }
9600 const URL_WITH_SCHEMA_REGEXP = /^([^:/?#]+):/;
9601
9602 /**
9603 * @license
9604 * Copyright Google LLC All Rights Reserved.
9605 *
9606 * Use of this source code is governed by an MIT-style license that can be
9607 * found in the LICENSE file at https://angular.io/license
9608 */
9609 const PROPERTY_PARTS_SEPARATOR = '.';
9610 const ATTRIBUTE_PREFIX = 'attr';
9611 const CLASS_PREFIX = 'class';
9612 const STYLE_PREFIX = 'style';
9613 const TEMPLATE_ATTR_PREFIX = '*';
9614 const ANIMATE_PROP_PREFIX = 'animate-';
9615 /**
9616 * Parses bindings in templates and in the directive host area.
9617 */
9618 class BindingParser {
9619 constructor(_exprParser, _interpolationConfig, _schemaRegistry, pipes, errors) {
9620 this._exprParser = _exprParser;
9621 this._interpolationConfig = _interpolationConfig;
9622 this._schemaRegistry = _schemaRegistry;
9623 this.errors = errors;
9624 this.pipesByName = null;
9625 this._usedPipes = new Map();
9626 // When the `pipes` parameter is `null`, do not check for used pipes
9627 // This is used in IVY when we might not know the available pipes at compile time
9628 if (pipes) {
9629 const pipesByName = new Map();
9630 pipes.forEach(pipe => pipesByName.set(pipe.name, pipe));
9631 this.pipesByName = pipesByName;
9632 }
9633 }
9634 get interpolationConfig() {
9635 return this._interpolationConfig;
9636 }
9637 getUsedPipes() {
9638 return Array.from(this._usedPipes.values());
9639 }
9640 createBoundHostProperties(dirMeta, sourceSpan) {
9641 if (dirMeta.hostProperties) {
9642 const boundProps = [];
9643 Object.keys(dirMeta.hostProperties).forEach(propName => {
9644 const expression = dirMeta.hostProperties[propName];
9645 if (typeof expression === 'string') {
9646 this.parsePropertyBinding(propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [],
9647 // Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the
9648 // sourceSpan, as it represents the sourceSpan of the host itself rather than the
9649 // source of the host binding (which doesn't exist in the template). Regardless,
9650 // neither of these values are used in Ivy but are only here to satisfy the function
9651 // signature. This should likely be refactored in the future so that `sourceSpan`
9652 // isn't being used inaccurately.
9653 boundProps, sourceSpan);
9654 }
9655 else {
9656 this._reportError(`Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
9657 }
9658 });
9659 return boundProps;
9660 }
9661 return null;
9662 }
9663 createDirectiveHostPropertyAsts(dirMeta, elementSelector, sourceSpan) {
9664 const boundProps = this.createBoundHostProperties(dirMeta, sourceSpan);
9665 return boundProps &&
9666 boundProps.map((prop) => this.createBoundElementProperty(elementSelector, prop));
9667 }
9668 createDirectiveHostEventAsts(dirMeta, sourceSpan) {
9669 if (dirMeta.hostListeners) {
9670 const targetEvents = [];
9671 Object.keys(dirMeta.hostListeners).forEach(propName => {
9672 const expression = dirMeta.hostListeners[propName];
9673 if (typeof expression === 'string') {
9674 // Use the `sourceSpan` for `keySpan` and `handlerSpan`. This isn't really accurate, but
9675 // neither is the `sourceSpan`, as it represents the `sourceSpan` of the host itself
9676 // rather than the source of the host binding (which doesn't exist in the template).
9677 // Regardless, neither of these values are used in Ivy but are only here to satisfy the
9678 // function signature. This should likely be refactored in the future so that `sourceSpan`
9679 // isn't being used inaccurately.
9680 this.parseEvent(propName, expression, sourceSpan, sourceSpan, [], targetEvents, sourceSpan);
9681 }
9682 else {
9683 this._reportError(`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
9684 }
9685 });
9686 return targetEvents;
9687 }
9688 return null;
9689 }
9690 parseInterpolation(value, sourceSpan) {
9691 const sourceInfo = sourceSpan.start.toString();
9692 const absoluteOffset = sourceSpan.fullStart.offset;
9693 try {
9694 const ast = this._exprParser.parseInterpolation(value, sourceInfo, absoluteOffset, this._interpolationConfig);
9695 if (ast)
9696 this._reportExpressionParserErrors(ast.errors, sourceSpan);
9697 this._checkPipes(ast, sourceSpan);
9698 return ast;
9699 }
9700 catch (e) {
9701 this._reportError(`${e}`, sourceSpan);
9702 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
9703 }
9704 }
9705 /**
9706 * Similar to `parseInterpolation`, but treats the provided string as a single expression
9707 * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).
9708 * This is used for parsing the switch expression in ICUs.
9709 */
9710 parseInterpolationExpression(expression, sourceSpan) {
9711 const sourceInfo = sourceSpan.start.toString();
9712 const absoluteOffset = sourceSpan.start.offset;
9713 try {
9714 const ast = this._exprParser.parseInterpolationExpression(expression, sourceInfo, absoluteOffset);
9715 if (ast)
9716 this._reportExpressionParserErrors(ast.errors, sourceSpan);
9717 this._checkPipes(ast, sourceSpan);
9718 return ast;
9719 }
9720 catch (e) {
9721 this._reportError(`${e}`, sourceSpan);
9722 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
9723 }
9724 }
9725 /**
9726 * Parses the bindings in a microsyntax expression, and converts them to
9727 * `ParsedProperty` or `ParsedVariable`.
9728 *
9729 * @param tplKey template binding name
9730 * @param tplValue template binding value
9731 * @param sourceSpan span of template binding relative to entire the template
9732 * @param absoluteValueOffset start of the tplValue relative to the entire template
9733 * @param targetMatchableAttrs potential attributes to match in the template
9734 * @param targetProps target property bindings in the template
9735 * @param targetVars target variables in the template
9736 */
9737 parseInlineTemplateBinding(tplKey, tplValue, sourceSpan, absoluteValueOffset, targetMatchableAttrs, targetProps, targetVars, isIvyAst) {
9738 const absoluteKeyOffset = sourceSpan.start.offset + TEMPLATE_ATTR_PREFIX.length;
9739 const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset);
9740 for (const binding of bindings) {
9741 // sourceSpan is for the entire HTML attribute. bindingSpan is for a particular
9742 // binding within the microsyntax expression so it's more narrow than sourceSpan.
9743 const bindingSpan = moveParseSourceSpan(sourceSpan, binding.sourceSpan);
9744 const key = binding.key.source;
9745 const keySpan = moveParseSourceSpan(sourceSpan, binding.key.span);
9746 if (binding instanceof VariableBinding) {
9747 const value = binding.value ? binding.value.source : '$implicit';
9748 const valueSpan = binding.value ? moveParseSourceSpan(sourceSpan, binding.value.span) : undefined;
9749 targetVars.push(new ParsedVariable(key, value, bindingSpan, keySpan, valueSpan));
9750 }
9751 else if (binding.value) {
9752 const srcSpan = isIvyAst ? bindingSpan : sourceSpan;
9753 const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);
9754 this._parsePropertyAst(key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
9755 }
9756 else {
9757 targetMatchableAttrs.push([key, '' /* value */]);
9758 // Since this is a literal attribute with no RHS, source span should be
9759 // just the key span.
9760 this.parseLiteralAttr(key, null /* value */, keySpan, absoluteValueOffset, undefined /* valueSpan */, targetMatchableAttrs, targetProps, keySpan);
9761 }
9762 }
9763 }
9764 /**
9765 * Parses the bindings in a microsyntax expression, e.g.
9766 * ```
9767 * <tag *tplKey="let value1 = prop; let value2 = localVar">
9768 * ```
9769 *
9770 * @param tplKey template binding name
9771 * @param tplValue template binding value
9772 * @param sourceSpan span of template binding relative to entire the template
9773 * @param absoluteKeyOffset start of the `tplKey`
9774 * @param absoluteValueOffset start of the `tplValue`
9775 */
9776 _parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset) {
9777 const sourceInfo = sourceSpan.start.toString();
9778 try {
9779 const bindingsResult = this._exprParser.parseTemplateBindings(tplKey, tplValue, sourceInfo, absoluteKeyOffset, absoluteValueOffset);
9780 this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
9781 bindingsResult.templateBindings.forEach((binding) => {
9782 if (binding.value instanceof ASTWithSource) {
9783 this._checkPipes(binding.value, sourceSpan);
9784 }
9785 });
9786 bindingsResult.warnings.forEach((warning) => {
9787 this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING);
9788 });
9789 return bindingsResult.templateBindings;
9790 }
9791 catch (e) {
9792 this._reportError(`${e}`, sourceSpan);
9793 return [];
9794 }
9795 }
9796 parseLiteralAttr(name, value, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs,
9797 // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
9798 // have to change This should be required when VE is removed.
9799 targetProps, keySpan) {
9800 if (isAnimationLabel(name)) {
9801 name = name.substring(1);
9802 if (keySpan !== undefined) {
9803 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
9804 }
9805 if (value) {
9806 this._reportError(`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
9807 ` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`, sourceSpan, ParseErrorLevel.ERROR);
9808 }
9809 this._parseAnimation(name, value, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
9810 }
9811 else {
9812 targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));
9813 }
9814 }
9815 parsePropertyBinding(name, expression, isHost, sourceSpan, absoluteOffset, valueSpan,
9816 // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
9817 // have to change This should be required when VE is removed.
9818 targetMatchableAttrs, targetProps, keySpan) {
9819 if (name.length === 0) {
9820 this._reportError(`Property name is missing in binding`, sourceSpan);
9821 }
9822 let isAnimationProp = false;
9823 if (name.startsWith(ANIMATE_PROP_PREFIX)) {
9824 isAnimationProp = true;
9825 name = name.substring(ANIMATE_PROP_PREFIX.length);
9826 if (keySpan !== undefined) {
9827 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + ANIMATE_PROP_PREFIX.length, keySpan.end.offset));
9828 }
9829 }
9830 else if (isAnimationLabel(name)) {
9831 isAnimationProp = true;
9832 name = name.substring(1);
9833 if (keySpan !== undefined) {
9834 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
9835 }
9836 }
9837 if (isAnimationProp) {
9838 this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
9839 }
9840 else {
9841 this._parsePropertyAst(name, this._parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
9842 }
9843 }
9844 parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs,
9845 // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
9846 // have to change This should be required when VE is removed.
9847 targetProps, keySpan) {
9848 const expr = this.parseInterpolation(value, valueSpan || sourceSpan);
9849 if (expr) {
9850 this._parsePropertyAst(name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
9851 return true;
9852 }
9853 return false;
9854 }
9855 _parsePropertyAst(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
9856 targetMatchableAttrs.push([name, ast.source]);
9857 targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
9858 }
9859 _parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
9860 if (name.length === 0) {
9861 this._reportError('Animation trigger is missing', sourceSpan);
9862 }
9863 // This will occur when a @trigger is not paired with an expression.
9864 // For animations it is valid to not have an expression since */void
9865 // states will be applied by angular when the element is attached/detached
9866 const ast = this._parseBinding(expression || 'undefined', false, valueSpan || sourceSpan, absoluteOffset);
9867 targetMatchableAttrs.push([name, ast.source]);
9868 targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan, keySpan, valueSpan));
9869 }
9870 _parseBinding(value, isHostBinding, sourceSpan, absoluteOffset) {
9871 const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown)').toString();
9872 try {
9873 const ast = isHostBinding ?
9874 this._exprParser.parseSimpleBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig) :
9875 this._exprParser.parseBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig);
9876 if (ast)
9877 this._reportExpressionParserErrors(ast.errors, sourceSpan);
9878 this._checkPipes(ast, sourceSpan);
9879 return ast;
9880 }
9881 catch (e) {
9882 this._reportError(`${e}`, sourceSpan);
9883 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
9884 }
9885 }
9886 createBoundElementProperty(elementSelector, boundProp, skipValidation = false, mapPropertyName = true) {
9887 if (boundProp.isAnimation) {
9888 return new BoundElementProperty(boundProp.name, 4 /* Animation */, SecurityContext.NONE, boundProp.expression, null, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
9889 }
9890 let unit = null;
9891 let bindingType = undefined;
9892 let boundPropertyName = null;
9893 const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR);
9894 let securityContexts = undefined;
9895 // Check for special cases (prefix style, attr, class)
9896 if (parts.length > 1) {
9897 if (parts[0] == ATTRIBUTE_PREFIX) {
9898 boundPropertyName = parts.slice(1).join(PROPERTY_PARTS_SEPARATOR);
9899 if (!skipValidation) {
9900 this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, true);
9901 }
9902 securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, boundPropertyName, true);
9903 const nsSeparatorIdx = boundPropertyName.indexOf(':');
9904 if (nsSeparatorIdx > -1) {
9905 const ns = boundPropertyName.substring(0, nsSeparatorIdx);
9906 const name = boundPropertyName.substring(nsSeparatorIdx + 1);
9907 boundPropertyName = mergeNsAndName(ns, name);
9908 }
9909 bindingType = 1 /* Attribute */;
9910 }
9911 else if (parts[0] == CLASS_PREFIX) {
9912 boundPropertyName = parts[1];
9913 bindingType = 2 /* Class */;
9914 securityContexts = [SecurityContext.NONE];
9915 }
9916 else if (parts[0] == STYLE_PREFIX) {
9917 unit = parts.length > 2 ? parts[2] : null;
9918 boundPropertyName = parts[1];
9919 bindingType = 3 /* Style */;
9920 securityContexts = [SecurityContext.STYLE];
9921 }
9922 }
9923 // If not a special case, use the full property name
9924 if (boundPropertyName === null) {
9925 const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);
9926 boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;
9927 securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);
9928 bindingType = 0 /* Property */;
9929 if (!skipValidation) {
9930 this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);
9931 }
9932 }
9933 return new BoundElementProperty(boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
9934 }
9935 // TODO: keySpan should be required but was made optional to avoid changing VE parser.
9936 parseEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
9937 if (name.length === 0) {
9938 this._reportError(`Event name is missing in binding`, sourceSpan);
9939 }
9940 if (isAnimationLabel(name)) {
9941 name = name.substr(1);
9942 if (keySpan !== undefined) {
9943 keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
9944 }
9945 this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
9946 }
9947 else {
9948 this._parseRegularEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);
9949 }
9950 }
9951 calcPossibleSecurityContexts(selector, propName, isAttribute) {
9952 const prop = this._schemaRegistry.getMappedPropName(propName);
9953 return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);
9954 }
9955 _parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) {
9956 const matches = splitAtPeriod(name, [name, '']);
9957 const eventName = matches[0];
9958 const phase = matches[1].toLowerCase();
9959 const ast = this._parseAction(expression, handlerSpan);
9960 targetEvents.push(new ParsedEvent(eventName, phase, 1 /* Animation */, ast, sourceSpan, handlerSpan, keySpan));
9961 if (eventName.length === 0) {
9962 this._reportError(`Animation event name is missing in binding`, sourceSpan);
9963 }
9964 if (phase) {
9965 if (phase !== 'start' && phase !== 'done') {
9966 this._reportError(`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, sourceSpan);
9967 }
9968 }
9969 else {
9970 this._reportError(`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, sourceSpan);
9971 }
9972 }
9973 _parseRegularEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
9974 // long format: 'target: eventName'
9975 const [target, eventName] = splitAtColon(name, [null, name]);
9976 const ast = this._parseAction(expression, handlerSpan);
9977 targetMatchableAttrs.push([name, ast.source]);
9978 targetEvents.push(new ParsedEvent(eventName, target, 0 /* Regular */, ast, sourceSpan, handlerSpan, keySpan));
9979 // Don't detect directives for event names for now,
9980 // so don't add the event name to the matchableAttrs
9981 }
9982 _parseAction(value, sourceSpan) {
9983 const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString();
9984 const absoluteOffset = (sourceSpan && sourceSpan.start) ? sourceSpan.start.offset : 0;
9985 try {
9986 const ast = this._exprParser.parseAction(value, sourceInfo, absoluteOffset, this._interpolationConfig);
9987 if (ast) {
9988 this._reportExpressionParserErrors(ast.errors, sourceSpan);
9989 }
9990 if (!ast || ast.ast instanceof EmptyExpr) {
9991 this._reportError(`Empty expressions are not allowed`, sourceSpan);
9992 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
9993 }
9994 this._checkPipes(ast, sourceSpan);
9995 return ast;
9996 }
9997 catch (e) {
9998 this._reportError(`${e}`, sourceSpan);
9999 return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
10000 }
10001 }
10002 _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
10003 this.errors.push(new ParseError(sourceSpan, message, level));
10004 }
10005 _reportExpressionParserErrors(errors, sourceSpan) {
10006 for (const error of errors) {
10007 this._reportError(error.message, sourceSpan);
10008 }
10009 }
10010 // Make sure all the used pipes are known in `this.pipesByName`
10011 _checkPipes(ast, sourceSpan) {
10012 if (ast && this.pipesByName) {
10013 const collector = new PipeCollector();
10014 ast.visit(collector);
10015 collector.pipes.forEach((ast, pipeName) => {
10016 const pipeMeta = this.pipesByName.get(pipeName);
10017 if (!pipeMeta) {
10018 this._reportError(`The pipe '${pipeName}' could not be found`, new ParseSourceSpan(sourceSpan.start.moveBy(ast.span.start), sourceSpan.start.moveBy(ast.span.end)));
10019 }
10020 else {
10021 this._usedPipes.set(pipeName, pipeMeta);
10022 }
10023 });
10024 }
10025 }
10026 /**
10027 * @param propName the name of the property / attribute
10028 * @param sourceSpan
10029 * @param isAttr true when binding to an attribute
10030 */
10031 _validatePropertyOrAttributeName(propName, sourceSpan, isAttr) {
10032 const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
10033 this._schemaRegistry.validateProperty(propName);
10034 if (report.error) {
10035 this._reportError(report.msg, sourceSpan, ParseErrorLevel.ERROR);
10036 }
10037 }
10038 }
10039 class PipeCollector extends RecursiveAstVisitor {
10040 constructor() {
10041 super(...arguments);
10042 this.pipes = new Map();
10043 }
10044 visitPipe(ast, context) {
10045 this.pipes.set(ast.name, ast);
10046 ast.exp.visit(this);
10047 this.visitAll(ast.args, context);
10048 return null;
10049 }
10050 }
10051 function isAnimationLabel(name) {
10052 return name[0] == '@';
10053 }
10054 function calcPossibleSecurityContexts(registry, selector, propName, isAttribute) {
10055 const ctxs = [];
10056 CssSelector.parse(selector).forEach((selector) => {
10057 const elementNames = selector.element ? [selector.element] : registry.allKnownElementNames();
10058 const notElementNames = new Set(selector.notSelectors.filter(selector => selector.isElementSelector())
10059 .map((selector) => selector.element));
10060 const possibleElementNames = elementNames.filter(elementName => !notElementNames.has(elementName));
10061 ctxs.push(...possibleElementNames.map(elementName => registry.securityContext(elementName, propName, isAttribute)));
10062 });
10063 return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();
10064 }
10065 /**
10066 * Compute a new ParseSourceSpan based off an original `sourceSpan` by using
10067 * absolute offsets from the specified `absoluteSpan`.
10068 *
10069 * @param sourceSpan original source span
10070 * @param absoluteSpan absolute source span to move to
10071 */
10072 function moveParseSourceSpan(sourceSpan, absoluteSpan) {
10073 // The difference of two absolute offsets provide the relative offset
10074 const startDiff = absoluteSpan.start - sourceSpan.start.offset;
10075 const endDiff = absoluteSpan.end - sourceSpan.end.offset;
10076 return new ParseSourceSpan(sourceSpan.start.moveBy(startDiff), sourceSpan.end.moveBy(endDiff), sourceSpan.fullStart.moveBy(startDiff), sourceSpan.details);
10077 }
10078
10079 /**
10080 * @license
10081 * Copyright Google LLC All Rights Reserved.
10082 *
10083 * Use of this source code is governed by an MIT-style license that can be
10084 * found in the LICENSE file at https://angular.io/license
10085 */
10086 const NG_CONTENT_SELECT_ATTR = 'select';
10087 const LINK_ELEMENT = 'link';
10088 const LINK_STYLE_REL_ATTR = 'rel';
10089 const LINK_STYLE_HREF_ATTR = 'href';
10090 const LINK_STYLE_REL_VALUE = 'stylesheet';
10091 const STYLE_ELEMENT = 'style';
10092 const SCRIPT_ELEMENT = 'script';
10093 const NG_NON_BINDABLE_ATTR = 'ngNonBindable';
10094 const NG_PROJECT_AS = 'ngProjectAs';
10095 function preparseElement(ast) {
10096 let selectAttr = null;
10097 let hrefAttr = null;
10098 let relAttr = null;
10099 let nonBindable = false;
10100 let projectAs = '';
10101 ast.attrs.forEach(attr => {
10102 const lcAttrName = attr.name.toLowerCase();
10103 if (lcAttrName == NG_CONTENT_SELECT_ATTR) {
10104 selectAttr = attr.value;
10105 }
10106 else if (lcAttrName == LINK_STYLE_HREF_ATTR) {
10107 hrefAttr = attr.value;
10108 }
10109 else if (lcAttrName == LINK_STYLE_REL_ATTR) {
10110 relAttr = attr.value;
10111 }
10112 else if (attr.name == NG_NON_BINDABLE_ATTR) {
10113 nonBindable = true;
10114 }
10115 else if (attr.name == NG_PROJECT_AS) {
10116 if (attr.value.length > 0) {
10117 projectAs = attr.value;
10118 }
10119 }
10120 });
10121 selectAttr = normalizeNgContentSelect(selectAttr);
10122 const nodeName = ast.name.toLowerCase();
10123 let type = PreparsedElementType.OTHER;
10124 if (isNgContent(nodeName)) {
10125 type = PreparsedElementType.NG_CONTENT;
10126 }
10127 else if (nodeName == STYLE_ELEMENT) {
10128 type = PreparsedElementType.STYLE;
10129 }
10130 else if (nodeName == SCRIPT_ELEMENT) {
10131 type = PreparsedElementType.SCRIPT;
10132 }
10133 else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
10134 type = PreparsedElementType.STYLESHEET;
10135 }
10136 return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable, projectAs);
10137 }
10138 var PreparsedElementType;
10139 (function (PreparsedElementType) {
10140 PreparsedElementType[PreparsedElementType["NG_CONTENT"] = 0] = "NG_CONTENT";
10141 PreparsedElementType[PreparsedElementType["STYLE"] = 1] = "STYLE";
10142 PreparsedElementType[PreparsedElementType["STYLESHEET"] = 2] = "STYLESHEET";
10143 PreparsedElementType[PreparsedElementType["SCRIPT"] = 3] = "SCRIPT";
10144 PreparsedElementType[PreparsedElementType["OTHER"] = 4] = "OTHER";
10145 })(PreparsedElementType || (PreparsedElementType = {}));
10146 class PreparsedElement {
10147 constructor(type, selectAttr, hrefAttr, nonBindable, projectAs) {
10148 this.type = type;
10149 this.selectAttr = selectAttr;
10150 this.hrefAttr = hrefAttr;
10151 this.nonBindable = nonBindable;
10152 this.projectAs = projectAs;
10153 }
10154 }
10155 function normalizeNgContentSelect(selectAttr) {
10156 if (selectAttr === null || selectAttr.length === 0) {
10157 return '*';
10158 }
10159 return selectAttr;
10160 }
10161
10162 /**
10163 * @license
10164 * Copyright Google LLC All Rights Reserved.
10165 *
10166 * Use of this source code is governed by an MIT-style license that can be
10167 * found in the LICENSE file at https://angular.io/license
10168 */
10169 function isEmptyExpression(ast) {
10170 if (ast instanceof ASTWithSource) {
10171 ast = ast.ast;
10172 }
10173 return ast instanceof EmptyExpr;
10174 }
10175
10176 /**
10177 * @license
10178 * Copyright Google LLC All Rights Reserved.
10179 *
10180 * Use of this source code is governed by an MIT-style license that can be
10181 * found in the LICENSE file at https://angular.io/license
10182 */
10183 /**
10184 * Parses string representation of a style and converts it into object literal.
10185 *
10186 * @param value string representation of style as used in the `style` attribute in HTML.
10187 * Example: `color: red; height: auto`.
10188 * @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
10189 * 'auto']`
10190 */
10191 function parse(value) {
10192 // we use a string array here instead of a string map
10193 // because a string-map is not guaranteed to retain the
10194 // order of the entries whereas a string array can be
10195 // constructed in a [key, value, key, value] format.
10196 const styles = [];
10197 let i = 0;
10198 let parenDepth = 0;
10199 let quote = 0 /* QuoteNone */;
10200 let valueStart = 0;
10201 let propStart = 0;
10202 let currentProp = null;
10203 let valueHasQuotes = false;
10204 while (i < value.length) {
10205 const token = value.charCodeAt(i++);
10206 switch (token) {
10207 case 40 /* OpenParen */:
10208 parenDepth++;
10209 break;
10210 case 41 /* CloseParen */:
10211 parenDepth--;
10212 break;
10213 case 39 /* QuoteSingle */:
10214 // valueStart needs to be there since prop values don't
10215 // have quotes in CSS
10216 valueHasQuotes = valueHasQuotes || valueStart > 0;
10217 if (quote === 0 /* QuoteNone */) {
10218 quote = 39 /* QuoteSingle */;
10219 }
10220 else if (quote === 39 /* QuoteSingle */ && value.charCodeAt(i - 1) !== 92 /* BackSlash */) {
10221 quote = 0 /* QuoteNone */;
10222 }
10223 break;
10224 case 34 /* QuoteDouble */:
10225 // same logic as above
10226 valueHasQuotes = valueHasQuotes || valueStart > 0;
10227 if (quote === 0 /* QuoteNone */) {
10228 quote = 34 /* QuoteDouble */;
10229 }
10230 else if (quote === 34 /* QuoteDouble */ && value.charCodeAt(i - 1) !== 92 /* BackSlash */) {
10231 quote = 0 /* QuoteNone */;
10232 }
10233 break;
10234 case 58 /* Colon */:
10235 if (!currentProp && parenDepth === 0 && quote === 0 /* QuoteNone */) {
10236 currentProp = hyphenate(value.substring(propStart, i - 1).trim());
10237 valueStart = i;
10238 }
10239 break;
10240 case 59 /* Semicolon */:
10241 if (currentProp && valueStart > 0 && parenDepth === 0 && quote === 0 /* QuoteNone */) {
10242 const styleVal = value.substring(valueStart, i - 1).trim();
10243 styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
10244 propStart = i;
10245 valueStart = 0;
10246 currentProp = null;
10247 valueHasQuotes = false;
10248 }
10249 break;
10250 }
10251 }
10252 if (currentProp && valueStart) {
10253 const styleVal = value.substr(valueStart).trim();
10254 styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
10255 }
10256 return styles;
10257 }
10258 function stripUnnecessaryQuotes(value) {
10259 const qS = value.charCodeAt(0);
10260 const qE = value.charCodeAt(value.length - 1);
10261 if (qS == qE && (qS == 39 /* QuoteSingle */ || qS == 34 /* QuoteDouble */)) {
10262 const tempValue = value.substring(1, value.length - 1);
10263 // special case to avoid using a multi-quoted string that was just chomped
10264 // (e.g. `font-family: "Verdana", "sans-serif"`)
10265 if (tempValue.indexOf('\'') == -1 && tempValue.indexOf('"') == -1) {
10266 value = tempValue;
10267 }
10268 }
10269 return value;
10270 }
10271 function hyphenate(value) {
10272 return value
10273 .replace(/[a-z][A-Z]/g, v => {
10274 return v.charAt(0) + '-' + v.charAt(1);
10275 })
10276 .toLowerCase();
10277 }
10278
10279 const IMPORTANT_FLAG = '!important';
10280 /**
10281 * Minimum amount of binding slots required in the runtime for style/class bindings.
10282 *
10283 * Styling in Angular uses up two slots in the runtime LView/TData data structures to
10284 * record binding data, property information and metadata.
10285 *
10286 * When a binding is registered it will place the following information in the `LView`:
10287 *
10288 * slot 1) binding value
10289 * slot 2) cached value (all other values collected before it in string form)
10290 *
10291 * When a binding is registered it will place the following information in the `TData`:
10292 *
10293 * slot 1) prop name
10294 * slot 2) binding index that points to the previous style/class binding (and some extra config
10295 * values)
10296 *
10297 * Let's imagine we have a binding that looks like so:
10298 *
10299 * ```
10300 * <div [style.width]="x" [style.height]="y">
10301 * ```
10302 *
10303 * Our `LView` and `TData` data-structures look like so:
10304 *
10305 * ```typescript
10306 * LView = [
10307 * // ...
10308 * x, // value of x
10309 * "width: x",
10310 *
10311 * y, // value of y
10312 * "width: x; height: y",
10313 * // ...
10314 * ];
10315 *
10316 * TData = [
10317 * // ...
10318 * "width", // binding slot 20
10319 * 0,
10320 *
10321 * "height",
10322 * 20,
10323 * // ...
10324 * ];
10325 * ```
10326 *
10327 * */
10328 const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2;
10329 /**
10330 * Produces creation/update instructions for all styling bindings (class and style)
10331 *
10332 * It also produces the creation instruction to register all initial styling values
10333 * (which are all the static class="..." and style="..." attribute values that exist
10334 * on an element within a template).
10335 *
10336 * The builder class below handles producing instructions for the following cases:
10337 *
10338 * - Static style/class attributes (style="..." and class="...")
10339 * - Dynamic style/class map bindings ([style]="map" and [class]="map|string")
10340 * - Dynamic style/class property bindings ([style.prop]="exp" and [class.name]="exp")
10341 *
10342 * Due to the complex relationship of all of these cases, the instructions generated
10343 * for these attributes/properties/bindings must be done so in the correct order. The
10344 * order which these must be generated is as follows:
10345 *
10346 * if (createMode) {
10347 * styling(...)
10348 * }
10349 * if (updateMode) {
10350 * styleMap(...)
10351 * classMap(...)
10352 * styleProp(...)
10353 * classProp(...)
10354 * }
10355 *
10356 * The creation/update methods within the builder class produce these instructions.
10357 */
10358 class StylingBuilder {
10359 constructor(_directiveExpr) {
10360 this._directiveExpr = _directiveExpr;
10361 /** Whether or not there are any static styling values present */
10362 this._hasInitialValues = false;
10363 /**
10364 * Whether or not there are any styling bindings present
10365 * (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
10366 */
10367 this.hasBindings = false;
10368 this.hasBindingsWithPipes = false;
10369 /** the input for [class] (if it exists) */
10370 this._classMapInput = null;
10371 /** the input for [style] (if it exists) */
10372 this._styleMapInput = null;
10373 /** an array of each [style.prop] input */
10374 this._singleStyleInputs = null;
10375 /** an array of each [class.name] input */
10376 this._singleClassInputs = null;
10377 this._lastStylingInput = null;
10378 this._firstStylingInput = null;
10379 // maps are used instead of hash maps because a Map will
10380 // retain the ordering of the keys
10381 /**
10382 * Represents the location of each style binding in the template
10383 * (e.g. `<div [style.width]="w" [style.height]="h">` implies
10384 * that `width=0` and `height=1`)
10385 */
10386 this._stylesIndex = new Map();
10387 /**
10388 * Represents the location of each class binding in the template
10389 * (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
10390 * that `big=0` and `hidden=1`)
10391 */
10392 this._classesIndex = new Map();
10393 this._initialStyleValues = [];
10394 this._initialClassValues = [];
10395 }
10396 /**
10397 * Registers a given input to the styling builder to be later used when producing AOT code.
10398 *
10399 * The code below will only accept the input if it is somehow tied to styling (whether it be
10400 * style/class bindings or static style/class attributes).
10401 */
10402 registerBoundInput(input) {
10403 // [attr.style] or [attr.class] are skipped in the code below,
10404 // they should not be treated as styling-based bindings since
10405 // they are intended to be written directly to the attr and
10406 // will therefore skip all style/class resolution that is present
10407 // with style="", [style]="" and [style.prop]="", class="",
10408 // [class.prop]="". [class]="" assignments
10409 let binding = null;
10410 let name = input.name;
10411 switch (input.type) {
10412 case 0 /* Property */:
10413 binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan);
10414 break;
10415 case 3 /* Style */:
10416 binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit);
10417 break;
10418 case 2 /* Class */:
10419 binding = this.registerClassInput(name, false, input.value, input.sourceSpan);
10420 break;
10421 }
10422 return binding ? true : false;
10423 }
10424 registerInputBasedOnName(name, expression, sourceSpan) {
10425 let binding = null;
10426 const prefix = name.substring(0, 6);
10427 const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!';
10428 const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');
10429 if (isStyle || isClass) {
10430 const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no
10431 const property = name.substr(isMapBased ? 5 : 6); // the dot explains why there's a +1
10432 if (isStyle) {
10433 binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan);
10434 }
10435 else {
10436 binding = this.registerClassInput(property, isMapBased, expression, sourceSpan);
10437 }
10438 }
10439 return binding;
10440 }
10441 registerStyleInput(name, isMapBased, value, sourceSpan, suffix) {
10442 if (isEmptyExpression(value)) {
10443 return null;
10444 }
10445 name = normalizePropName(name);
10446 const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
10447 suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
10448 const entry = { name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag };
10449 if (isMapBased) {
10450 this._styleMapInput = entry;
10451 }
10452 else {
10453 (this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
10454 registerIntoMap(this._stylesIndex, property);
10455 }
10456 this._lastStylingInput = entry;
10457 this._firstStylingInput = this._firstStylingInput || entry;
10458 this._checkForPipes(value);
10459 this.hasBindings = true;
10460 return entry;
10461 }
10462 registerClassInput(name, isMapBased, value, sourceSpan) {
10463 if (isEmptyExpression(value)) {
10464 return null;
10465 }
10466 const { property, hasOverrideFlag } = parseProperty(name);
10467 const entry = { name: property, value, sourceSpan, hasOverrideFlag, suffix: null };
10468 if (isMapBased) {
10469 if (this._classMapInput) {
10470 throw new Error('[class] and [className] bindings cannot be used on the same element simultaneously');
10471 }
10472 this._classMapInput = entry;
10473 }
10474 else {
10475 (this._singleClassInputs = this._singleClassInputs || []).push(entry);
10476 registerIntoMap(this._classesIndex, property);
10477 }
10478 this._lastStylingInput = entry;
10479 this._firstStylingInput = this._firstStylingInput || entry;
10480 this._checkForPipes(value);
10481 this.hasBindings = true;
10482 return entry;
10483 }
10484 _checkForPipes(value) {
10485 if ((value instanceof ASTWithSource) && (value.ast instanceof BindingPipe)) {
10486 this.hasBindingsWithPipes = true;
10487 }
10488 }
10489 /**
10490 * Registers the element's static style string value to the builder.
10491 *
10492 * @param value the style string (e.g. `width:100px; height:200px;`)
10493 */
10494 registerStyleAttr(value) {
10495 this._initialStyleValues = parse(value);
10496 this._hasInitialValues = true;
10497 }
10498 /**
10499 * Registers the element's static class string value to the builder.
10500 *
10501 * @param value the className string (e.g. `disabled gold zoom`)
10502 */
10503 registerClassAttr(value) {
10504 this._initialClassValues = value.trim().split(/\s+/g);
10505 this._hasInitialValues = true;
10506 }
10507 /**
10508 * Appends all styling-related expressions to the provided attrs array.
10509 *
10510 * @param attrs an existing array where each of the styling expressions
10511 * will be inserted into.
10512 */
10513 populateInitialStylingAttrs(attrs) {
10514 // [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
10515 if (this._initialClassValues.length) {
10516 attrs.push(literal(1 /* Classes */));
10517 for (let i = 0; i < this._initialClassValues.length; i++) {
10518 attrs.push(literal(this._initialClassValues[i]));
10519 }
10520 }
10521 // [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]
10522 if (this._initialStyleValues.length) {
10523 attrs.push(literal(2 /* Styles */));
10524 for (let i = 0; i < this._initialStyleValues.length; i += 2) {
10525 attrs.push(literal(this._initialStyleValues[i]), literal(this._initialStyleValues[i + 1]));
10526 }
10527 }
10528 }
10529 /**
10530 * Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
10531 *
10532 * The instruction generation code below is used for producing the AOT statement code which is
10533 * responsible for registering initial styles (within a directive hostBindings' creation block),
10534 * as well as any of the provided attribute values, to the directive host element.
10535 */
10536 assignHostAttrs(attrs, definitionMap) {
10537 if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
10538 this.populateInitialStylingAttrs(attrs);
10539 definitionMap.set('hostAttrs', literalArr(attrs));
10540 }
10541 }
10542 /**
10543 * Builds an instruction with all the expressions and parameters for `classMap`.
10544 *
10545 * The instruction data will contain all expressions for `classMap` to function
10546 * which includes the `[class]` expression params.
10547 */
10548 buildClassMapInstruction(valueConverter) {
10549 if (this._classMapInput) {
10550 return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
10551 }
10552 return null;
10553 }
10554 /**
10555 * Builds an instruction with all the expressions and parameters for `styleMap`.
10556 *
10557 * The instruction data will contain all expressions for `styleMap` to function
10558 * which includes the `[style]` expression params.
10559 */
10560 buildStyleMapInstruction(valueConverter) {
10561 if (this._styleMapInput) {
10562 return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
10563 }
10564 return null;
10565 }
10566 _buildMapBasedInstruction(valueConverter, isClassBased, stylingInput) {
10567 // each styling binding value is stored in the LView
10568 // map-based bindings allocate two slots: one for the
10569 // previous binding value and another for the previous
10570 // className or style attribute value.
10571 let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
10572 // these values must be outside of the update block so that they can
10573 // be evaluated (the AST visit call) during creation time so that any
10574 // pipes can be picked up in time before the template is built
10575 const mapValue = stylingInput.value.visit(valueConverter);
10576 let reference;
10577 if (mapValue instanceof Interpolation) {
10578 totalBindingSlotsRequired += mapValue.expressions.length;
10579 reference = isClassBased ? getClassMapInterpolationExpression(mapValue) :
10580 getStyleMapInterpolationExpression(mapValue);
10581 }
10582 else {
10583 reference = isClassBased ? Identifiers$1.classMap : Identifiers$1.styleMap;
10584 }
10585 return {
10586 reference,
10587 calls: [{
10588 supportsInterpolation: true,
10589 sourceSpan: stylingInput.sourceSpan,
10590 allocateBindingSlots: totalBindingSlotsRequired,
10591 params: (convertFn) => {
10592 const convertResult = convertFn(mapValue);
10593 const params = Array.isArray(convertResult) ? convertResult : [convertResult];
10594 return params;
10595 }
10596 }]
10597 };
10598 }
10599 _buildSingleInputs(reference, inputs, valueConverter, getInterpolationExpressionFn, isClassBased) {
10600 const instructions = [];
10601 inputs.forEach(input => {
10602 const previousInstruction = instructions[instructions.length - 1];
10603 const value = input.value.visit(valueConverter);
10604 let referenceForCall = reference;
10605 // each styling binding value is stored in the LView
10606 // but there are two values stored for each binding:
10607 // 1) the value itself
10608 // 2) an intermediate value (concatenation of style up to this point).
10609 // We need to store the intermediate value so that we don't allocate
10610 // the strings on each CD.
10611 let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
10612 if (value instanceof Interpolation) {
10613 totalBindingSlotsRequired += value.expressions.length;
10614 if (getInterpolationExpressionFn) {
10615 referenceForCall = getInterpolationExpressionFn(value);
10616 }
10617 }
10618 const call = {
10619 sourceSpan: input.sourceSpan,
10620 allocateBindingSlots: totalBindingSlotsRequired,
10621 supportsInterpolation: !!getInterpolationExpressionFn,
10622 params: (convertFn) => {
10623 // params => stylingProp(propName, value, suffix)
10624 const params = [];
10625 params.push(literal(input.name));
10626 const convertResult = convertFn(value);
10627 if (Array.isArray(convertResult)) {
10628 params.push(...convertResult);
10629 }
10630 else {
10631 params.push(convertResult);
10632 }
10633 // [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore,
10634 // if that is detected then we need to pass that in as an optional param.
10635 if (!isClassBased && input.suffix !== null) {
10636 params.push(literal(input.suffix));
10637 }
10638 return params;
10639 }
10640 };
10641 // If we ended up generating a call to the same instruction as the previous styling property
10642 // we can chain the calls together safely to save some bytes, otherwise we have to generate
10643 // a separate instruction call. This is primarily a concern with interpolation instructions
10644 // where we may start off with one `reference`, but end up using another based on the
10645 // number of interpolations.
10646 if (previousInstruction && previousInstruction.reference === referenceForCall) {
10647 previousInstruction.calls.push(call);
10648 }
10649 else {
10650 instructions.push({ reference: referenceForCall, calls: [call] });
10651 }
10652 });
10653 return instructions;
10654 }
10655 _buildClassInputs(valueConverter) {
10656 if (this._singleClassInputs) {
10657 return this._buildSingleInputs(Identifiers$1.classProp, this._singleClassInputs, valueConverter, null, true);
10658 }
10659 return [];
10660 }
10661 _buildStyleInputs(valueConverter) {
10662 if (this._singleStyleInputs) {
10663 return this._buildSingleInputs(Identifiers$1.styleProp, this._singleStyleInputs, valueConverter, getStylePropInterpolationExpression, false);
10664 }
10665 return [];
10666 }
10667 /**
10668 * Constructs all instructions which contain the expressions that will be placed
10669 * into the update block of a template function or a directive hostBindings function.
10670 */
10671 buildUpdateLevelInstructions(valueConverter) {
10672 const instructions = [];
10673 if (this.hasBindings) {
10674 const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
10675 if (styleMapInstruction) {
10676 instructions.push(styleMapInstruction);
10677 }
10678 const classMapInstruction = this.buildClassMapInstruction(valueConverter);
10679 if (classMapInstruction) {
10680 instructions.push(classMapInstruction);
10681 }
10682 instructions.push(...this._buildStyleInputs(valueConverter));
10683 instructions.push(...this._buildClassInputs(valueConverter));
10684 }
10685 return instructions;
10686 }
10687 }
10688 function registerIntoMap(map, key) {
10689 if (!map.has(key)) {
10690 map.set(key, map.size);
10691 }
10692 }
10693 function parseProperty(name) {
10694 let hasOverrideFlag = false;
10695 const overrideIndex = name.indexOf(IMPORTANT_FLAG);
10696 if (overrideIndex !== -1) {
10697 name = overrideIndex > 0 ? name.substring(0, overrideIndex) : '';
10698 hasOverrideFlag = true;
10699 }
10700 let suffix = null;
10701 let property = name;
10702 const unitIndex = name.lastIndexOf('.');
10703 if (unitIndex > 0) {
10704 suffix = name.substr(unitIndex + 1);
10705 property = name.substring(0, unitIndex);
10706 }
10707 return { property, suffix, hasOverrideFlag };
10708 }
10709 /**
10710 * Gets the instruction to generate for an interpolated class map.
10711 * @param interpolation An Interpolation AST
10712 */
10713 function getClassMapInterpolationExpression(interpolation) {
10714 switch (getInterpolationArgsLength(interpolation)) {
10715 case 1:
10716 return Identifiers$1.classMap;
10717 case 3:
10718 return Identifiers$1.classMapInterpolate1;
10719 case 5:
10720 return Identifiers$1.classMapInterpolate2;
10721 case 7:
10722 return Identifiers$1.classMapInterpolate3;
10723 case 9:
10724 return Identifiers$1.classMapInterpolate4;
10725 case 11:
10726 return Identifiers$1.classMapInterpolate5;
10727 case 13:
10728 return Identifiers$1.classMapInterpolate6;
10729 case 15:
10730 return Identifiers$1.classMapInterpolate7;
10731 case 17:
10732 return Identifiers$1.classMapInterpolate8;
10733 default:
10734 return Identifiers$1.classMapInterpolateV;
10735 }
10736 }
10737 /**
10738 * Gets the instruction to generate for an interpolated style map.
10739 * @param interpolation An Interpolation AST
10740 */
10741 function getStyleMapInterpolationExpression(interpolation) {
10742 switch (getInterpolationArgsLength(interpolation)) {
10743 case 1:
10744 return Identifiers$1.styleMap;
10745 case 3:
10746 return Identifiers$1.styleMapInterpolate1;
10747 case 5:
10748 return Identifiers$1.styleMapInterpolate2;
10749 case 7:
10750 return Identifiers$1.styleMapInterpolate3;
10751 case 9:
10752 return Identifiers$1.styleMapInterpolate4;
10753 case 11:
10754 return Identifiers$1.styleMapInterpolate5;
10755 case 13:
10756 return Identifiers$1.styleMapInterpolate6;
10757 case 15:
10758 return Identifiers$1.styleMapInterpolate7;
10759 case 17:
10760 return Identifiers$1.styleMapInterpolate8;
10761 default:
10762 return Identifiers$1.styleMapInterpolateV;
10763 }
10764 }
10765 /**
10766 * Gets the instruction to generate for an interpolated style prop.
10767 * @param interpolation An Interpolation AST
10768 */
10769 function getStylePropInterpolationExpression(interpolation) {
10770 switch (getInterpolationArgsLength(interpolation)) {
10771 case 1:
10772 return Identifiers$1.styleProp;
10773 case 3:
10774 return Identifiers$1.stylePropInterpolate1;
10775 case 5:
10776 return Identifiers$1.stylePropInterpolate2;
10777 case 7:
10778 return Identifiers$1.stylePropInterpolate3;
10779 case 9:
10780 return Identifiers$1.stylePropInterpolate4;
10781 case 11:
10782 return Identifiers$1.stylePropInterpolate5;
10783 case 13:
10784 return Identifiers$1.stylePropInterpolate6;
10785 case 15:
10786 return Identifiers$1.stylePropInterpolate7;
10787 case 17:
10788 return Identifiers$1.stylePropInterpolate8;
10789 default:
10790 return Identifiers$1.stylePropInterpolateV;
10791 }
10792 }
10793 function normalizePropName(prop) {
10794 return hyphenate(prop);
10795 }
10796
10797 /**
10798 * @license
10799 * Copyright Google LLC All Rights Reserved.
10800 *
10801 * Use of this source code is governed by an MIT-style license that can be
10802 * found in the LICENSE file at https://angular.io/license
10803 */
10804 var TokenType$1;
10805 (function (TokenType) {
10806 TokenType[TokenType["Character"] = 0] = "Character";
10807 TokenType[TokenType["Identifier"] = 1] = "Identifier";
10808 TokenType[TokenType["Keyword"] = 2] = "Keyword";
10809 TokenType[TokenType["String"] = 3] = "String";
10810 TokenType[TokenType["Operator"] = 4] = "Operator";
10811 TokenType[TokenType["Number"] = 5] = "Number";
10812 TokenType[TokenType["Error"] = 6] = "Error";
10813 })(TokenType$1 || (TokenType$1 = {}));
10814 const KEYWORDS = ['var', 'let', 'as', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
10815 class Lexer {
10816 tokenize(text) {
10817 const scanner = new _Scanner(text);
10818 const tokens = [];
10819 let token = scanner.scanToken();
10820 while (token != null) {
10821 tokens.push(token);
10822 token = scanner.scanToken();
10823 }
10824 return tokens;
10825 }
10826 }
10827 class Token$1 {
10828 constructor(index, end, type, numValue, strValue) {
10829 this.index = index;
10830 this.end = end;
10831 this.type = type;
10832 this.numValue = numValue;
10833 this.strValue = strValue;
10834 }
10835 isCharacter(code) {
10836 return this.type == TokenType$1.Character && this.numValue == code;
10837 }
10838 isNumber() {
10839 return this.type == TokenType$1.Number;
10840 }
10841 isString() {
10842 return this.type == TokenType$1.String;
10843 }
10844 isOperator(operator) {
10845 return this.type == TokenType$1.Operator && this.strValue == operator;
10846 }
10847 isIdentifier() {
10848 return this.type == TokenType$1.Identifier;
10849 }
10850 isKeyword() {
10851 return this.type == TokenType$1.Keyword;
10852 }
10853 isKeywordLet() {
10854 return this.type == TokenType$1.Keyword && this.strValue == 'let';
10855 }
10856 isKeywordAs() {
10857 return this.type == TokenType$1.Keyword && this.strValue == 'as';
10858 }
10859 isKeywordNull() {
10860 return this.type == TokenType$1.Keyword && this.strValue == 'null';
10861 }
10862 isKeywordUndefined() {
10863 return this.type == TokenType$1.Keyword && this.strValue == 'undefined';
10864 }
10865 isKeywordTrue() {
10866 return this.type == TokenType$1.Keyword && this.strValue == 'true';
10867 }
10868 isKeywordFalse() {
10869 return this.type == TokenType$1.Keyword && this.strValue == 'false';
10870 }
10871 isKeywordThis() {
10872 return this.type == TokenType$1.Keyword && this.strValue == 'this';
10873 }
10874 isError() {
10875 return this.type == TokenType$1.Error;
10876 }
10877 toNumber() {
10878 return this.type == TokenType$1.Number ? this.numValue : -1;
10879 }
10880 toString() {
10881 switch (this.type) {
10882 case TokenType$1.Character:
10883 case TokenType$1.Identifier:
10884 case TokenType$1.Keyword:
10885 case TokenType$1.Operator:
10886 case TokenType$1.String:
10887 case TokenType$1.Error:
10888 return this.strValue;
10889 case TokenType$1.Number:
10890 return this.numValue.toString();
10891 default:
10892 return null;
10893 }
10894 }
10895 }
10896 function newCharacterToken(index, end, code) {
10897 return new Token$1(index, end, TokenType$1.Character, code, String.fromCharCode(code));
10898 }
10899 function newIdentifierToken(index, end, text) {
10900 return new Token$1(index, end, TokenType$1.Identifier, 0, text);
10901 }
10902 function newKeywordToken(index, end, text) {
10903 return new Token$1(index, end, TokenType$1.Keyword, 0, text);
10904 }
10905 function newOperatorToken(index, end, text) {
10906 return new Token$1(index, end, TokenType$1.Operator, 0, text);
10907 }
10908 function newStringToken(index, end, text) {
10909 return new Token$1(index, end, TokenType$1.String, 0, text);
10910 }
10911 function newNumberToken(index, end, n) {
10912 return new Token$1(index, end, TokenType$1.Number, n, '');
10913 }
10914 function newErrorToken(index, end, message) {
10915 return new Token$1(index, end, TokenType$1.Error, 0, message);
10916 }
10917 const EOF = new Token$1(-1, -1, TokenType$1.Character, 0, '');
10918 class _Scanner {
10919 constructor(input) {
10920 this.input = input;
10921 this.peek = 0;
10922 this.index = -1;
10923 this.length = input.length;
10924 this.advance();
10925 }
10926 advance() {
10927 this.peek = ++this.index >= this.length ? $EOF : this.input.charCodeAt(this.index);
10928 }
10929 scanToken() {
10930 const input = this.input, length = this.length;
10931 let peek = this.peek, index = this.index;
10932 // Skip whitespace.
10933 while (peek <= $SPACE) {
10934 if (++index >= length) {
10935 peek = $EOF;
10936 break;
10937 }
10938 else {
10939 peek = input.charCodeAt(index);
10940 }
10941 }
10942 this.peek = peek;
10943 this.index = index;
10944 if (index >= length) {
10945 return null;
10946 }
10947 // Handle identifiers and numbers.
10948 if (isIdentifierStart(peek))
10949 return this.scanIdentifier();
10950 if (isDigit(peek))
10951 return this.scanNumber(index);
10952 const start = index;
10953 switch (peek) {
10954 case $PERIOD:
10955 this.advance();
10956 return isDigit(this.peek) ? this.scanNumber(start) :
10957 newCharacterToken(start, this.index, $PERIOD);
10958 case $LPAREN:
10959 case $RPAREN:
10960 case $LBRACE:
10961 case $RBRACE:
10962 case $LBRACKET:
10963 case $RBRACKET:
10964 case $COMMA:
10965 case $COLON:
10966 case $SEMICOLON:
10967 return this.scanCharacter(start, peek);
10968 case $SQ:
10969 case $DQ:
10970 return this.scanString();
10971 case $HASH:
10972 case $PLUS:
10973 case $MINUS:
10974 case $STAR:
10975 case $SLASH:
10976 case $PERCENT:
10977 case $CARET:
10978 return this.scanOperator(start, String.fromCharCode(peek));
10979 case $QUESTION:
10980 return this.scanComplexOperator(start, '?', $PERIOD, '.');
10981 case $LT:
10982 case $GT:
10983 return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=');
10984 case $BANG:
10985 case $EQ:
10986 return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=', $EQ, '=');
10987 case $AMPERSAND:
10988 return this.scanComplexOperator(start, '&', $AMPERSAND, '&');
10989 case $BAR:
10990 return this.scanComplexOperator(start, '|', $BAR, '|');
10991 case $NBSP:
10992 while (isWhitespace(this.peek))
10993 this.advance();
10994 return this.scanToken();
10995 }
10996 this.advance();
10997 return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);
10998 }
10999 scanCharacter(start, code) {
11000 this.advance();
11001 return newCharacterToken(start, this.index, code);
11002 }
11003 scanOperator(start, str) {
11004 this.advance();
11005 return newOperatorToken(start, this.index, str);
11006 }
11007 /**
11008 * Tokenize a 2/3 char long operator
11009 *
11010 * @param start start index in the expression
11011 * @param one first symbol (always part of the operator)
11012 * @param twoCode code point for the second symbol
11013 * @param two second symbol (part of the operator when the second code point matches)
11014 * @param threeCode code point for the third symbol
11015 * @param three third symbol (part of the operator when provided and matches source expression)
11016 */
11017 scanComplexOperator(start, one, twoCode, two, threeCode, three) {
11018 this.advance();
11019 let str = one;
11020 if (this.peek == twoCode) {
11021 this.advance();
11022 str += two;
11023 }
11024 if (threeCode != null && this.peek == threeCode) {
11025 this.advance();
11026 str += three;
11027 }
11028 return newOperatorToken(start, this.index, str);
11029 }
11030 scanIdentifier() {
11031 const start = this.index;
11032 this.advance();
11033 while (isIdentifierPart(this.peek))
11034 this.advance();
11035 const str = this.input.substring(start, this.index);
11036 return KEYWORDS.indexOf(str) > -1 ? newKeywordToken(start, this.index, str) :
11037 newIdentifierToken(start, this.index, str);
11038 }
11039 scanNumber(start) {
11040 let simple = (this.index === start);
11041 this.advance(); // Skip initial digit.
11042 while (true) {
11043 if (isDigit(this.peek)) ;
11044 else if (this.peek == $PERIOD) {
11045 simple = false;
11046 }
11047 else if (isExponentStart(this.peek)) {
11048 this.advance();
11049 if (isExponentSign(this.peek))
11050 this.advance();
11051 if (!isDigit(this.peek))
11052 return this.error('Invalid exponent', -1);
11053 simple = false;
11054 }
11055 else {
11056 break;
11057 }
11058 this.advance();
11059 }
11060 const str = this.input.substring(start, this.index);
11061 const value = simple ? parseIntAutoRadix(str) : parseFloat(str);
11062 return newNumberToken(start, this.index, value);
11063 }
11064 scanString() {
11065 const start = this.index;
11066 const quote = this.peek;
11067 this.advance(); // Skip initial quote.
11068 let buffer = '';
11069 let marker = this.index;
11070 const input = this.input;
11071 while (this.peek != quote) {
11072 if (this.peek == $BACKSLASH) {
11073 buffer += input.substring(marker, this.index);
11074 this.advance();
11075 let unescapedCode;
11076 // Workaround for TS2.1-introduced type strictness
11077 this.peek = this.peek;
11078 if (this.peek == $u) {
11079 // 4 character hex code for unicode character.
11080 const hex = input.substring(this.index + 1, this.index + 5);
11081 if (/^[0-9a-f]+$/i.test(hex)) {
11082 unescapedCode = parseInt(hex, 16);
11083 }
11084 else {
11085 return this.error(`Invalid unicode escape [\\u${hex}]`, 0);
11086 }
11087 for (let i = 0; i < 5; i++) {
11088 this.advance();
11089 }
11090 }
11091 else {
11092 unescapedCode = unescape(this.peek);
11093 this.advance();
11094 }
11095 buffer += String.fromCharCode(unescapedCode);
11096 marker = this.index;
11097 }
11098 else if (this.peek == $EOF) {
11099 return this.error('Unterminated quote', 0);
11100 }
11101 else {
11102 this.advance();
11103 }
11104 }
11105 const last = input.substring(marker, this.index);
11106 this.advance(); // Skip terminating quote.
11107 return newStringToken(start, this.index, buffer + last);
11108 }
11109 error(message, offset) {
11110 const position = this.index + offset;
11111 return newErrorToken(position, this.index, `Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
11112 }
11113 }
11114 function isIdentifierStart(code) {
11115 return ($a <= code && code <= $z) || ($A <= code && code <= $Z) ||
11116 (code == $_) || (code == $$);
11117 }
11118 function isIdentifier(input) {
11119 if (input.length == 0)
11120 return false;
11121 const scanner = new _Scanner(input);
11122 if (!isIdentifierStart(scanner.peek))
11123 return false;
11124 scanner.advance();
11125 while (scanner.peek !== $EOF) {
11126 if (!isIdentifierPart(scanner.peek))
11127 return false;
11128 scanner.advance();
11129 }
11130 return true;
11131 }
11132 function isIdentifierPart(code) {
11133 return isAsciiLetter(code) || isDigit(code) || (code == $_) ||
11134 (code == $$);
11135 }
11136 function isExponentStart(code) {
11137 return code == $e || code == $E;
11138 }
11139 function isExponentSign(code) {
11140 return code == $MINUS || code == $PLUS;
11141 }
11142 function isQuote(code) {
11143 return code === $SQ || code === $DQ || code === $BT;
11144 }
11145 function unescape(code) {
11146 switch (code) {
11147 case $n:
11148 return $LF;
11149 case $f:
11150 return $FF;
11151 case $r:
11152 return $CR;
11153 case $t:
11154 return $TAB;
11155 case $v:
11156 return $VTAB;
11157 default:
11158 return code;
11159 }
11160 }
11161 function parseIntAutoRadix(text) {
11162 const result = parseInt(text);
11163 if (isNaN(result)) {
11164 throw new Error('Invalid integer literal when parsing ' + text);
11165 }
11166 return result;
11167 }
11168
11169 /**
11170 * @license
11171 * Copyright Google LLC All Rights Reserved.
11172 *
11173 * Use of this source code is governed by an MIT-style license that can be
11174 * found in the LICENSE file at https://angular.io/license
11175 */
11176 class SplitInterpolation {
11177 constructor(strings, expressions, offsets) {
11178 this.strings = strings;
11179 this.expressions = expressions;
11180 this.offsets = offsets;
11181 }
11182 }
11183 class TemplateBindingParseResult {
11184 constructor(templateBindings, warnings, errors) {
11185 this.templateBindings = templateBindings;
11186 this.warnings = warnings;
11187 this.errors = errors;
11188 }
11189 }
11190 class Parser$1 {
11191 constructor(_lexer) {
11192 this._lexer = _lexer;
11193 this.errors = [];
11194 this.simpleExpressionChecker = SimpleExpressionChecker;
11195 }
11196 parseAction(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
11197 this._checkNoInterpolation(input, location, interpolationConfig);
11198 const sourceToLex = this._stripComments(input);
11199 const tokens = this._lexer.tokenize(this._stripComments(input));
11200 const ast = new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, true, this.errors, input.length - sourceToLex.length)
11201 .parseChain();
11202 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
11203 }
11204 parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
11205 const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);
11206 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
11207 }
11208 checkSimpleExpression(ast) {
11209 const checker = new this.simpleExpressionChecker();
11210 ast.visit(checker);
11211 return checker.errors;
11212 }
11213 parseSimpleBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
11214 const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);
11215 const errors = this.checkSimpleExpression(ast);
11216 if (errors.length > 0) {
11217 this._reportError(`Host binding expression cannot contain ${errors.join(' ')}`, input, location);
11218 }
11219 return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
11220 }
11221 _reportError(message, input, errLocation, ctxLocation) {
11222 this.errors.push(new ParserError(message, input, errLocation, ctxLocation));
11223 }
11224 _parseBindingAst(input, location, absoluteOffset, interpolationConfig) {
11225 // Quotes expressions use 3rd-party expression language. We don't want to use
11226 // our lexer or parser for that, so we check for that ahead of time.
11227 const quote = this._parseQuote(input, location, absoluteOffset);
11228 if (quote != null) {
11229 return quote;
11230 }
11231 this._checkNoInterpolation(input, location, interpolationConfig);
11232 const sourceToLex = this._stripComments(input);
11233 const tokens = this._lexer.tokenize(sourceToLex);
11234 return new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, input.length - sourceToLex.length)
11235 .parseChain();
11236 }
11237 _parseQuote(input, location, absoluteOffset) {
11238 if (input == null)
11239 return null;
11240 const prefixSeparatorIndex = input.indexOf(':');
11241 if (prefixSeparatorIndex == -1)
11242 return null;
11243 const prefix = input.substring(0, prefixSeparatorIndex).trim();
11244 if (!isIdentifier(prefix))
11245 return null;
11246 const uninterpretedExpression = input.substring(prefixSeparatorIndex + 1);
11247 const span = new ParseSpan(0, input.length);
11248 return new Quote(span, span.toAbsolute(absoluteOffset), prefix, uninterpretedExpression, location);
11249 }
11250 /**
11251 * Parse microsyntax template expression and return a list of bindings or
11252 * parsing errors in case the given expression is invalid.
11253 *
11254 * For example,
11255 * ```
11256 * <div *ngFor="let item of items">
11257 * ^ ^ absoluteValueOffset for `templateValue`
11258 * absoluteKeyOffset for `templateKey`
11259 * ```
11260 * contains three bindings:
11261 * 1. ngFor -> null
11262 * 2. item -> NgForOfContext.$implicit
11263 * 3. ngForOf -> items
11264 *
11265 * This is apparent from the de-sugared template:
11266 * ```
11267 * <ng-template ngFor let-item [ngForOf]="items">
11268 * ```
11269 *
11270 * @param templateKey name of directive, without the * prefix. For example: ngIf, ngFor
11271 * @param templateValue RHS of the microsyntax attribute
11272 * @param templateUrl template filename if it's external, component filename if it's inline
11273 * @param absoluteKeyOffset start of the `templateKey`
11274 * @param absoluteValueOffset start of the `templateValue`
11275 */
11276 parseTemplateBindings(templateKey, templateValue, templateUrl, absoluteKeyOffset, absoluteValueOffset) {
11277 const tokens = this._lexer.tokenize(templateValue);
11278 const parser = new _ParseAST(templateValue, templateUrl, absoluteValueOffset, tokens, templateValue.length, false /* parseAction */, this.errors, 0 /* relative offset */);
11279 return parser.parseTemplateBindings({
11280 source: templateKey,
11281 span: new AbsoluteSourceSpan(absoluteKeyOffset, absoluteKeyOffset + templateKey.length),
11282 });
11283 }
11284 parseInterpolation(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
11285 const { strings, expressions, offsets } = this.splitInterpolation(input, location, interpolationConfig);
11286 if (expressions.length === 0)
11287 return null;
11288 const expressionNodes = [];
11289 for (let i = 0; i < expressions.length; ++i) {
11290 const expressionText = expressions[i].text;
11291 const sourceToLex = this._stripComments(expressionText);
11292 const tokens = this._lexer.tokenize(sourceToLex);
11293 const ast = new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, offsets[i] + (expressionText.length - sourceToLex.length))
11294 .parseChain();
11295 expressionNodes.push(ast);
11296 }
11297 return this.createInterpolationAst(strings.map(s => s.text), expressionNodes, input, location, absoluteOffset);
11298 }
11299 /**
11300 * Similar to `parseInterpolation`, but treats the provided string as a single expression
11301 * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).
11302 * This is used for parsing the switch expression in ICUs.
11303 */
11304 parseInterpolationExpression(expression, location, absoluteOffset) {
11305 const sourceToLex = this._stripComments(expression);
11306 const tokens = this._lexer.tokenize(sourceToLex);
11307 const ast = new _ParseAST(expression, location, absoluteOffset, tokens, sourceToLex.length,
11308 /* parseAction */ false, this.errors, 0)
11309 .parseChain();
11310 const strings = ['', '']; // The prefix and suffix strings are both empty
11311 return this.createInterpolationAst(strings, [ast], expression, location, absoluteOffset);
11312 }
11313 createInterpolationAst(strings, expressions, input, location, absoluteOffset) {
11314 const span = new ParseSpan(0, input.length);
11315 const interpolation = new Interpolation(span, span.toAbsolute(absoluteOffset), strings, expressions);
11316 return new ASTWithSource(interpolation, input, location, absoluteOffset, this.errors);
11317 }
11318 /**
11319 * Splits a string of text into "raw" text segments and expressions present in interpolations in
11320 * the string.
11321 * Returns `null` if there are no interpolations, otherwise a
11322 * `SplitInterpolation` with splits that look like
11323 * <raw text> <expression> <raw text> ... <raw text> <expression> <raw text>
11324 */
11325 splitInterpolation(input, location, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
11326 const strings = [];
11327 const expressions = [];
11328 const offsets = [];
11329 let i = 0;
11330 let atInterpolation = false;
11331 let extendLastString = false;
11332 let { start: interpStart, end: interpEnd } = interpolationConfig;
11333 while (i < input.length) {
11334 if (!atInterpolation) {
11335 // parse until starting {{
11336 const start = i;
11337 i = input.indexOf(interpStart, i);
11338 if (i === -1) {
11339 i = input.length;
11340 }
11341 const text = input.substring(start, i);
11342 strings.push({ text, start, end: i });
11343 atInterpolation = true;
11344 }
11345 else {
11346 // parse from starting {{ to ending }} while ignoring content inside quotes.
11347 const fullStart = i;
11348 const exprStart = fullStart + interpStart.length;
11349 const exprEnd = this._getInterpolationEndIndex(input, interpEnd, exprStart);
11350 if (exprEnd === -1) {
11351 // Could not find the end of the interpolation; do not parse an expression.
11352 // Instead we should extend the content on the last raw string.
11353 atInterpolation = false;
11354 extendLastString = true;
11355 break;
11356 }
11357 const fullEnd = exprEnd + interpEnd.length;
11358 const text = input.substring(exprStart, exprEnd);
11359 if (text.trim().length === 0) {
11360 this._reportError('Blank expressions are not allowed in interpolated strings', input, `at column ${i} in`, location);
11361 }
11362 expressions.push({ text, start: fullStart, end: fullEnd });
11363 offsets.push(exprStart);
11364 i = fullEnd;
11365 atInterpolation = false;
11366 }
11367 }
11368 if (!atInterpolation) {
11369 // If we are now at a text section, add the remaining content as a raw string.
11370 if (extendLastString) {
11371 const piece = strings[strings.length - 1];
11372 piece.text += input.substring(i);
11373 piece.end = input.length;
11374 }
11375 else {
11376 strings.push({ text: input.substring(i), start: i, end: input.length });
11377 }
11378 }
11379 return new SplitInterpolation(strings, expressions, offsets);
11380 }
11381 wrapLiteralPrimitive(input, location, absoluteOffset) {
11382 const span = new ParseSpan(0, input == null ? 0 : input.length);
11383 return new ASTWithSource(new LiteralPrimitive(span, span.toAbsolute(absoluteOffset), input), input, location, absoluteOffset, this.errors);
11384 }
11385 _stripComments(input) {
11386 const i = this._commentStart(input);
11387 return i != null ? input.substring(0, i).trim() : input;
11388 }
11389 _commentStart(input) {
11390 let outerQuote = null;
11391 for (let i = 0; i < input.length - 1; i++) {
11392 const char = input.charCodeAt(i);
11393 const nextChar = input.charCodeAt(i + 1);
11394 if (char === $SLASH && nextChar == $SLASH && outerQuote == null)
11395 return i;
11396 if (outerQuote === char) {
11397 outerQuote = null;
11398 }
11399 else if (outerQuote == null && isQuote(char)) {
11400 outerQuote = char;
11401 }
11402 }
11403 return null;
11404 }
11405 _checkNoInterpolation(input, location, { start, end }) {
11406 let startIndex = -1;
11407 let endIndex = -1;
11408 for (const charIndex of this._forEachUnquotedChar(input, 0)) {
11409 if (startIndex === -1) {
11410 if (input.startsWith(start)) {
11411 startIndex = charIndex;
11412 }
11413 }
11414 else {
11415 endIndex = this._getInterpolationEndIndex(input, end, charIndex);
11416 if (endIndex > -1) {
11417 break;
11418 }
11419 }
11420 }
11421 if (startIndex > -1 && endIndex > -1) {
11422 this._reportError(`Got interpolation (${start}${end}) where expression was expected`, input, `at column ${startIndex} in`, location);
11423 }
11424 }
11425 /**
11426 * Finds the index of the end of an interpolation expression
11427 * while ignoring comments and quoted content.
11428 */
11429 _getInterpolationEndIndex(input, expressionEnd, start) {
11430 for (const charIndex of this._forEachUnquotedChar(input, start)) {
11431 if (input.startsWith(expressionEnd, charIndex)) {
11432 return charIndex;
11433 }
11434 // Nothing else in the expression matters after we've
11435 // hit a comment so look directly for the end token.
11436 if (input.startsWith('//', charIndex)) {
11437 return input.indexOf(expressionEnd, charIndex);
11438 }
11439 }
11440 return -1;
11441 }
11442 /**
11443 * Generator used to iterate over the character indexes of a string that are outside of quotes.
11444 * @param input String to loop through.
11445 * @param start Index within the string at which to start.
11446 */
11447 *_forEachUnquotedChar(input, start) {
11448 let currentQuote = null;
11449 let escapeCount = 0;
11450 for (let i = start; i < input.length; i++) {
11451 const char = input[i];
11452 // Skip the characters inside quotes. Note that we only care about the outer-most
11453 // quotes matching up and we need to account for escape characters.
11454 if (isQuote(input.charCodeAt(i)) && (currentQuote === null || currentQuote === char) &&
11455 escapeCount % 2 === 0) {
11456 currentQuote = currentQuote === null ? char : null;
11457 }
11458 else if (currentQuote === null) {
11459 yield i;
11460 }
11461 escapeCount = char === '\\' ? escapeCount + 1 : 0;
11462 }
11463 }
11464 }
11465 class IvyParser extends Parser$1 {
11466 constructor() {
11467 super(...arguments);
11468 this.simpleExpressionChecker = IvySimpleExpressionChecker;
11469 }
11470 }
11471 /** Describes a stateful context an expression parser is in. */
11472 var ParseContextFlags;
11473 (function (ParseContextFlags) {
11474 ParseContextFlags[ParseContextFlags["None"] = 0] = "None";
11475 /**
11476 * A Writable context is one in which a value may be written to an lvalue.
11477 * For example, after we see a property access, we may expect a write to the
11478 * property via the "=" operator.
11479 * prop
11480 * ^ possible "=" after
11481 */
11482 ParseContextFlags[ParseContextFlags["Writable"] = 1] = "Writable";
11483 })(ParseContextFlags || (ParseContextFlags = {}));
11484 class _ParseAST {
11485 constructor(input, location, absoluteOffset, tokens, inputLength, parseAction, errors, offset) {
11486 this.input = input;
11487 this.location = location;
11488 this.absoluteOffset = absoluteOffset;
11489 this.tokens = tokens;
11490 this.inputLength = inputLength;
11491 this.parseAction = parseAction;
11492 this.errors = errors;
11493 this.offset = offset;
11494 this.rparensExpected = 0;
11495 this.rbracketsExpected = 0;
11496 this.rbracesExpected = 0;
11497 this.context = ParseContextFlags.None;
11498 // Cache of expression start and input indeces to the absolute source span they map to, used to
11499 // prevent creating superfluous source spans in `sourceSpan`.
11500 // A serial of the expression start and input index is used for mapping because both are stateful
11501 // and may change for subsequent expressions visited by the parser.
11502 this.sourceSpanCache = new Map();
11503 this.index = 0;
11504 }
11505 peek(offset) {
11506 const i = this.index + offset;
11507 return i < this.tokens.length ? this.tokens[i] : EOF;
11508 }
11509 get next() {
11510 return this.peek(0);
11511 }
11512 /** Whether all the parser input has been processed. */
11513 get atEOF() {
11514 return this.index >= this.tokens.length;
11515 }
11516 /**
11517 * Index of the next token to be processed, or the end of the last token if all have been
11518 * processed.
11519 */
11520 get inputIndex() {
11521 return this.atEOF ? this.currentEndIndex : this.next.index + this.offset;
11522 }
11523 /**
11524 * End index of the last processed token, or the start of the first token if none have been
11525 * processed.
11526 */
11527 get currentEndIndex() {
11528 if (this.index > 0) {
11529 const curToken = this.peek(-1);
11530 return curToken.end + this.offset;
11531 }
11532 // No tokens have been processed yet; return the next token's start or the length of the input
11533 // if there is no token.
11534 if (this.tokens.length === 0) {
11535 return this.inputLength + this.offset;
11536 }
11537 return this.next.index + this.offset;
11538 }
11539 /**
11540 * Returns the absolute offset of the start of the current token.
11541 */
11542 get currentAbsoluteOffset() {
11543 return this.absoluteOffset + this.inputIndex;
11544 }
11545 /**
11546 * Retrieve a `ParseSpan` from `start` to the current position (or to `artificialEndIndex` if
11547 * provided).
11548 *
11549 * @param start Position from which the `ParseSpan` will start.
11550 * @param artificialEndIndex Optional ending index to be used if provided (and if greater than the
11551 * natural ending index)
11552 */
11553 span(start, artificialEndIndex) {
11554 let endIndex = this.currentEndIndex;
11555 if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) {
11556 endIndex = artificialEndIndex;
11557 }
11558 return new ParseSpan(start, endIndex);
11559 }
11560 sourceSpan(start, artificialEndIndex) {
11561 const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`;
11562 if (!this.sourceSpanCache.has(serial)) {
11563 this.sourceSpanCache.set(serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset));
11564 }
11565 return this.sourceSpanCache.get(serial);
11566 }
11567 advance() {
11568 this.index++;
11569 }
11570 /**
11571 * Executes a callback in the provided context.
11572 */
11573 withContext(context, cb) {
11574 this.context |= context;
11575 const ret = cb();
11576 this.context ^= context;
11577 return ret;
11578 }
11579 consumeOptionalCharacter(code) {
11580 if (this.next.isCharacter(code)) {
11581 this.advance();
11582 return true;
11583 }
11584 else {
11585 return false;
11586 }
11587 }
11588 peekKeywordLet() {
11589 return this.next.isKeywordLet();
11590 }
11591 peekKeywordAs() {
11592 return this.next.isKeywordAs();
11593 }
11594 /**
11595 * Consumes an expected character, otherwise emits an error about the missing expected character
11596 * and skips over the token stream until reaching a recoverable point.
11597 *
11598 * See `this.error` and `this.skip` for more details.
11599 */
11600 expectCharacter(code) {
11601 if (this.consumeOptionalCharacter(code))
11602 return;
11603 this.error(`Missing expected ${String.fromCharCode(code)}`);
11604 }
11605 consumeOptionalOperator(op) {
11606 if (this.next.isOperator(op)) {
11607 this.advance();
11608 return true;
11609 }
11610 else {
11611 return false;
11612 }
11613 }
11614 expectOperator(operator) {
11615 if (this.consumeOptionalOperator(operator))
11616 return;
11617 this.error(`Missing expected operator ${operator}`);
11618 }
11619 prettyPrintToken(tok) {
11620 return tok === EOF ? 'end of input' : `token ${tok}`;
11621 }
11622 expectIdentifierOrKeyword() {
11623 const n = this.next;
11624 if (!n.isIdentifier() && !n.isKeyword()) {
11625 this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`);
11626 return null;
11627 }
11628 this.advance();
11629 return n.toString();
11630 }
11631 expectIdentifierOrKeywordOrString() {
11632 const n = this.next;
11633 if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
11634 this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier, keyword, or string`);
11635 return '';
11636 }
11637 this.advance();
11638 return n.toString();
11639 }
11640 parseChain() {
11641 const exprs = [];
11642 const start = this.inputIndex;
11643 while (this.index < this.tokens.length) {
11644 const expr = this.parsePipe();
11645 exprs.push(expr);
11646 if (this.consumeOptionalCharacter($SEMICOLON)) {
11647 if (!this.parseAction) {
11648 this.error('Binding expression cannot contain chained expression');
11649 }
11650 while (this.consumeOptionalCharacter($SEMICOLON)) {
11651 } // read all semicolons
11652 }
11653 else if (this.index < this.tokens.length) {
11654 this.error(`Unexpected token '${this.next}'`);
11655 }
11656 }
11657 if (exprs.length == 0) {
11658 // We have no expressions so create an empty expression that spans the entire input length
11659 const artificialStart = this.offset;
11660 const artificialEnd = this.offset + this.inputLength;
11661 return new EmptyExpr(this.span(artificialStart, artificialEnd), this.sourceSpan(artificialStart, artificialEnd));
11662 }
11663 if (exprs.length == 1)
11664 return exprs[0];
11665 return new Chain(this.span(start), this.sourceSpan(start), exprs);
11666 }
11667 parsePipe() {
11668 const start = this.inputIndex;
11669 let result = this.parseExpression();
11670 if (this.consumeOptionalOperator('|')) {
11671 if (this.parseAction) {
11672 this.error('Cannot have a pipe in an action expression');
11673 }
11674 do {
11675 const nameStart = this.inputIndex;
11676 let nameId = this.expectIdentifierOrKeyword();
11677 let nameSpan;
11678 let fullSpanEnd = undefined;
11679 if (nameId !== null) {
11680 nameSpan = this.sourceSpan(nameStart);
11681 }
11682 else {
11683 // No valid identifier was found, so we'll assume an empty pipe name ('').
11684 nameId = '';
11685 // However, there may have been whitespace present between the pipe character and the next
11686 // token in the sequence (or the end of input). We want to track this whitespace so that
11687 // the `BindingPipe` we produce covers not just the pipe character, but any trailing
11688 // whitespace beyond it. Another way of thinking about this is that the zero-length name
11689 // is assumed to be at the end of any whitespace beyond the pipe character.
11690 //
11691 // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the
11692 // beginning of the next token, or until the end of input if the next token is EOF.
11693 fullSpanEnd = this.next.index !== -1 ? this.next.index : this.inputLength + this.offset;
11694 // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace
11695 // beyond the pipe character.
11696 nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset);
11697 }
11698 const args = [];
11699 while (this.consumeOptionalCharacter($COLON)) {
11700 args.push(this.parseExpression());
11701 // If there are additional expressions beyond the name, then the artificial end for the
11702 // name is no longer relevant.
11703 }
11704 result = new BindingPipe(this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, nameSpan);
11705 } while (this.consumeOptionalOperator('|'));
11706 }
11707 return result;
11708 }
11709 parseExpression() {
11710 return this.parseConditional();
11711 }
11712 parseConditional() {
11713 const start = this.inputIndex;
11714 const result = this.parseLogicalOr();
11715 if (this.consumeOptionalOperator('?')) {
11716 const yes = this.parsePipe();
11717 let no;
11718 if (!this.consumeOptionalCharacter($COLON)) {
11719 const end = this.inputIndex;
11720 const expression = this.input.substring(start, end);
11721 this.error(`Conditional expression ${expression} requires all 3 expressions`);
11722 no = new EmptyExpr(this.span(start), this.sourceSpan(start));
11723 }
11724 else {
11725 no = this.parsePipe();
11726 }
11727 return new Conditional(this.span(start), this.sourceSpan(start), result, yes, no);
11728 }
11729 else {
11730 return result;
11731 }
11732 }
11733 parseLogicalOr() {
11734 // '||'
11735 const start = this.inputIndex;
11736 let result = this.parseLogicalAnd();
11737 while (this.consumeOptionalOperator('||')) {
11738 const right = this.parseLogicalAnd();
11739 result = new Binary(this.span(start), this.sourceSpan(start), '||', result, right);
11740 }
11741 return result;
11742 }
11743 parseLogicalAnd() {
11744 // '&&'
11745 const start = this.inputIndex;
11746 let result = this.parseEquality();
11747 while (this.consumeOptionalOperator('&&')) {
11748 const right = this.parseEquality();
11749 result = new Binary(this.span(start), this.sourceSpan(start), '&&', result, right);
11750 }
11751 return result;
11752 }
11753 parseEquality() {
11754 // '==','!=','===','!=='
11755 const start = this.inputIndex;
11756 let result = this.parseRelational();
11757 while (this.next.type == TokenType$1.Operator) {
11758 const operator = this.next.strValue;
11759 switch (operator) {
11760 case '==':
11761 case '===':
11762 case '!=':
11763 case '!==':
11764 this.advance();
11765 const right = this.parseRelational();
11766 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
11767 continue;
11768 }
11769 break;
11770 }
11771 return result;
11772 }
11773 parseRelational() {
11774 // '<', '>', '<=', '>='
11775 const start = this.inputIndex;
11776 let result = this.parseAdditive();
11777 while (this.next.type == TokenType$1.Operator) {
11778 const operator = this.next.strValue;
11779 switch (operator) {
11780 case '<':
11781 case '>':
11782 case '<=':
11783 case '>=':
11784 this.advance();
11785 const right = this.parseAdditive();
11786 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
11787 continue;
11788 }
11789 break;
11790 }
11791 return result;
11792 }
11793 parseAdditive() {
11794 // '+', '-'
11795 const start = this.inputIndex;
11796 let result = this.parseMultiplicative();
11797 while (this.next.type == TokenType$1.Operator) {
11798 const operator = this.next.strValue;
11799 switch (operator) {
11800 case '+':
11801 case '-':
11802 this.advance();
11803 let right = this.parseMultiplicative();
11804 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
11805 continue;
11806 }
11807 break;
11808 }
11809 return result;
11810 }
11811 parseMultiplicative() {
11812 // '*', '%', '/'
11813 const start = this.inputIndex;
11814 let result = this.parsePrefix();
11815 while (this.next.type == TokenType$1.Operator) {
11816 const operator = this.next.strValue;
11817 switch (operator) {
11818 case '*':
11819 case '%':
11820 case '/':
11821 this.advance();
11822 let right = this.parsePrefix();
11823 result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
11824 continue;
11825 }
11826 break;
11827 }
11828 return result;
11829 }
11830 parsePrefix() {
11831 if (this.next.type == TokenType$1.Operator) {
11832 const start = this.inputIndex;
11833 const operator = this.next.strValue;
11834 let result;
11835 switch (operator) {
11836 case '+':
11837 this.advance();
11838 result = this.parsePrefix();
11839 return Unary.createPlus(this.span(start), this.sourceSpan(start), result);
11840 case '-':
11841 this.advance();
11842 result = this.parsePrefix();
11843 return Unary.createMinus(this.span(start), this.sourceSpan(start), result);
11844 case '!':
11845 this.advance();
11846 result = this.parsePrefix();
11847 return new PrefixNot(this.span(start), this.sourceSpan(start), result);
11848 }
11849 }
11850 return this.parseCallChain();
11851 }
11852 parseCallChain() {
11853 const start = this.inputIndex;
11854 let result = this.parsePrimary();
11855 while (true) {
11856 if (this.consumeOptionalCharacter($PERIOD)) {
11857 result = this.parseAccessMemberOrMethodCall(result, start, false);
11858 }
11859 else if (this.consumeOptionalOperator('?.')) {
11860 result = this.parseAccessMemberOrMethodCall(result, start, true);
11861 }
11862 else if (this.consumeOptionalCharacter($LBRACKET)) {
11863 this.withContext(ParseContextFlags.Writable, () => {
11864 this.rbracketsExpected++;
11865 const key = this.parsePipe();
11866 if (key instanceof EmptyExpr) {
11867 this.error(`Key access cannot be empty`);
11868 }
11869 this.rbracketsExpected--;
11870 this.expectCharacter($RBRACKET);
11871 if (this.consumeOptionalOperator('=')) {
11872 const value = this.parseConditional();
11873 result = new KeyedWrite(this.span(start), this.sourceSpan(start), result, key, value);
11874 }
11875 else {
11876 result = new KeyedRead(this.span(start), this.sourceSpan(start), result, key);
11877 }
11878 });
11879 }
11880 else if (this.consumeOptionalCharacter($LPAREN)) {
11881 this.rparensExpected++;
11882 const args = this.parseCallArguments();
11883 this.rparensExpected--;
11884 this.expectCharacter($RPAREN);
11885 result = new FunctionCall(this.span(start), this.sourceSpan(start), result, args);
11886 }
11887 else if (this.consumeOptionalOperator('!')) {
11888 result = new NonNullAssert(this.span(start), this.sourceSpan(start), result);
11889 }
11890 else {
11891 return result;
11892 }
11893 }
11894 }
11895 parsePrimary() {
11896 const start = this.inputIndex;
11897 if (this.consumeOptionalCharacter($LPAREN)) {
11898 this.rparensExpected++;
11899 const result = this.parsePipe();
11900 this.rparensExpected--;
11901 this.expectCharacter($RPAREN);
11902 return result;
11903 }
11904 else if (this.next.isKeywordNull()) {
11905 this.advance();
11906 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), null);
11907 }
11908 else if (this.next.isKeywordUndefined()) {
11909 this.advance();
11910 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), void 0);
11911 }
11912 else if (this.next.isKeywordTrue()) {
11913 this.advance();
11914 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), true);
11915 }
11916 else if (this.next.isKeywordFalse()) {
11917 this.advance();
11918 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), false);
11919 }
11920 else if (this.next.isKeywordThis()) {
11921 this.advance();
11922 return new ThisReceiver(this.span(start), this.sourceSpan(start));
11923 }
11924 else if (this.consumeOptionalCharacter($LBRACKET)) {
11925 this.rbracketsExpected++;
11926 const elements = this.parseExpressionList($RBRACKET);
11927 this.rbracketsExpected--;
11928 this.expectCharacter($RBRACKET);
11929 return new LiteralArray(this.span(start), this.sourceSpan(start), elements);
11930 }
11931 else if (this.next.isCharacter($LBRACE)) {
11932 return this.parseLiteralMap();
11933 }
11934 else if (this.next.isIdentifier()) {
11935 return this.parseAccessMemberOrMethodCall(new ImplicitReceiver(this.span(start), this.sourceSpan(start)), start, false);
11936 }
11937 else if (this.next.isNumber()) {
11938 const value = this.next.toNumber();
11939 this.advance();
11940 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), value);
11941 }
11942 else if (this.next.isString()) {
11943 const literalValue = this.next.toString();
11944 this.advance();
11945 return new LiteralPrimitive(this.span(start), this.sourceSpan(start), literalValue);
11946 }
11947 else if (this.index >= this.tokens.length) {
11948 this.error(`Unexpected end of expression: ${this.input}`);
11949 return new EmptyExpr(this.span(start), this.sourceSpan(start));
11950 }
11951 else {
11952 this.error(`Unexpected token ${this.next}`);
11953 return new EmptyExpr(this.span(start), this.sourceSpan(start));
11954 }
11955 }
11956 parseExpressionList(terminator) {
11957 const result = [];
11958 do {
11959 if (!this.next.isCharacter(terminator)) {
11960 result.push(this.parsePipe());
11961 }
11962 else {
11963 break;
11964 }
11965 } while (this.consumeOptionalCharacter($COMMA));
11966 return result;
11967 }
11968 parseLiteralMap() {
11969 const keys = [];
11970 const values = [];
11971 const start = this.inputIndex;
11972 this.expectCharacter($LBRACE);
11973 if (!this.consumeOptionalCharacter($RBRACE)) {
11974 this.rbracesExpected++;
11975 do {
11976 const quoted = this.next.isString();
11977 const key = this.expectIdentifierOrKeywordOrString();
11978 keys.push({ key, quoted });
11979 this.expectCharacter($COLON);
11980 values.push(this.parsePipe());
11981 } while (this.consumeOptionalCharacter($COMMA));
11982 this.rbracesExpected--;
11983 this.expectCharacter($RBRACE);
11984 }
11985 return new LiteralMap(this.span(start), this.sourceSpan(start), keys, values);
11986 }
11987 parseAccessMemberOrMethodCall(receiver, start, isSafe = false) {
11988 const nameStart = this.inputIndex;
11989 const id = this.withContext(ParseContextFlags.Writable, () => {
11990 var _a;
11991 const id = (_a = this.expectIdentifierOrKeyword()) !== null && _a !== void 0 ? _a : '';
11992 if (id.length === 0) {
11993 this.error(`Expected identifier for property access`, receiver.span.end);
11994 }
11995 return id;
11996 });
11997 const nameSpan = this.sourceSpan(nameStart);
11998 if (this.consumeOptionalCharacter($LPAREN)) {
11999 this.rparensExpected++;
12000 const args = this.parseCallArguments();
12001 this.expectCharacter($RPAREN);
12002 this.rparensExpected--;
12003 const span = this.span(start);
12004 const sourceSpan = this.sourceSpan(start);
12005 return isSafe ? new SafeMethodCall(span, sourceSpan, nameSpan, receiver, id, args) :
12006 new MethodCall(span, sourceSpan, nameSpan, receiver, id, args);
12007 }
12008 else {
12009 if (isSafe) {
12010 if (this.consumeOptionalOperator('=')) {
12011 this.error('The \'?.\' operator cannot be used in the assignment');
12012 return new EmptyExpr(this.span(start), this.sourceSpan(start));
12013 }
12014 else {
12015 return new SafePropertyRead(this.span(start), this.sourceSpan(start), nameSpan, receiver, id);
12016 }
12017 }
12018 else {
12019 if (this.consumeOptionalOperator('=')) {
12020 if (!this.parseAction) {
12021 this.error('Bindings cannot contain assignments');
12022 return new EmptyExpr(this.span(start), this.sourceSpan(start));
12023 }
12024 const value = this.parseConditional();
12025 return new PropertyWrite(this.span(start), this.sourceSpan(start), nameSpan, receiver, id, value);
12026 }
12027 else {
12028 return new PropertyRead(this.span(start), this.sourceSpan(start), nameSpan, receiver, id);
12029 }
12030 }
12031 }
12032 }
12033 parseCallArguments() {
12034 if (this.next.isCharacter($RPAREN))
12035 return [];
12036 const positionals = [];
12037 do {
12038 positionals.push(this.parsePipe());
12039 } while (this.consumeOptionalCharacter($COMMA));
12040 return positionals;
12041 }
12042 /**
12043 * Parses an identifier, a keyword, a string with an optional `-` in between,
12044 * and returns the string along with its absolute source span.
12045 */
12046 expectTemplateBindingKey() {
12047 let result = '';
12048 let operatorFound = false;
12049 const start = this.currentAbsoluteOffset;
12050 do {
12051 result += this.expectIdentifierOrKeywordOrString();
12052 operatorFound = this.consumeOptionalOperator('-');
12053 if (operatorFound) {
12054 result += '-';
12055 }
12056 } while (operatorFound);
12057 return {
12058 source: result,
12059 span: new AbsoluteSourceSpan(start, start + result.length),
12060 };
12061 }
12062 /**
12063 * Parse microsyntax template expression and return a list of bindings or
12064 * parsing errors in case the given expression is invalid.
12065 *
12066 * For example,
12067 * ```
12068 * <div *ngFor="let item of items; index as i; trackBy: func">
12069 * ```
12070 * contains five bindings:
12071 * 1. ngFor -> null
12072 * 2. item -> NgForOfContext.$implicit
12073 * 3. ngForOf -> items
12074 * 4. i -> NgForOfContext.index
12075 * 5. ngForTrackBy -> func
12076 *
12077 * For a full description of the microsyntax grammar, see
12078 * https://gist.github.com/mhevery/d3530294cff2e4a1b3fe15ff75d08855
12079 *
12080 * @param templateKey name of the microsyntax directive, like ngIf, ngFor,
12081 * without the *, along with its absolute span.
12082 */
12083 parseTemplateBindings(templateKey) {
12084 const bindings = [];
12085 // The first binding is for the template key itself
12086 // In *ngFor="let item of items", key = "ngFor", value = null
12087 // In *ngIf="cond | pipe", key = "ngIf", value = "cond | pipe"
12088 bindings.push(...this.parseDirectiveKeywordBindings(templateKey));
12089 while (this.index < this.tokens.length) {
12090 // If it starts with 'let', then this must be variable declaration
12091 const letBinding = this.parseLetBinding();
12092 if (letBinding) {
12093 bindings.push(letBinding);
12094 }
12095 else {
12096 // Two possible cases here, either `value "as" key` or
12097 // "directive-keyword expression". We don't know which case, but both
12098 // "value" and "directive-keyword" are template binding key, so consume
12099 // the key first.
12100 const key = this.expectTemplateBindingKey();
12101 // Peek at the next token, if it is "as" then this must be variable
12102 // declaration.
12103 const binding = this.parseAsBinding(key);
12104 if (binding) {
12105 bindings.push(binding);
12106 }
12107 else {
12108 // Otherwise the key must be a directive keyword, like "of". Transform
12109 // the key to actual key. Eg. of -> ngForOf, trackBy -> ngForTrackBy
12110 key.source =
12111 templateKey.source + key.source.charAt(0).toUpperCase() + key.source.substring(1);
12112 bindings.push(...this.parseDirectiveKeywordBindings(key));
12113 }
12114 }
12115 this.consumeStatementTerminator();
12116 }
12117 return new TemplateBindingParseResult(bindings, [] /* warnings */, this.errors);
12118 }
12119 /**
12120 * Parse a directive keyword, followed by a mandatory expression.
12121 * For example, "of items", "trackBy: func".
12122 * The bindings are: ngForOf -> items, ngForTrackBy -> func
12123 * There could be an optional "as" binding that follows the expression.
12124 * For example,
12125 * ```
12126 * *ngFor="let item of items | slice:0:1 as collection".
12127 * ^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
12128 * keyword bound target optional 'as' binding
12129 * ```
12130 *
12131 * @param key binding key, for example, ngFor, ngIf, ngForOf, along with its
12132 * absolute span.
12133 */
12134 parseDirectiveKeywordBindings(key) {
12135 const bindings = [];
12136 this.consumeOptionalCharacter($COLON); // trackBy: trackByFunction
12137 const value = this.getDirectiveBoundTarget();
12138 let spanEnd = this.currentAbsoluteOffset;
12139 // The binding could optionally be followed by "as". For example,
12140 // *ngIf="cond | pipe as x". In this case, the key in the "as" binding
12141 // is "x" and the value is the template key itself ("ngIf"). Note that the
12142 // 'key' in the current context now becomes the "value" in the next binding.
12143 const asBinding = this.parseAsBinding(key);
12144 if (!asBinding) {
12145 this.consumeStatementTerminator();
12146 spanEnd = this.currentAbsoluteOffset;
12147 }
12148 const sourceSpan = new AbsoluteSourceSpan(key.span.start, spanEnd);
12149 bindings.push(new ExpressionBinding(sourceSpan, key, value));
12150 if (asBinding) {
12151 bindings.push(asBinding);
12152 }
12153 return bindings;
12154 }
12155 /**
12156 * Return the expression AST for the bound target of a directive keyword
12157 * binding. For example,
12158 * ```
12159 * *ngIf="condition | pipe"
12160 * ^^^^^^^^^^^^^^^^ bound target for "ngIf"
12161 * *ngFor="let item of items"
12162 * ^^^^^ bound target for "ngForOf"
12163 * ```
12164 */
12165 getDirectiveBoundTarget() {
12166 if (this.next === EOF || this.peekKeywordAs() || this.peekKeywordLet()) {
12167 return null;
12168 }
12169 const ast = this.parsePipe(); // example: "condition | async"
12170 const { start, end } = ast.span;
12171 const value = this.input.substring(start, end);
12172 return new ASTWithSource(ast, value, this.location, this.absoluteOffset + start, this.errors);
12173 }
12174 /**
12175 * Return the binding for a variable declared using `as`. Note that the order
12176 * of the key-value pair in this declaration is reversed. For example,
12177 * ```
12178 * *ngFor="let item of items; index as i"
12179 * ^^^^^ ^
12180 * value key
12181 * ```
12182 *
12183 * @param value name of the value in the declaration, "ngIf" in the example
12184 * above, along with its absolute span.
12185 */
12186 parseAsBinding(value) {
12187 if (!this.peekKeywordAs()) {
12188 return null;
12189 }
12190 this.advance(); // consume the 'as' keyword
12191 const key = this.expectTemplateBindingKey();
12192 this.consumeStatementTerminator();
12193 const sourceSpan = new AbsoluteSourceSpan(value.span.start, this.currentAbsoluteOffset);
12194 return new VariableBinding(sourceSpan, key, value);
12195 }
12196 /**
12197 * Return the binding for a variable declared using `let`. For example,
12198 * ```
12199 * *ngFor="let item of items; let i=index;"
12200 * ^^^^^^^^ ^^^^^^^^^^^
12201 * ```
12202 * In the first binding, `item` is bound to `NgForOfContext.$implicit`.
12203 * In the second binding, `i` is bound to `NgForOfContext.index`.
12204 */
12205 parseLetBinding() {
12206 if (!this.peekKeywordLet()) {
12207 return null;
12208 }
12209 const spanStart = this.currentAbsoluteOffset;
12210 this.advance(); // consume the 'let' keyword
12211 const key = this.expectTemplateBindingKey();
12212 let value = null;
12213 if (this.consumeOptionalOperator('=')) {
12214 value = this.expectTemplateBindingKey();
12215 }
12216 this.consumeStatementTerminator();
12217 const sourceSpan = new AbsoluteSourceSpan(spanStart, this.currentAbsoluteOffset);
12218 return new VariableBinding(sourceSpan, key, value);
12219 }
12220 /**
12221 * Consume the optional statement terminator: semicolon or comma.
12222 */
12223 consumeStatementTerminator() {
12224 this.consumeOptionalCharacter($SEMICOLON) || this.consumeOptionalCharacter($COMMA);
12225 }
12226 /**
12227 * Records an error and skips over the token stream until reaching a recoverable point. See
12228 * `this.skip` for more details on token skipping.
12229 */
12230 error(message, index = null) {
12231 this.errors.push(new ParserError(message, this.input, this.locationText(index), this.location));
12232 this.skip();
12233 }
12234 locationText(index = null) {
12235 if (index == null)
12236 index = this.index;
12237 return (index < this.tokens.length) ? `at column ${this.tokens[index].index + 1} in` :
12238 `at the end of the expression`;
12239 }
12240 /**
12241 * Error recovery should skip tokens until it encounters a recovery point.
12242 *
12243 * The following are treated as unconditional recovery points:
12244 * - end of input
12245 * - ';' (parseChain() is always the root production, and it expects a ';')
12246 * - '|' (since pipes may be chained and each pipe expression may be treated independently)
12247 *
12248 * The following are conditional recovery points:
12249 * - ')', '}', ']' if one of calling productions is expecting one of these symbols
12250 * - This allows skip() to recover from errors such as '(a.) + 1' allowing more of the AST to
12251 * be retained (it doesn't skip any tokens as the ')' is retained because of the '(' begins
12252 * an '(' <expr> ')' production).
12253 * The recovery points of grouping symbols must be conditional as they must be skipped if
12254 * none of the calling productions are not expecting the closing token else we will never
12255 * make progress in the case of an extraneous group closing symbol (such as a stray ')').
12256 * That is, we skip a closing symbol if we are not in a grouping production.
12257 * - '=' in a `Writable` context
12258 * - In this context, we are able to recover after seeing the `=` operator, which
12259 * signals the presence of an independent rvalue expression following the `=` operator.
12260 *
12261 * If a production expects one of these token it increments the corresponding nesting count,
12262 * and then decrements it just prior to checking if the token is in the input.
12263 */
12264 skip() {
12265 let n = this.next;
12266 while (this.index < this.tokens.length && !n.isCharacter($SEMICOLON) &&
12267 !n.isOperator('|') && (this.rparensExpected <= 0 || !n.isCharacter($RPAREN)) &&
12268 (this.rbracesExpected <= 0 || !n.isCharacter($RBRACE)) &&
12269 (this.rbracketsExpected <= 0 || !n.isCharacter($RBRACKET)) &&
12270 (!(this.context & ParseContextFlags.Writable) || !n.isOperator('='))) {
12271 if (this.next.isError()) {
12272 this.errors.push(new ParserError(this.next.toString(), this.input, this.locationText(), this.location));
12273 }
12274 this.advance();
12275 n = this.next;
12276 }
12277 }
12278 }
12279 class SimpleExpressionChecker {
12280 constructor() {
12281 this.errors = [];
12282 }
12283 visitImplicitReceiver(ast, context) { }
12284 visitThisReceiver(ast, context) { }
12285 visitInterpolation(ast, context) { }
12286 visitLiteralPrimitive(ast, context) { }
12287 visitPropertyRead(ast, context) { }
12288 visitPropertyWrite(ast, context) { }
12289 visitSafePropertyRead(ast, context) { }
12290 visitMethodCall(ast, context) { }
12291 visitSafeMethodCall(ast, context) { }
12292 visitFunctionCall(ast, context) { }
12293 visitLiteralArray(ast, context) {
12294 this.visitAll(ast.expressions, context);
12295 }
12296 visitLiteralMap(ast, context) {
12297 this.visitAll(ast.values, context);
12298 }
12299 visitUnary(ast, context) { }
12300 visitBinary(ast, context) { }
12301 visitPrefixNot(ast, context) { }
12302 visitNonNullAssert(ast, context) { }
12303 visitConditional(ast, context) { }
12304 visitPipe(ast, context) {
12305 this.errors.push('pipes');
12306 }
12307 visitKeyedRead(ast, context) { }
12308 visitKeyedWrite(ast, context) { }
12309 visitAll(asts, context) {
12310 return asts.map(node => node.visit(this, context));
12311 }
12312 visitChain(ast, context) { }
12313 visitQuote(ast, context) { }
12314 }
12315 /**
12316 * This class implements SimpleExpressionChecker used in View Engine and performs more strict checks
12317 * to make sure host bindings do not contain pipes. In View Engine, having pipes in host bindings is
12318 * not supported as well, but in some cases (like `!(value | async)`) the error is not triggered at
12319 * compile time. In order to preserve View Engine behavior, more strict checks are introduced for
12320 * Ivy mode only.
12321 */
12322 class IvySimpleExpressionChecker extends RecursiveAstVisitor {
12323 constructor() {
12324 super(...arguments);
12325 this.errors = [];
12326 }
12327 visitPipe() {
12328 this.errors.push('pipes');
12329 }
12330 }
12331
12332 /**
12333 * @license
12334 * Copyright Google LLC All Rights Reserved.
12335 *
12336 * Use of this source code is governed by an MIT-style license that can be
12337 * found in the LICENSE file at https://angular.io/license
12338 */
12339 // =================================================================================================
12340 // =================================================================================================
12341 // =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
12342 // =================================================================================================
12343 // =================================================================================================
12344 //
12345 // DO NOT EDIT THIS LIST OF SECURITY SENSITIVE PROPERTIES WITHOUT A SECURITY REVIEW!
12346 // Reach out to mprobst for details.
12347 //
12348 // =================================================================================================
12349 /** Map from tagName|propertyName to SecurityContext. Properties applying to all tags use '*'. */
12350 let _SECURITY_SCHEMA;
12351 function SECURITY_SCHEMA() {
12352 if (!_SECURITY_SCHEMA) {
12353 _SECURITY_SCHEMA = {};
12354 // Case is insignificant below, all element and attribute names are lower-cased for lookup.
12355 registerContext(SecurityContext.HTML, [
12356 'iframe|srcdoc',
12357 '*|innerHTML',
12358 '*|outerHTML',
12359 ]);
12360 registerContext(SecurityContext.STYLE, ['*|style']);
12361 // NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them.
12362 registerContext(SecurityContext.URL, [
12363 '*|formAction', 'area|href', 'area|ping', 'audio|src', 'a|href',
12364 'a|ping', 'blockquote|cite', 'body|background', 'del|cite', 'form|action',
12365 'img|src', 'img|srcset', 'input|src', 'ins|cite', 'q|cite',
12366 'source|src', 'source|srcset', 'track|src', 'video|poster', 'video|src',
12367 ]);
12368 registerContext(SecurityContext.RESOURCE_URL, [
12369 'applet|code',
12370 'applet|codebase',
12371 'base|href',
12372 'embed|src',
12373 'frame|src',
12374 'head|profile',
12375 'html|manifest',
12376 'iframe|src',
12377 'link|href',
12378 'media|src',
12379 'object|codebase',
12380 'object|data',
12381 'script|src',
12382 ]);
12383 }
12384 return _SECURITY_SCHEMA;
12385 }
12386 function registerContext(ctx, specs) {
12387 for (const spec of specs)
12388 _SECURITY_SCHEMA[spec.toLowerCase()] = ctx;
12389 }
12390
12391 /**
12392 * @license
12393 * Copyright Google LLC All Rights Reserved.
12394 *
12395 * Use of this source code is governed by an MIT-style license that can be
12396 * found in the LICENSE file at https://angular.io/license
12397 */
12398 class ElementSchemaRegistry {
12399 }
12400
12401 /**
12402 * @license
12403 * Copyright Google LLC All Rights Reserved.
12404 *
12405 * Use of this source code is governed by an MIT-style license that can be
12406 * found in the LICENSE file at https://angular.io/license
12407 */
12408 const BOOLEAN = 'boolean';
12409 const NUMBER = 'number';
12410 const STRING = 'string';
12411 const OBJECT = 'object';
12412 /**
12413 * This array represents the DOM schema. It encodes inheritance, properties, and events.
12414 *
12415 * ## Overview
12416 *
12417 * Each line represents one kind of element. The `element_inheritance` and properties are joined
12418 * using `element_inheritance|properties` syntax.
12419 *
12420 * ## Element Inheritance
12421 *
12422 * The `element_inheritance` can be further subdivided as `element1,element2,...^parentElement`.
12423 * Here the individual elements are separated by `,` (commas). Every element in the list
12424 * has identical properties.
12425 *
12426 * An `element` may inherit additional properties from `parentElement` If no `^parentElement` is
12427 * specified then `""` (blank) element is assumed.
12428 *
12429 * NOTE: The blank element inherits from root `[Element]` element, the super element of all
12430 * elements.
12431 *
12432 * NOTE an element prefix such as `:svg:` has no special meaning to the schema.
12433 *
12434 * ## Properties
12435 *
12436 * Each element has a set of properties separated by `,` (commas). Each property can be prefixed
12437 * by a special character designating its type:
12438 *
12439 * - (no prefix): property is a string.
12440 * - `*`: property represents an event.
12441 * - `!`: property is a boolean.
12442 * - `#`: property is a number.
12443 * - `%`: property is an object.
12444 *
12445 * ## Query
12446 *
12447 * The class creates an internal squas representation which allows to easily answer the query of
12448 * if a given property exist on a given element.
12449 *
12450 * NOTE: We don't yet support querying for types or events.
12451 * NOTE: This schema is auto extracted from `schema_extractor.ts` located in the test folder,
12452 * see dom_element_schema_registry_spec.ts
12453 */
12454 // =================================================================================================
12455 // =================================================================================================
12456 // =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
12457 // =================================================================================================
12458 // =================================================================================================
12459 //
12460 // DO NOT EDIT THIS DOM SCHEMA WITHOUT A SECURITY REVIEW!
12461 //
12462 // Newly added properties must be security reviewed and assigned an appropriate SecurityContext in
12463 // dom_security_schema.ts. Reach out to mprobst & rjamet for details.
12464 //
12465 // =================================================================================================
12466 const SCHEMA = [
12467 '[Element]|textContent,%classList,className,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*copy,*cut,*paste,*search,*selectstart,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerHTML,#scrollLeft,#scrollTop,slot' +
12468 /* added manually to avoid breaking changes */
12469 ',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored',
12470 '[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',
12471 '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',
12472 'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,src,%srcObject,#volume',
12473 ':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',
12474 ':svg:graphics^:svg:|',
12475 ':svg:animation^:svg:|*begin,*end,*repeat',
12476 ':svg:geometry^:svg:|',
12477 ':svg:componentTransferFunction^:svg:|',
12478 ':svg:gradient^:svg:|',
12479 ':svg:textContent^:svg:graphics|',
12480 ':svg:textPositioning^:svg:textContent|',
12481 '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',
12482 'area^[HTMLElement]|alt,coords,download,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,referrerPolicy,rel,search,shape,target,username',
12483 'audio^media|',
12484 'br^[HTMLElement]|clear',
12485 'base^[HTMLElement]|href,target',
12486 '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',
12487 'button^[HTMLElement]|!autofocus,!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value',
12488 'canvas^[HTMLElement]|#height,#width',
12489 'content^[HTMLElement]|select',
12490 'dl^[HTMLElement]|!compact',
12491 'datalist^[HTMLElement]|',
12492 'details^[HTMLElement]|!open',
12493 'dialog^[HTMLElement]|!open,returnValue',
12494 'dir^[HTMLElement]|!compact',
12495 'div^[HTMLElement]|align',
12496 'embed^[HTMLElement]|align,height,name,src,type,width',
12497 'fieldset^[HTMLElement]|!disabled,name',
12498 'font^[HTMLElement]|color,face,size',
12499 'form^[HTMLElement]|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target',
12500 'frame^[HTMLElement]|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src',
12501 'frameset^[HTMLElement]|cols,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows',
12502 'hr^[HTMLElement]|align,color,!noShade,size,width',
12503 'head^[HTMLElement]|',
12504 'h1,h2,h3,h4,h5,h6^[HTMLElement]|align',
12505 'html^[HTMLElement]|version',
12506 'iframe^[HTMLElement]|align,!allowFullscreen,frameBorder,height,longDesc,marginHeight,marginWidth,name,referrerPolicy,%sandbox,scrolling,src,srcdoc,width',
12507 'img^[HTMLElement]|align,alt,border,%crossOrigin,#height,#hspace,!isMap,longDesc,lowsrc,name,referrerPolicy,sizes,src,srcset,useMap,#vspace,#width',
12508 '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',
12509 'li^[HTMLElement]|type,#value',
12510 'label^[HTMLElement]|htmlFor',
12511 'legend^[HTMLElement]|align',
12512 'link^[HTMLElement]|as,charset,%crossOrigin,!disabled,href,hreflang,integrity,media,referrerPolicy,rel,%relList,rev,%sizes,target,type',
12513 'map^[HTMLElement]|name',
12514 'marquee^[HTMLElement]|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width',
12515 'menu^[HTMLElement]|!compact',
12516 'meta^[HTMLElement]|content,httpEquiv,name,scheme',
12517 'meter^[HTMLElement]|#high,#low,#max,#min,#optimum,#value',
12518 'ins,del^[HTMLElement]|cite,dateTime',
12519 'ol^[HTMLElement]|!compact,!reversed,#start,type',
12520 'object^[HTMLElement]|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width',
12521 'optgroup^[HTMLElement]|!disabled,label',
12522 'option^[HTMLElement]|!defaultSelected,!disabled,label,!selected,text,value',
12523 'output^[HTMLElement]|defaultValue,%htmlFor,name,value',
12524 'p^[HTMLElement]|align',
12525 'param^[HTMLElement]|name,type,value,valueType',
12526 'picture^[HTMLElement]|',
12527 'pre^[HTMLElement]|#width',
12528 'progress^[HTMLElement]|#max,#value',
12529 'q,blockquote,cite^[HTMLElement]|',
12530 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,src,text,type',
12531 'select^[HTMLElement]|!autofocus,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value',
12532 'shadow^[HTMLElement]|',
12533 'slot^[HTMLElement]|name',
12534 'source^[HTMLElement]|media,sizes,src,srcset,type',
12535 'span^[HTMLElement]|',
12536 'style^[HTMLElement]|!disabled,media,type',
12537 'caption^[HTMLElement]|align',
12538 'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width',
12539 'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width',
12540 'table^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width',
12541 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign',
12542 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign',
12543 'template^[HTMLElement]|',
12544 'textarea^[HTMLElement]|autocapitalize,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap',
12545 'title^[HTMLElement]|text',
12546 'track^[HTMLElement]|!default,kind,label,src,srclang',
12547 'ul^[HTMLElement]|!compact,type',
12548 'unknown^[HTMLElement]|',
12549 'video^media|#height,poster,#width',
12550 ':svg:a^:svg:graphics|',
12551 ':svg:animate^:svg:animation|',
12552 ':svg:animateMotion^:svg:animation|',
12553 ':svg:animateTransform^:svg:animation|',
12554 ':svg:circle^:svg:geometry|',
12555 ':svg:clipPath^:svg:graphics|',
12556 ':svg:defs^:svg:graphics|',
12557 ':svg:desc^:svg:|',
12558 ':svg:discard^:svg:|',
12559 ':svg:ellipse^:svg:geometry|',
12560 ':svg:feBlend^:svg:|',
12561 ':svg:feColorMatrix^:svg:|',
12562 ':svg:feComponentTransfer^:svg:|',
12563 ':svg:feComposite^:svg:|',
12564 ':svg:feConvolveMatrix^:svg:|',
12565 ':svg:feDiffuseLighting^:svg:|',
12566 ':svg:feDisplacementMap^:svg:|',
12567 ':svg:feDistantLight^:svg:|',
12568 ':svg:feDropShadow^:svg:|',
12569 ':svg:feFlood^:svg:|',
12570 ':svg:feFuncA^:svg:componentTransferFunction|',
12571 ':svg:feFuncB^:svg:componentTransferFunction|',
12572 ':svg:feFuncG^:svg:componentTransferFunction|',
12573 ':svg:feFuncR^:svg:componentTransferFunction|',
12574 ':svg:feGaussianBlur^:svg:|',
12575 ':svg:feImage^:svg:|',
12576 ':svg:feMerge^:svg:|',
12577 ':svg:feMergeNode^:svg:|',
12578 ':svg:feMorphology^:svg:|',
12579 ':svg:feOffset^:svg:|',
12580 ':svg:fePointLight^:svg:|',
12581 ':svg:feSpecularLighting^:svg:|',
12582 ':svg:feSpotLight^:svg:|',
12583 ':svg:feTile^:svg:|',
12584 ':svg:feTurbulence^:svg:|',
12585 ':svg:filter^:svg:|',
12586 ':svg:foreignObject^:svg:graphics|',
12587 ':svg:g^:svg:graphics|',
12588 ':svg:image^:svg:graphics|',
12589 ':svg:line^:svg:geometry|',
12590 ':svg:linearGradient^:svg:gradient|',
12591 ':svg:mpath^:svg:|',
12592 ':svg:marker^:svg:|',
12593 ':svg:mask^:svg:|',
12594 ':svg:metadata^:svg:|',
12595 ':svg:path^:svg:geometry|',
12596 ':svg:pattern^:svg:|',
12597 ':svg:polygon^:svg:geometry|',
12598 ':svg:polyline^:svg:geometry|',
12599 ':svg:radialGradient^:svg:gradient|',
12600 ':svg:rect^:svg:geometry|',
12601 ':svg:svg^:svg:graphics|#currentScale,#zoomAndPan',
12602 ':svg:script^:svg:|type',
12603 ':svg:set^:svg:animation|',
12604 ':svg:stop^:svg:|',
12605 ':svg:style^:svg:|!disabled,media,title,type',
12606 ':svg:switch^:svg:graphics|',
12607 ':svg:symbol^:svg:|',
12608 ':svg:tspan^:svg:textPositioning|',
12609 ':svg:text^:svg:textPositioning|',
12610 ':svg:textPath^:svg:textContent|',
12611 ':svg:title^:svg:|',
12612 ':svg:use^:svg:graphics|',
12613 ':svg:view^:svg:|#zoomAndPan',
12614 'data^[HTMLElement]|value',
12615 'keygen^[HTMLElement]|!autofocus,challenge,!disabled,form,keytype,name',
12616 'menuitem^[HTMLElement]|type,label,icon,!disabled,!checked,radiogroup,!default',
12617 'summary^[HTMLElement]|',
12618 'time^[HTMLElement]|dateTime',
12619 ':svg:cursor^:svg:|',
12620 ];
12621 const _ATTR_TO_PROP = {
12622 'class': 'className',
12623 'for': 'htmlFor',
12624 'formaction': 'formAction',
12625 'innerHtml': 'innerHTML',
12626 'readonly': 'readOnly',
12627 'tabindex': 'tabIndex',
12628 };
12629 // Invert _ATTR_TO_PROP.
12630 const _PROP_TO_ATTR = Object.keys(_ATTR_TO_PROP).reduce((inverted, attr) => {
12631 inverted[_ATTR_TO_PROP[attr]] = attr;
12632 return inverted;
12633 }, {});
12634 class DomElementSchemaRegistry extends ElementSchemaRegistry {
12635 constructor() {
12636 super();
12637 this._schema = {};
12638 SCHEMA.forEach(encodedType => {
12639 const type = {};
12640 const [strType, strProperties] = encodedType.split('|');
12641 const properties = strProperties.split(',');
12642 const [typeNames, superName] = strType.split('^');
12643 typeNames.split(',').forEach(tag => this._schema[tag.toLowerCase()] = type);
12644 const superType = superName && this._schema[superName.toLowerCase()];
12645 if (superType) {
12646 Object.keys(superType).forEach((prop) => {
12647 type[prop] = superType[prop];
12648 });
12649 }
12650 properties.forEach((property) => {
12651 if (property.length > 0) {
12652 switch (property[0]) {
12653 case '*':
12654 // We don't yet support events.
12655 // If ever allowing to bind to events, GO THROUGH A SECURITY REVIEW, allowing events
12656 // will
12657 // almost certainly introduce bad XSS vulnerabilities.
12658 // type[property.substring(1)] = EVENT;
12659 break;
12660 case '!':
12661 type[property.substring(1)] = BOOLEAN;
12662 break;
12663 case '#':
12664 type[property.substring(1)] = NUMBER;
12665 break;
12666 case '%':
12667 type[property.substring(1)] = OBJECT;
12668 break;
12669 default:
12670 type[property] = STRING;
12671 }
12672 }
12673 });
12674 });
12675 }
12676 hasProperty(tagName, propName, schemaMetas) {
12677 if (schemaMetas.some((schema) => schema.name === NO_ERRORS_SCHEMA.name)) {
12678 return true;
12679 }
12680 if (tagName.indexOf('-') > -1) {
12681 if (isNgContainer(tagName) || isNgContent(tagName)) {
12682 return false;
12683 }
12684 if (schemaMetas.some((schema) => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
12685 // Can't tell now as we don't know which properties a custom element will get
12686 // once it is instantiated
12687 return true;
12688 }
12689 }
12690 const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown'];
12691 return !!elementProperties[propName];
12692 }
12693 hasElement(tagName, schemaMetas) {
12694 if (schemaMetas.some((schema) => schema.name === NO_ERRORS_SCHEMA.name)) {
12695 return true;
12696 }
12697 if (tagName.indexOf('-') > -1) {
12698 if (isNgContainer(tagName) || isNgContent(tagName)) {
12699 return true;
12700 }
12701 if (schemaMetas.some((schema) => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
12702 // Allow any custom elements
12703 return true;
12704 }
12705 }
12706 return !!this._schema[tagName.toLowerCase()];
12707 }
12708 /**
12709 * securityContext returns the security context for the given property on the given DOM tag.
12710 *
12711 * Tag and property name are statically known and cannot change at runtime, i.e. it is not
12712 * possible to bind a value into a changing attribute or tag name.
12713 *
12714 * The filtering is based on a list of allowed tags|attributes. All attributes in the schema
12715 * above are assumed to have the 'NONE' security context, i.e. that they are safe inert
12716 * string values. Only specific well known attack vectors are assigned their appropriate context.
12717 */
12718 securityContext(tagName, propName, isAttribute) {
12719 if (isAttribute) {
12720 // NB: For security purposes, use the mapped property name, not the attribute name.
12721 propName = this.getMappedPropName(propName);
12722 }
12723 // Make sure comparisons are case insensitive, so that case differences between attribute and
12724 // property names do not have a security impact.
12725 tagName = tagName.toLowerCase();
12726 propName = propName.toLowerCase();
12727 let ctx = SECURITY_SCHEMA()[tagName + '|' + propName];
12728 if (ctx) {
12729 return ctx;
12730 }
12731 ctx = SECURITY_SCHEMA()['*|' + propName];
12732 return ctx ? ctx : SecurityContext.NONE;
12733 }
12734 getMappedPropName(propName) {
12735 return _ATTR_TO_PROP[propName] || propName;
12736 }
12737 getDefaultComponentElementName() {
12738 return 'ng-component';
12739 }
12740 validateProperty(name) {
12741 if (name.toLowerCase().startsWith('on')) {
12742 const msg = `Binding to event property '${name}' is disallowed for security reasons, ` +
12743 `please use (${name.slice(2)})=...` +
12744 `\nIf '${name}' is a directive input, make sure the directive is imported by the` +
12745 ` current module.`;
12746 return { error: true, msg: msg };
12747 }
12748 else {
12749 return { error: false };
12750 }
12751 }
12752 validateAttribute(name) {
12753 if (name.toLowerCase().startsWith('on')) {
12754 const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
12755 `please use (${name.slice(2)})=...`;
12756 return { error: true, msg: msg };
12757 }
12758 else {
12759 return { error: false };
12760 }
12761 }
12762 allKnownElementNames() {
12763 return Object.keys(this._schema);
12764 }
12765 allKnownAttributesOfElement(tagName) {
12766 const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown'];
12767 // Convert properties to attributes.
12768 return Object.keys(elementProperties).map(prop => { var _a; return (_a = _PROP_TO_ATTR[prop]) !== null && _a !== void 0 ? _a : prop; });
12769 }
12770 normalizeAnimationStyleProperty(propName) {
12771 return dashCaseToCamelCase(propName);
12772 }
12773 normalizeAnimationStyleValue(camelCaseProp, userProvidedProp, val) {
12774 let unit = '';
12775 const strVal = val.toString().trim();
12776 let errorMsg = null;
12777 if (_isPixelDimensionStyle(camelCaseProp) && val !== 0 && val !== '0') {
12778 if (typeof val === 'number') {
12779 unit = 'px';
12780 }
12781 else {
12782 const valAndSuffixMatch = val.match(/^[+-]?[\d\.]+([a-z]*)$/);
12783 if (valAndSuffixMatch && valAndSuffixMatch[1].length == 0) {
12784 errorMsg = `Please provide a CSS unit value for ${userProvidedProp}:${val}`;
12785 }
12786 }
12787 }
12788 return { error: errorMsg, value: strVal + unit };
12789 }
12790 }
12791 function _isPixelDimensionStyle(prop) {
12792 switch (prop) {
12793 case 'width':
12794 case 'height':
12795 case 'minWidth':
12796 case 'minHeight':
12797 case 'maxWidth':
12798 case 'maxHeight':
12799 case 'left':
12800 case 'top':
12801 case 'bottom':
12802 case 'right':
12803 case 'fontSize':
12804 case 'outlineWidth':
12805 case 'outlineOffset':
12806 case 'paddingTop':
12807 case 'paddingLeft':
12808 case 'paddingBottom':
12809 case 'paddingRight':
12810 case 'marginTop':
12811 case 'marginLeft':
12812 case 'marginBottom':
12813 case 'marginRight':
12814 case 'borderRadius':
12815 case 'borderWidth':
12816 case 'borderTopWidth':
12817 case 'borderLeftWidth':
12818 case 'borderRightWidth':
12819 case 'borderBottomWidth':
12820 case 'textIndent':
12821 return true;
12822 default:
12823 return false;
12824 }
12825 }
12826
12827 /**
12828 * @license
12829 * Copyright Google LLC All Rights Reserved.
12830 *
12831 * Use of this source code is governed by an MIT-style license that can be
12832 * found in the LICENSE file at https://angular.io/license
12833 */
12834 /**
12835 * Set of tagName|propertyName corresponding to Trusted Types sinks. Properties applying to all
12836 * tags use '*'.
12837 *
12838 * Extracted from, and should be kept in sync with
12839 * https://w3c.github.io/webappsec-trusted-types/dist/spec/#integrations
12840 */
12841 const TRUSTED_TYPES_SINKS = new Set([
12842 // NOTE: All strings in this set *must* be lowercase!
12843 // TrustedHTML
12844 'iframe|srcdoc',
12845 '*|innerhtml',
12846 '*|outerhtml',
12847 // NB: no TrustedScript here, as the corresponding tags are stripped by the compiler.
12848 // TrustedScriptURL
12849 'embed|src',
12850 'object|codebase',
12851 'object|data',
12852 ]);
12853 /**
12854 * isTrustedTypesSink returns true if the given property on the given DOM tag is a Trusted Types
12855 * sink. In that case, use `ElementSchemaRegistry.securityContext` to determine which particular
12856 * Trusted Type is required for values passed to the sink:
12857 * - SecurityContext.HTML corresponds to TrustedHTML
12858 * - SecurityContext.RESOURCE_URL corresponds to TrustedScriptURL
12859 */
12860 function isTrustedTypesSink(tagName, propName) {
12861 // Make sure comparisons are case insensitive, so that case differences between attribute and
12862 // property names do not have a security impact.
12863 tagName = tagName.toLowerCase();
12864 propName = propName.toLowerCase();
12865 return TRUSTED_TYPES_SINKS.has(tagName + '|' + propName) ||
12866 TRUSTED_TYPES_SINKS.has('*|' + propName);
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 const BIND_NAME_REGEXP = /^(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*)$/;
12877 // Group 1 = "bind-"
12878 const KW_BIND_IDX = 1;
12879 // Group 2 = "let-"
12880 const KW_LET_IDX = 2;
12881 // Group 3 = "ref-/#"
12882 const KW_REF_IDX = 3;
12883 // Group 4 = "on-"
12884 const KW_ON_IDX = 4;
12885 // Group 5 = "bindon-"
12886 const KW_BINDON_IDX = 5;
12887 // Group 6 = "@"
12888 const KW_AT_IDX = 6;
12889 // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@"
12890 const IDENT_KW_IDX = 7;
12891 const BINDING_DELIMS = {
12892 BANANA_BOX: { start: '[(', end: ')]' },
12893 PROPERTY: { start: '[', end: ']' },
12894 EVENT: { start: '(', end: ')' },
12895 };
12896 const TEMPLATE_ATTR_PREFIX$1 = '*';
12897 function htmlAstToRender3Ast(htmlNodes, bindingParser) {
12898 const transformer = new HtmlAstToIvyAst(bindingParser);
12899 const ivyNodes = visitAll$1(transformer, htmlNodes);
12900 // Errors might originate in either the binding parser or the html to ivy transformer
12901 const allErrors = bindingParser.errors.concat(transformer.errors);
12902 return {
12903 nodes: ivyNodes,
12904 errors: allErrors,
12905 styleUrls: transformer.styleUrls,
12906 styles: transformer.styles,
12907 ngContentSelectors: transformer.ngContentSelectors,
12908 };
12909 }
12910 class HtmlAstToIvyAst {
12911 constructor(bindingParser) {
12912 this.bindingParser = bindingParser;
12913 this.errors = [];
12914 this.styles = [];
12915 this.styleUrls = [];
12916 this.ngContentSelectors = [];
12917 this.inI18nBlock = false;
12918 }
12919 // HTML visitor
12920 visitElement(element) {
12921 const isI18nRootElement = isI18nRootNode(element.i18n);
12922 if (isI18nRootElement) {
12923 if (this.inI18nBlock) {
12924 this.reportError('Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.', element.sourceSpan);
12925 }
12926 this.inI18nBlock = true;
12927 }
12928 const preparsedElement = preparseElement(element);
12929 if (preparsedElement.type === PreparsedElementType.SCRIPT) {
12930 return null;
12931 }
12932 else if (preparsedElement.type === PreparsedElementType.STYLE) {
12933 const contents = textContents(element);
12934 if (contents !== null) {
12935 this.styles.push(contents);
12936 }
12937 return null;
12938 }
12939 else if (preparsedElement.type === PreparsedElementType.STYLESHEET &&
12940 isStyleUrlResolvable(preparsedElement.hrefAttr)) {
12941 this.styleUrls.push(preparsedElement.hrefAttr);
12942 return null;
12943 }
12944 // Whether the element is a `<ng-template>`
12945 const isTemplateElement = isNgTemplate(element.name);
12946 const parsedProperties = [];
12947 const boundEvents = [];
12948 const variables = [];
12949 const references = [];
12950 const attributes = [];
12951 const i18nAttrsMeta = {};
12952 const templateParsedProperties = [];
12953 const templateVariables = [];
12954 // Whether the element has any *-attribute
12955 let elementHasInlineTemplate = false;
12956 for (const attribute of element.attrs) {
12957 let hasBinding = false;
12958 const normalizedName = normalizeAttributeName(attribute.name);
12959 // `*attr` defines template bindings
12960 let isTemplateBinding = false;
12961 if (attribute.i18n) {
12962 i18nAttrsMeta[attribute.name] = attribute.i18n;
12963 }
12964 if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX$1)) {
12965 // *-attributes
12966 if (elementHasInlineTemplate) {
12967 this.reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attribute.sourceSpan);
12968 }
12969 isTemplateBinding = true;
12970 elementHasInlineTemplate = true;
12971 const templateValue = attribute.value;
12972 const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX$1.length);
12973 const parsedVariables = [];
12974 const absoluteValueOffset = attribute.valueSpan ?
12975 attribute.valueSpan.start.offset :
12976 // If there is no value span the attribute does not have a value, like `attr` in
12977 //`<div attr></div>`. In this case, point to one character beyond the last character of
12978 // the attribute name.
12979 attribute.sourceSpan.start.offset + attribute.name.length;
12980 this.bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables, true /* isIvyAst */);
12981 templateVariables.push(...parsedVariables.map(v => new Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan)));
12982 }
12983 else {
12984 // Check for variables, events, property bindings, interpolation
12985 hasBinding = this.parseAttribute(isTemplateElement, attribute, [], parsedProperties, boundEvents, variables, references);
12986 }
12987 if (!hasBinding && !isTemplateBinding) {
12988 // don't include the bindings as attributes as well in the AST
12989 attributes.push(this.visitAttribute(attribute));
12990 }
12991 }
12992 const children = visitAll$1(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children);
12993 let parsedElement;
12994 if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
12995 // `<ng-content>`
12996 if (element.children &&
12997 !element.children.every((node) => isEmptyTextNode(node) || isCommentNode(node))) {
12998 this.reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
12999 }
13000 const selector = preparsedElement.selectAttr;
13001 const attrs = element.attrs.map(attr => this.visitAttribute(attr));
13002 parsedElement = new Content(selector, attrs, element.sourceSpan, element.i18n);
13003 this.ngContentSelectors.push(selector);
13004 }
13005 else if (isTemplateElement) {
13006 // `<ng-template>`
13007 const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
13008 parsedElement = new Template(element.name, attributes, attrs.bound, boundEvents, [ /* no template attributes */], children, references, variables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
13009 }
13010 else {
13011 const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
13012 parsedElement = new Element(element.name, attributes, attrs.bound, boundEvents, children, references, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
13013 }
13014 if (elementHasInlineTemplate) {
13015 // If this node is an inline-template (e.g. has *ngFor) then we need to create a template
13016 // node that contains this node.
13017 // Moreover, if the node is an element, then we need to hoist its attributes to the template
13018 // node for matching against content projection selectors.
13019 const attrs = this.extractAttributes('ng-template', templateParsedProperties, i18nAttrsMeta);
13020 const templateAttrs = [];
13021 attrs.literal.forEach(attr => templateAttrs.push(attr));
13022 attrs.bound.forEach(attr => templateAttrs.push(attr));
13023 const hoistedAttrs = parsedElement instanceof Element ?
13024 {
13025 attributes: parsedElement.attributes,
13026 inputs: parsedElement.inputs,
13027 outputs: parsedElement.outputs,
13028 } :
13029 { attributes: [], inputs: [], outputs: [] };
13030 // For <ng-template>s with structural directives on them, avoid passing i18n information to
13031 // the wrapping template to prevent unnecessary i18n instructions from being generated. The
13032 // necessary i18n meta information will be extracted from child elements.
13033 const i18n = isTemplateElement && isI18nRootElement ? undefined : element.i18n;
13034 // TODO(pk): test for this case
13035 parsedElement = new Template(parsedElement.name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, templateAttrs, [parsedElement], [ /* no references */], templateVariables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, i18n);
13036 }
13037 if (isI18nRootElement) {
13038 this.inI18nBlock = false;
13039 }
13040 return parsedElement;
13041 }
13042 visitAttribute(attribute) {
13043 return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
13044 }
13045 visitText(text) {
13046 return this._visitTextWithInterpolation(text.value, text.sourceSpan, text.i18n);
13047 }
13048 visitExpansion(expansion) {
13049 if (!expansion.i18n) {
13050 // do not generate Icu in case it was created
13051 // outside of i18n block in a template
13052 return null;
13053 }
13054 if (!isI18nRootNode(expansion.i18n)) {
13055 throw new Error(`Invalid type "${expansion.i18n.constructor}" for "i18n" property of ${expansion.sourceSpan.toString()}. Expected a "Message"`);
13056 }
13057 const message = expansion.i18n;
13058 const vars = {};
13059 const placeholders = {};
13060 // extract VARs from ICUs - we process them separately while
13061 // assembling resulting message via goog.getMsg function, since
13062 // we need to pass them to top-level goog.getMsg call
13063 Object.keys(message.placeholders).forEach(key => {
13064 const value = message.placeholders[key];
13065 if (key.startsWith(I18N_ICU_VAR_PREFIX)) {
13066 // Currently when the `plural` or `select` keywords in an ICU contain trailing spaces (e.g.
13067 // `{count, select , ...}`), these spaces are also included into the key names in ICU vars
13068 // (e.g. "VAR_SELECT "). These trailing spaces are not desirable, since they will later be
13069 // converted into `_` symbols while normalizing placeholder names, which might lead to
13070 // mismatches at runtime (i.e. placeholder will not be replaced with the correct value).
13071 const formattedKey = key.trim();
13072 const ast = this.bindingParser.parseInterpolationExpression(value.text, value.sourceSpan);
13073 vars[formattedKey] = new BoundText(ast, value.sourceSpan);
13074 }
13075 else {
13076 placeholders[key] = this._visitTextWithInterpolation(value.text, value.sourceSpan);
13077 }
13078 });
13079 return new Icu(vars, placeholders, expansion.sourceSpan, message);
13080 }
13081 visitExpansionCase(expansionCase) {
13082 return null;
13083 }
13084 visitComment(comment) {
13085 return null;
13086 }
13087 // convert view engine `ParsedProperty` to a format suitable for IVY
13088 extractAttributes(elementName, properties, i18nPropsMeta) {
13089 const bound = [];
13090 const literal = [];
13091 properties.forEach(prop => {
13092 const i18n = i18nPropsMeta[prop.name];
13093 if (prop.isLiteral) {
13094 literal.push(new TextAttribute(prop.name, prop.expression.source || '', prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n));
13095 }
13096 else {
13097 // Note that validation is skipped and property mapping is disabled
13098 // due to the fact that we need to make sure a given prop is not an
13099 // input of a directive and directive matching happens at runtime.
13100 const bep = this.bindingParser.createBoundElementProperty(elementName, prop, /* skipValidation */ true, /* mapPropertyName */ false);
13101 bound.push(BoundAttribute.fromBoundElementProperty(bep, i18n));
13102 }
13103 });
13104 return { bound, literal };
13105 }
13106 parseAttribute(isTemplateElement, attribute, matchableAttributes, parsedProperties, boundEvents, variables, references) {
13107 const name = normalizeAttributeName(attribute.name);
13108 const value = attribute.value;
13109 const srcSpan = attribute.sourceSpan;
13110 const absoluteOffset = attribute.valueSpan ? attribute.valueSpan.start.offset : srcSpan.start.offset;
13111 function createKeySpan(srcSpan, prefix, identifier) {
13112 // We need to adjust the start location for the keySpan to account for the removed 'data-'
13113 // prefix from `normalizeAttributeName`.
13114 const normalizationAdjustment = attribute.name.length - name.length;
13115 const keySpanStart = srcSpan.start.moveBy(prefix.length + normalizationAdjustment);
13116 const keySpanEnd = keySpanStart.moveBy(identifier.length);
13117 return new ParseSourceSpan(keySpanStart, keySpanEnd, keySpanStart, identifier);
13118 }
13119 const bindParts = name.match(BIND_NAME_REGEXP);
13120 if (bindParts) {
13121 if (bindParts[KW_BIND_IDX] != null) {
13122 const identifier = bindParts[IDENT_KW_IDX];
13123 const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier);
13124 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
13125 }
13126 else if (bindParts[KW_LET_IDX]) {
13127 if (isTemplateElement) {
13128 const identifier = bindParts[IDENT_KW_IDX];
13129 const keySpan = createKeySpan(srcSpan, bindParts[KW_LET_IDX], identifier);
13130 this.parseVariable(identifier, value, srcSpan, keySpan, attribute.valueSpan, variables);
13131 }
13132 else {
13133 this.reportError(`"let-" is only supported on ng-template elements.`, srcSpan);
13134 }
13135 }
13136 else if (bindParts[KW_REF_IDX]) {
13137 const identifier = bindParts[IDENT_KW_IDX];
13138 const keySpan = createKeySpan(srcSpan, bindParts[KW_REF_IDX], identifier);
13139 this.parseReference(identifier, value, srcSpan, keySpan, attribute.valueSpan, references);
13140 }
13141 else if (bindParts[KW_ON_IDX]) {
13142 const events = [];
13143 const identifier = bindParts[IDENT_KW_IDX];
13144 const keySpan = createKeySpan(srcSpan, bindParts[KW_ON_IDX], identifier);
13145 this.bindingParser.parseEvent(identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
13146 addEvents(events, boundEvents);
13147 }
13148 else if (bindParts[KW_BINDON_IDX]) {
13149 const identifier = bindParts[IDENT_KW_IDX];
13150 const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier);
13151 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
13152 this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
13153 }
13154 else if (bindParts[KW_AT_IDX]) {
13155 const keySpan = createKeySpan(srcSpan, '', name);
13156 this.bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
13157 }
13158 return true;
13159 }
13160 // We didn't see a kw-prefixed property binding, but we have not yet checked
13161 // for the []/()/[()] syntax.
13162 let delims = null;
13163 if (name.startsWith(BINDING_DELIMS.BANANA_BOX.start)) {
13164 delims = BINDING_DELIMS.BANANA_BOX;
13165 }
13166 else if (name.startsWith(BINDING_DELIMS.PROPERTY.start)) {
13167 delims = BINDING_DELIMS.PROPERTY;
13168 }
13169 else if (name.startsWith(BINDING_DELIMS.EVENT.start)) {
13170 delims = BINDING_DELIMS.EVENT;
13171 }
13172 if (delims !== null &&
13173 // NOTE: older versions of the parser would match a start/end delimited
13174 // binding iff the property name was terminated by the ending delimiter
13175 // and the identifier in the binding was non-empty.
13176 // TODO(ayazhafiz): update this to handle malformed bindings.
13177 name.endsWith(delims.end) && name.length > delims.start.length + delims.end.length) {
13178 const identifier = name.substring(delims.start.length, name.length - delims.end.length);
13179 const keySpan = createKeySpan(srcSpan, delims.start, identifier);
13180 if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {
13181 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
13182 this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
13183 }
13184 else if (delims.start === BINDING_DELIMS.PROPERTY.start) {
13185 this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
13186 }
13187 else {
13188 const events = [];
13189 this.bindingParser.parseEvent(identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
13190 addEvents(events, boundEvents);
13191 }
13192 return true;
13193 }
13194 // No explicit binding found.
13195 const keySpan = createKeySpan(srcSpan, '' /* prefix */, name);
13196 const hasBinding = this.bindingParser.parsePropertyInterpolation(name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
13197 return hasBinding;
13198 }
13199 _visitTextWithInterpolation(value, sourceSpan, i18n) {
13200 const valueNoNgsp = replaceNgsp(value);
13201 const expr = this.bindingParser.parseInterpolation(valueNoNgsp, sourceSpan);
13202 return expr ? new BoundText(expr, sourceSpan, i18n) : new Text(valueNoNgsp, sourceSpan);
13203 }
13204 parseVariable(identifier, value, sourceSpan, keySpan, valueSpan, variables) {
13205 if (identifier.indexOf('-') > -1) {
13206 this.reportError(`"-" is not allowed in variable names`, sourceSpan);
13207 }
13208 else if (identifier.length === 0) {
13209 this.reportError(`Variable does not have a name`, sourceSpan);
13210 }
13211 variables.push(new Variable(identifier, value, sourceSpan, keySpan, valueSpan));
13212 }
13213 parseReference(identifier, value, sourceSpan, keySpan, valueSpan, references) {
13214 if (identifier.indexOf('-') > -1) {
13215 this.reportError(`"-" is not allowed in reference names`, sourceSpan);
13216 }
13217 else if (identifier.length === 0) {
13218 this.reportError(`Reference does not have a name`, sourceSpan);
13219 }
13220 else if (references.some(reference => reference.name === identifier)) {
13221 this.reportError(`Reference "#${identifier}" is defined more than once`, sourceSpan);
13222 }
13223 references.push(new Reference(identifier, value, sourceSpan, keySpan, valueSpan));
13224 }
13225 parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) {
13226 const events = [];
13227 this.bindingParser.parseEvent(`${name}Change`, `${expression}=$event`, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
13228 addEvents(events, boundEvents);
13229 }
13230 reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
13231 this.errors.push(new ParseError(sourceSpan, message, level));
13232 }
13233 }
13234 class NonBindableVisitor {
13235 visitElement(ast) {
13236 const preparsedElement = preparseElement(ast);
13237 if (preparsedElement.type === PreparsedElementType.SCRIPT ||
13238 preparsedElement.type === PreparsedElementType.STYLE ||
13239 preparsedElement.type === PreparsedElementType.STYLESHEET) {
13240 // Skipping <script> for security reasons
13241 // Skipping <style> and stylesheets as we already processed them
13242 // in the StyleCompiler
13243 return null;
13244 }
13245 const children = visitAll$1(this, ast.children, null);
13246 return new Element(ast.name, visitAll$1(this, ast.attrs),
13247 /* inputs */ [], /* outputs */ [], children, /* references */ [], ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
13248 }
13249 visitComment(comment) {
13250 return null;
13251 }
13252 visitAttribute(attribute) {
13253 return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
13254 }
13255 visitText(text) {
13256 return new Text(text.value, text.sourceSpan);
13257 }
13258 visitExpansion(expansion) {
13259 return null;
13260 }
13261 visitExpansionCase(expansionCase) {
13262 return null;
13263 }
13264 }
13265 const NON_BINDABLE_VISITOR = new NonBindableVisitor();
13266 function normalizeAttributeName(attrName) {
13267 return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
13268 }
13269 function addEvents(events, boundEvents) {
13270 boundEvents.push(...events.map(e => BoundEvent.fromParsedEvent(e)));
13271 }
13272 function isEmptyTextNode(node) {
13273 return node instanceof Text$2 && node.value.trim().length == 0;
13274 }
13275 function isCommentNode(node) {
13276 return node instanceof Comment;
13277 }
13278 function textContents(node) {
13279 if (node.children.length !== 1 || !(node.children[0] instanceof Text$2)) {
13280 return null;
13281 }
13282 else {
13283 return node.children[0].value;
13284 }
13285 }
13286
13287 /**
13288 * @license
13289 * Copyright Google LLC All Rights Reserved.
13290 *
13291 * Use of this source code is governed by an MIT-style license that can be
13292 * found in the LICENSE file at https://angular.io/license
13293 */
13294 var TagType;
13295 (function (TagType) {
13296 TagType[TagType["ELEMENT"] = 0] = "ELEMENT";
13297 TagType[TagType["TEMPLATE"] = 1] = "TEMPLATE";
13298 })(TagType || (TagType = {}));
13299 /**
13300 * Generates an object that is used as a shared state between parent and all child contexts.
13301 */
13302 function setupRegistry() {
13303 return { getUniqueId: getSeqNumberGenerator(), icus: new Map() };
13304 }
13305 /**
13306 * I18nContext is a helper class which keeps track of all i18n-related aspects
13307 * (accumulates placeholders, bindings, etc) between i18nStart and i18nEnd instructions.
13308 *
13309 * When we enter a nested template, the top-level context is being passed down
13310 * to the nested component, which uses this context to generate a child instance
13311 * of I18nContext class (to handle nested template) and at the end, reconciles it back
13312 * with the parent context.
13313 *
13314 * @param index Instruction index of i18nStart, which initiates this context
13315 * @param ref Reference to a translation const that represents the content if thus context
13316 * @param level Nestng level defined for child contexts
13317 * @param templateIndex Instruction index of a template which this context belongs to
13318 * @param meta Meta information (id, meaning, description, etc) associated with this context
13319 */
13320 class I18nContext {
13321 constructor(index, ref, level = 0, templateIndex = null, meta, registry) {
13322 this.index = index;
13323 this.ref = ref;
13324 this.level = level;
13325 this.templateIndex = templateIndex;
13326 this.meta = meta;
13327 this.registry = registry;
13328 this.bindings = new Set();
13329 this.placeholders = new Map();
13330 this.isEmitted = false;
13331 this._unresolvedCtxCount = 0;
13332 this._registry = registry || setupRegistry();
13333 this.id = this._registry.getUniqueId();
13334 }
13335 appendTag(type, node, index, closed) {
13336 if (node.isVoid && closed) {
13337 return; // ignore "close" for void tags
13338 }
13339 const ph = node.isVoid || !closed ? node.startName : node.closeName;
13340 const content = { type, index, ctx: this.id, isVoid: node.isVoid, closed };
13341 updatePlaceholderMap(this.placeholders, ph, content);
13342 }
13343 get icus() {
13344 return this._registry.icus;
13345 }
13346 get isRoot() {
13347 return this.level === 0;
13348 }
13349 get isResolved() {
13350 return this._unresolvedCtxCount === 0;
13351 }
13352 getSerializedPlaceholders() {
13353 const result = new Map();
13354 this.placeholders.forEach((values, key) => result.set(key, values.map(serializePlaceholderValue)));
13355 return result;
13356 }
13357 // public API to accumulate i18n-related content
13358 appendBinding(binding) {
13359 this.bindings.add(binding);
13360 }
13361 appendIcu(name, ref) {
13362 updatePlaceholderMap(this._registry.icus, name, ref);
13363 }
13364 appendBoundText(node) {
13365 const phs = assembleBoundTextPlaceholders(node, this.bindings.size, this.id);
13366 phs.forEach((values, key) => updatePlaceholderMap(this.placeholders, key, ...values));
13367 }
13368 appendTemplate(node, index) {
13369 // add open and close tags at the same time,
13370 // since we process nested templates separately
13371 this.appendTag(TagType.TEMPLATE, node, index, false);
13372 this.appendTag(TagType.TEMPLATE, node, index, true);
13373 this._unresolvedCtxCount++;
13374 }
13375 appendElement(node, index, closed) {
13376 this.appendTag(TagType.ELEMENT, node, index, closed);
13377 }
13378 appendProjection(node, index) {
13379 // Add open and close tags at the same time, since `<ng-content>` has no content,
13380 // so when we come across `<ng-content>` we can register both open and close tags.
13381 // Note: runtime i18n logic doesn't distinguish `<ng-content>` tag placeholders and
13382 // regular element tag placeholders, so we generate element placeholders for both types.
13383 this.appendTag(TagType.ELEMENT, node, index, false);
13384 this.appendTag(TagType.ELEMENT, node, index, true);
13385 }
13386 /**
13387 * Generates an instance of a child context based on the root one,
13388 * when we enter a nested template within I18n section.
13389 *
13390 * @param index Instruction index of corresponding i18nStart, which initiates this context
13391 * @param templateIndex Instruction index of a template which this context belongs to
13392 * @param meta Meta information (id, meaning, description, etc) associated with this context
13393 *
13394 * @returns I18nContext instance
13395 */
13396 forkChildContext(index, templateIndex, meta) {
13397 return new I18nContext(index, this.ref, this.level + 1, templateIndex, meta, this._registry);
13398 }
13399 /**
13400 * Reconciles child context into parent one once the end of the i18n block is reached (i18nEnd).
13401 *
13402 * @param context Child I18nContext instance to be reconciled with parent context.
13403 */
13404 reconcileChildContext(context) {
13405 // set the right context id for open and close
13406 // template tags, so we can use it as sub-block ids
13407 ['start', 'close'].forEach((op) => {
13408 const key = context.meta[`${op}Name`];
13409 const phs = this.placeholders.get(key) || [];
13410 const tag = phs.find(findTemplateFn(this.id, context.templateIndex));
13411 if (tag) {
13412 tag.ctx = context.id;
13413 }
13414 });
13415 // reconcile placeholders
13416 const childPhs = context.placeholders;
13417 childPhs.forEach((values, key) => {
13418 const phs = this.placeholders.get(key);
13419 if (!phs) {
13420 this.placeholders.set(key, values);
13421 return;
13422 }
13423 // try to find matching template...
13424 const tmplIdx = phs.findIndex(findTemplateFn(context.id, context.templateIndex));
13425 if (tmplIdx >= 0) {
13426 // ... if found - replace it with nested template content
13427 const isCloseTag = key.startsWith('CLOSE');
13428 const isTemplateTag = key.endsWith('NG-TEMPLATE');
13429 if (isTemplateTag) {
13430 // current template's content is placed before or after
13431 // parent template tag, depending on the open/close atrribute
13432 phs.splice(tmplIdx + (isCloseTag ? 0 : 1), 0, ...values);
13433 }
13434 else {
13435 const idx = isCloseTag ? values.length - 1 : 0;
13436 values[idx].tmpl = phs[tmplIdx];
13437 phs.splice(tmplIdx, 1, ...values);
13438 }
13439 }
13440 else {
13441 // ... otherwise just append content to placeholder value
13442 phs.push(...values);
13443 }
13444 this.placeholders.set(key, phs);
13445 });
13446 this._unresolvedCtxCount--;
13447 }
13448 }
13449 //
13450 // Helper methods
13451 //
13452 function wrap(symbol, index, contextId, closed) {
13453 const state = closed ? '/' : '';
13454 return wrapI18nPlaceholder(`${state}${symbol}${index}`, contextId);
13455 }
13456 function wrapTag(symbol, { index, ctx, isVoid }, closed) {
13457 return isVoid ? wrap(symbol, index, ctx) + wrap(symbol, index, ctx, true) :
13458 wrap(symbol, index, ctx, closed);
13459 }
13460 function findTemplateFn(ctx, templateIndex) {
13461 return (token) => typeof token === 'object' && token.type === TagType.TEMPLATE &&
13462 token.index === templateIndex && token.ctx === ctx;
13463 }
13464 function serializePlaceholderValue(value) {
13465 const element = (data, closed) => wrapTag('#', data, closed);
13466 const template = (data, closed) => wrapTag('*', data, closed);
13467 switch (value.type) {
13468 case TagType.ELEMENT:
13469 // close element tag
13470 if (value.closed) {
13471 return element(value, true) + (value.tmpl ? template(value.tmpl, true) : '');
13472 }
13473 // open element tag that also initiates a template
13474 if (value.tmpl) {
13475 return template(value.tmpl) + element(value) +
13476 (value.isVoid ? template(value.tmpl, true) : '');
13477 }
13478 return element(value);
13479 case TagType.TEMPLATE:
13480 return template(value, value.closed);
13481 default:
13482 return value;
13483 }
13484 }
13485
13486 /**
13487 * @license
13488 * Copyright Google LLC All Rights Reserved.
13489 *
13490 * Use of this source code is governed by an MIT-style license that can be
13491 * found in the LICENSE file at https://angular.io/license
13492 */
13493 class IcuSerializerVisitor {
13494 visitText(text) {
13495 return text.value;
13496 }
13497 visitContainer(container) {
13498 return container.children.map(child => child.visit(this)).join('');
13499 }
13500 visitIcu(icu) {
13501 const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
13502 const result = `{${icu.expressionPlaceholder}, ${icu.type}, ${strCases.join(' ')}}`;
13503 return result;
13504 }
13505 visitTagPlaceholder(ph) {
13506 return ph.isVoid ?
13507 this.formatPh(ph.startName) :
13508 `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
13509 }
13510 visitPlaceholder(ph) {
13511 return this.formatPh(ph.name);
13512 }
13513 visitIcuPlaceholder(ph, context) {
13514 return this.formatPh(ph.name);
13515 }
13516 formatPh(value) {
13517 return `{${formatI18nPlaceholderName(value, /* useCamelCase */ false)}}`;
13518 }
13519 }
13520 const serializer = new IcuSerializerVisitor();
13521 function serializeIcuNode(icu) {
13522 return icu.visit(serializer);
13523 }
13524
13525 /**
13526 * @license
13527 * Copyright Google LLC All Rights Reserved.
13528 *
13529 * Use of this source code is governed by an MIT-style license that can be
13530 * found in the LICENSE file at https://angular.io/license
13531 */
13532 const TAG_TO_PLACEHOLDER_NAMES = {
13533 'A': 'LINK',
13534 'B': 'BOLD_TEXT',
13535 'BR': 'LINE_BREAK',
13536 'EM': 'EMPHASISED_TEXT',
13537 'H1': 'HEADING_LEVEL1',
13538 'H2': 'HEADING_LEVEL2',
13539 'H3': 'HEADING_LEVEL3',
13540 'H4': 'HEADING_LEVEL4',
13541 'H5': 'HEADING_LEVEL5',
13542 'H6': 'HEADING_LEVEL6',
13543 'HR': 'HORIZONTAL_RULE',
13544 'I': 'ITALIC_TEXT',
13545 'LI': 'LIST_ITEM',
13546 'LINK': 'MEDIA_LINK',
13547 'OL': 'ORDERED_LIST',
13548 'P': 'PARAGRAPH',
13549 'Q': 'QUOTATION',
13550 'S': 'STRIKETHROUGH_TEXT',
13551 'SMALL': 'SMALL_TEXT',
13552 'SUB': 'SUBSTRIPT',
13553 'SUP': 'SUPERSCRIPT',
13554 'TBODY': 'TABLE_BODY',
13555 'TD': 'TABLE_CELL',
13556 'TFOOT': 'TABLE_FOOTER',
13557 'TH': 'TABLE_HEADER_CELL',
13558 'THEAD': 'TABLE_HEADER',
13559 'TR': 'TABLE_ROW',
13560 'TT': 'MONOSPACED_TEXT',
13561 'U': 'UNDERLINED_TEXT',
13562 'UL': 'UNORDERED_LIST',
13563 };
13564 /**
13565 * Creates unique names for placeholder with different content.
13566 *
13567 * Returns the same placeholder name when the content is identical.
13568 */
13569 class PlaceholderRegistry {
13570 constructor() {
13571 // Count the occurrence of the base name top generate a unique name
13572 this._placeHolderNameCounts = {};
13573 // Maps signature to placeholder names
13574 this._signatureToName = {};
13575 }
13576 getStartTagPlaceholderName(tag, attrs, isVoid) {
13577 const signature = this._hashTag(tag, attrs, isVoid);
13578 if (this._signatureToName[signature]) {
13579 return this._signatureToName[signature];
13580 }
13581 const upperTag = tag.toUpperCase();
13582 const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
13583 const name = this._generateUniqueName(isVoid ? baseName : `START_${baseName}`);
13584 this._signatureToName[signature] = name;
13585 return name;
13586 }
13587 getCloseTagPlaceholderName(tag) {
13588 const signature = this._hashClosingTag(tag);
13589 if (this._signatureToName[signature]) {
13590 return this._signatureToName[signature];
13591 }
13592 const upperTag = tag.toUpperCase();
13593 const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
13594 const name = this._generateUniqueName(`CLOSE_${baseName}`);
13595 this._signatureToName[signature] = name;
13596 return name;
13597 }
13598 getPlaceholderName(name, content) {
13599 const upperName = name.toUpperCase();
13600 const signature = `PH: ${upperName}=${content}`;
13601 if (this._signatureToName[signature]) {
13602 return this._signatureToName[signature];
13603 }
13604 const uniqueName = this._generateUniqueName(upperName);
13605 this._signatureToName[signature] = uniqueName;
13606 return uniqueName;
13607 }
13608 getUniquePlaceholder(name) {
13609 return this._generateUniqueName(name.toUpperCase());
13610 }
13611 // Generate a hash for a tag - does not take attribute order into account
13612 _hashTag(tag, attrs, isVoid) {
13613 const start = `<${tag}`;
13614 const strAttrs = Object.keys(attrs).sort().map((name) => ` ${name}=${attrs[name]}`).join('');
13615 const end = isVoid ? '/>' : `></${tag}>`;
13616 return start + strAttrs + end;
13617 }
13618 _hashClosingTag(tag) {
13619 return this._hashTag(`/${tag}`, {}, false);
13620 }
13621 _generateUniqueName(base) {
13622 const seen = this._placeHolderNameCounts.hasOwnProperty(base);
13623 if (!seen) {
13624 this._placeHolderNameCounts[base] = 1;
13625 return base;
13626 }
13627 const id = this._placeHolderNameCounts[base];
13628 this._placeHolderNameCounts[base] = id + 1;
13629 return `${base}_${id}`;
13630 }
13631 }
13632
13633 /**
13634 * @license
13635 * Copyright Google LLC All Rights Reserved.
13636 *
13637 * Use of this source code is governed by an MIT-style license that can be
13638 * found in the LICENSE file at https://angular.io/license
13639 */
13640 const _expParser = new Parser$1(new Lexer());
13641 /**
13642 * Returns a function converting html nodes to an i18n Message given an interpolationConfig
13643 */
13644 function createI18nMessageFactory(interpolationConfig) {
13645 const visitor = new _I18nVisitor(_expParser, interpolationConfig);
13646 return (nodes, meaning, description, customId, visitNodeFn) => visitor.toI18nMessage(nodes, meaning, description, customId, visitNodeFn);
13647 }
13648 function noopVisitNodeFn(_html, i18n) {
13649 return i18n;
13650 }
13651 class _I18nVisitor {
13652 constructor(_expressionParser, _interpolationConfig) {
13653 this._expressionParser = _expressionParser;
13654 this._interpolationConfig = _interpolationConfig;
13655 }
13656 toI18nMessage(nodes, meaning = '', description = '', customId = '', visitNodeFn) {
13657 const context = {
13658 isIcu: nodes.length == 1 && nodes[0] instanceof Expansion,
13659 icuDepth: 0,
13660 placeholderRegistry: new PlaceholderRegistry(),
13661 placeholderToContent: {},
13662 placeholderToMessage: {},
13663 visitNodeFn: visitNodeFn || noopVisitNodeFn,
13664 };
13665 const i18nodes = visitAll$1(this, nodes, context);
13666 return new Message(i18nodes, context.placeholderToContent, context.placeholderToMessage, meaning, description, customId);
13667 }
13668 visitElement(el, context) {
13669 var _a;
13670 const children = visitAll$1(this, el.children, context);
13671 const attrs = {};
13672 el.attrs.forEach(attr => {
13673 // Do not visit the attributes, translatable ones are top-level ASTs
13674 attrs[attr.name] = attr.value;
13675 });
13676 const isVoid = getHtmlTagDefinition(el.name).isVoid;
13677 const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid);
13678 context.placeholderToContent[startPhName] = {
13679 text: el.startSourceSpan.toString(),
13680 sourceSpan: el.startSourceSpan,
13681 };
13682 let closePhName = '';
13683 if (!isVoid) {
13684 closePhName = context.placeholderRegistry.getCloseTagPlaceholderName(el.name);
13685 context.placeholderToContent[closePhName] = {
13686 text: `</${el.name}>`,
13687 sourceSpan: (_a = el.endSourceSpan) !== null && _a !== void 0 ? _a : el.sourceSpan,
13688 };
13689 }
13690 const node = new TagPlaceholder(el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
13691 return context.visitNodeFn(el, node);
13692 }
13693 visitAttribute(attribute, context) {
13694 const node = this._visitTextWithInterpolation(attribute.value, attribute.valueSpan || attribute.sourceSpan, context, attribute.i18n);
13695 return context.visitNodeFn(attribute, node);
13696 }
13697 visitText(text, context) {
13698 const node = this._visitTextWithInterpolation(text.value, text.sourceSpan, context, text.i18n);
13699 return context.visitNodeFn(text, node);
13700 }
13701 visitComment(comment, context) {
13702 return null;
13703 }
13704 visitExpansion(icu, context) {
13705 context.icuDepth++;
13706 const i18nIcuCases = {};
13707 const i18nIcu = new Icu$1(icu.switchValue, icu.type, i18nIcuCases, icu.sourceSpan);
13708 icu.cases.forEach((caze) => {
13709 i18nIcuCases[caze.value] = new Container(caze.expression.map((node) => node.visit(this, context)), caze.expSourceSpan);
13710 });
13711 context.icuDepth--;
13712 if (context.isIcu || context.icuDepth > 0) {
13713 // Returns an ICU node when:
13714 // - the message (vs a part of the message) is an ICU message, or
13715 // - the ICU message is nested.
13716 const expPh = context.placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`);
13717 i18nIcu.expressionPlaceholder = expPh;
13718 context.placeholderToContent[expPh] = {
13719 text: icu.switchValue,
13720 sourceSpan: icu.switchValueSourceSpan,
13721 };
13722 return context.visitNodeFn(icu, i18nIcu);
13723 }
13724 // Else returns a placeholder
13725 // ICU placeholders should not be replaced with their original content but with the their
13726 // translations.
13727 // TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
13728 const phName = context.placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
13729 context.placeholderToMessage[phName] = this.toI18nMessage([icu], '', '', '', undefined);
13730 const node = new IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
13731 return context.visitNodeFn(icu, node);
13732 }
13733 visitExpansionCase(_icuCase, _context) {
13734 throw new Error('Unreachable code');
13735 }
13736 /**
13737 * Split the, potentially interpolated, text up into text and placeholder pieces.
13738 *
13739 * @param text The potentially interpolated string to be split.
13740 * @param sourceSpan The span of the whole of the `text` string.
13741 * @param context The current context of the visitor, used to compute and store placeholders.
13742 * @param previousI18n Any i18n metadata associated with this `text` from a previous pass.
13743 */
13744 _visitTextWithInterpolation(text, sourceSpan, context, previousI18n) {
13745 const { strings, expressions } = this._expressionParser.splitInterpolation(text, sourceSpan.start.toString(), this._interpolationConfig);
13746 // No expressions, return a single text.
13747 if (expressions.length === 0) {
13748 return new Text$1(text, sourceSpan);
13749 }
13750 // Return a sequence of `Text` and `Placeholder` nodes grouped in a `Container`.
13751 const nodes = [];
13752 for (let i = 0; i < strings.length - 1; i++) {
13753 this._addText(nodes, strings[i], sourceSpan);
13754 this._addPlaceholder(nodes, context, expressions[i], sourceSpan);
13755 }
13756 // The last index contains no expression
13757 this._addText(nodes, strings[strings.length - 1], sourceSpan);
13758 // Whitespace removal may have invalidated the interpolation source-spans.
13759 reusePreviousSourceSpans(nodes, previousI18n);
13760 return new Container(nodes, sourceSpan);
13761 }
13762 /**
13763 * Create a new `Text` node from the `textPiece` and add it to the `nodes` collection.
13764 *
13765 * @param nodes The nodes to which the created `Text` node should be added.
13766 * @param textPiece The text and relative span information for this `Text` node.
13767 * @param interpolationSpan The span of the whole interpolated text.
13768 */
13769 _addText(nodes, textPiece, interpolationSpan) {
13770 if (textPiece.text.length > 0) {
13771 // No need to add empty strings
13772 const stringSpan = getOffsetSourceSpan(interpolationSpan, textPiece);
13773 nodes.push(new Text$1(textPiece.text, stringSpan));
13774 }
13775 }
13776 /**
13777 * Create a new `Placeholder` node from the `expression` and add it to the `nodes` collection.
13778 *
13779 * @param nodes The nodes to which the created `Text` node should be added.
13780 * @param context The current context of the visitor, used to compute and store placeholders.
13781 * @param expression The expression text and relative span information for this `Placeholder`
13782 * node.
13783 * @param interpolationSpan The span of the whole interpolated text.
13784 */
13785 _addPlaceholder(nodes, context, expression, interpolationSpan) {
13786 const sourceSpan = getOffsetSourceSpan(interpolationSpan, expression);
13787 const baseName = extractPlaceholderName(expression.text) || 'INTERPOLATION';
13788 const phName = context.placeholderRegistry.getPlaceholderName(baseName, expression.text);
13789 const text = this._interpolationConfig.start + expression.text + this._interpolationConfig.end;
13790 context.placeholderToContent[phName] = { text, sourceSpan };
13791 nodes.push(new Placeholder(expression.text, phName, sourceSpan));
13792 }
13793 }
13794 /**
13795 * Re-use the source-spans from `previousI18n` metadata for the `nodes`.
13796 *
13797 * Whitespace removal can invalidate the source-spans of interpolation nodes, so we
13798 * reuse the source-span stored from a previous pass before the whitespace was removed.
13799 *
13800 * @param nodes The `Text` and `Placeholder` nodes to be processed.
13801 * @param previousI18n Any i18n metadata for these `nodes` stored from a previous pass.
13802 */
13803 function reusePreviousSourceSpans(nodes, previousI18n) {
13804 if (previousI18n instanceof Message) {
13805 // The `previousI18n` is an i18n `Message`, so we are processing an `Attribute` with i18n
13806 // metadata. The `Message` should consist only of a single `Container` that contains the
13807 // parts (`Text` and `Placeholder`) to process.
13808 assertSingleContainerMessage(previousI18n);
13809 previousI18n = previousI18n.nodes[0];
13810 }
13811 if (previousI18n instanceof Container) {
13812 // The `previousI18n` is a `Container`, which means that this is a second i18n extraction pass
13813 // after whitespace has been removed from the AST ndoes.
13814 assertEquivalentNodes(previousI18n.children, nodes);
13815 // Reuse the source-spans from the first pass.
13816 for (let i = 0; i < nodes.length; i++) {
13817 nodes[i].sourceSpan = previousI18n.children[i].sourceSpan;
13818 }
13819 }
13820 }
13821 /**
13822 * Asserts that the `message` contains exactly one `Container` node.
13823 */
13824 function assertSingleContainerMessage(message) {
13825 const nodes = message.nodes;
13826 if (nodes.length !== 1 || !(nodes[0] instanceof Container)) {
13827 throw new Error('Unexpected previous i18n message - expected it to consist of only a single `Container` node.');
13828 }
13829 }
13830 /**
13831 * Asserts that the `previousNodes` and `node` collections have the same number of elements and
13832 * corresponding elements have the same node type.
13833 */
13834 function assertEquivalentNodes(previousNodes, nodes) {
13835 if (previousNodes.length !== nodes.length) {
13836 throw new Error('The number of i18n message children changed between first and second pass.');
13837 }
13838 if (previousNodes.some((node, i) => nodes[i].constructor !== node.constructor)) {
13839 throw new Error('The types of the i18n message children changed between first and second pass.');
13840 }
13841 }
13842 /**
13843 * Create a new `ParseSourceSpan` from the `sourceSpan`, offset by the `start` and `end` values.
13844 */
13845 function getOffsetSourceSpan(sourceSpan, { start, end }) {
13846 return new ParseSourceSpan(sourceSpan.fullStart.moveBy(start), sourceSpan.fullStart.moveBy(end));
13847 }
13848 const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g;
13849 function extractPlaceholderName(input) {
13850 return input.split(_CUSTOM_PH_EXP)[2];
13851 }
13852
13853 /**
13854 * @license
13855 * Copyright Google LLC All Rights Reserved.
13856 *
13857 * Use of this source code is governed by an MIT-style license that can be
13858 * found in the LICENSE file at https://angular.io/license
13859 */
13860 /**
13861 * An i18n error.
13862 */
13863 class I18nError extends ParseError {
13864 constructor(span, msg) {
13865 super(span, msg);
13866 }
13867 }
13868
13869 /**
13870 * @license
13871 * Copyright Google LLC All Rights Reserved.
13872 *
13873 * Use of this source code is governed by an MIT-style license that can be
13874 * found in the LICENSE file at https://angular.io/license
13875 */
13876 const setI18nRefs = (htmlNode, i18nNode) => {
13877 if (htmlNode instanceof NodeWithI18n) {
13878 if (i18nNode instanceof IcuPlaceholder && htmlNode.i18n instanceof Message) {
13879 // This html node represents an ICU but this is a second processing pass, and the legacy id
13880 // was computed in the previous pass and stored in the `i18n` property as a message.
13881 // We are about to wipe out that property so capture the previous message to be reused when
13882 // generating the message for this ICU later. See `_generateI18nMessage()`.
13883 i18nNode.previousMessage = htmlNode.i18n;
13884 }
13885 htmlNode.i18n = i18nNode;
13886 }
13887 return i18nNode;
13888 };
13889 /**
13890 * This visitor walks over HTML parse tree and converts information stored in
13891 * i18n-related attributes ("i18n" and "i18n-*") into i18n meta object that is
13892 * stored with other element's and attribute's information.
13893 */
13894 class I18nMetaVisitor {
13895 constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false) {
13896 this.interpolationConfig = interpolationConfig;
13897 this.keepI18nAttrs = keepI18nAttrs;
13898 this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
13899 // whether visited nodes contain i18n information
13900 this.hasI18nMeta = false;
13901 this._errors = [];
13902 // i18n message generation factory
13903 this._createI18nMessage = createI18nMessageFactory(this.interpolationConfig);
13904 }
13905 _generateI18nMessage(nodes, meta = '', visitNodeFn) {
13906 const { meaning, description, customId } = this._parseMetadata(meta);
13907 const message = this._createI18nMessage(nodes, meaning, description, customId, visitNodeFn);
13908 this._setMessageId(message, meta);
13909 this._setLegacyIds(message, meta);
13910 return message;
13911 }
13912 visitAllWithErrors(nodes) {
13913 const result = nodes.map(node => node.visit(this, null));
13914 return new ParseTreeResult(result, this._errors);
13915 }
13916 visitElement(element) {
13917 if (hasI18nAttrs(element)) {
13918 this.hasI18nMeta = true;
13919 const attrs = [];
13920 const attrsMeta = {};
13921 for (const attr of element.attrs) {
13922 if (attr.name === I18N_ATTR) {
13923 // root 'i18n' node attribute
13924 const i18n = element.i18n || attr.value;
13925 const message = this._generateI18nMessage(element.children, i18n, setI18nRefs);
13926 // do not assign empty i18n meta
13927 if (message.nodes.length) {
13928 element.i18n = message;
13929 }
13930 }
13931 else if (attr.name.startsWith(I18N_ATTR_PREFIX)) {
13932 // 'i18n-*' attributes
13933 const name = attr.name.slice(I18N_ATTR_PREFIX.length);
13934 if (isTrustedTypesSink(element.name, name)) {
13935 this._reportError(attr, `Translating attribute '${name}' is disallowed for security reasons.`);
13936 }
13937 else {
13938 attrsMeta[name] = attr.value;
13939 }
13940 }
13941 else {
13942 // non-i18n attributes
13943 attrs.push(attr);
13944 }
13945 }
13946 // set i18n meta for attributes
13947 if (Object.keys(attrsMeta).length) {
13948 for (const attr of attrs) {
13949 const meta = attrsMeta[attr.name];
13950 // do not create translation for empty attributes
13951 if (meta !== undefined && attr.value) {
13952 attr.i18n = this._generateI18nMessage([attr], attr.i18n || meta);
13953 }
13954 }
13955 }
13956 if (!this.keepI18nAttrs) {
13957 // update element's attributes,
13958 // keeping only non-i18n related ones
13959 element.attrs = attrs;
13960 }
13961 }
13962 visitAll$1(this, element.children, element.i18n);
13963 return element;
13964 }
13965 visitExpansion(expansion, currentMessage) {
13966 let message;
13967 const meta = expansion.i18n;
13968 this.hasI18nMeta = true;
13969 if (meta instanceof IcuPlaceholder) {
13970 // set ICU placeholder name (e.g. "ICU_1"),
13971 // generated while processing root element contents,
13972 // so we can reference it when we output translation
13973 const name = meta.name;
13974 message = this._generateI18nMessage([expansion], meta);
13975 const icu = icuFromI18nMessage(message);
13976 icu.name = name;
13977 }
13978 else {
13979 // ICU is a top level message, try to use metadata from container element if provided via
13980 // `context` argument. Note: context may not be available for standalone ICUs (without
13981 // wrapping element), so fallback to ICU metadata in this case.
13982 message = this._generateI18nMessage([expansion], currentMessage || meta);
13983 }
13984 expansion.i18n = message;
13985 return expansion;
13986 }
13987 visitText(text) {
13988 return text;
13989 }
13990 visitAttribute(attribute) {
13991 return attribute;
13992 }
13993 visitComment(comment) {
13994 return comment;
13995 }
13996 visitExpansionCase(expansionCase) {
13997 return expansionCase;
13998 }
13999 /**
14000 * Parse the general form `meta` passed into extract the explicit metadata needed to create a
14001 * `Message`.
14002 *
14003 * There are three possibilities for the `meta` variable
14004 * 1) a string from an `i18n` template attribute: parse it to extract the metadata values.
14005 * 2) a `Message` from a previous processing pass: reuse the metadata values in the message.
14006 * 4) other: ignore this and just process the message metadata as normal
14007 *
14008 * @param meta the bucket that holds information about the message
14009 * @returns the parsed metadata.
14010 */
14011 _parseMetadata(meta) {
14012 return typeof meta === 'string' ? parseI18nMeta(meta) :
14013 meta instanceof Message ? meta : {};
14014 }
14015 /**
14016 * Generate (or restore) message id if not specified already.
14017 */
14018 _setMessageId(message, meta) {
14019 if (!message.id) {
14020 message.id = meta instanceof Message && meta.id || decimalDigest(message);
14021 }
14022 }
14023 /**
14024 * Update the `message` with a `legacyId` if necessary.
14025 *
14026 * @param message the message whose legacy id should be set
14027 * @param meta information about the message being processed
14028 */
14029 _setLegacyIds(message, meta) {
14030 if (this.enableI18nLegacyMessageIdFormat) {
14031 message.legacyIds = [computeDigest(message), computeDecimalDigest(message)];
14032 }
14033 else if (typeof meta !== 'string') {
14034 // This occurs if we are doing the 2nd pass after whitespace removal (see `parseTemplate()` in
14035 // `packages/compiler/src/render3/view/template.ts`).
14036 // In that case we want to reuse the legacy message generated in the 1st pass (see
14037 // `setI18nRefs()`).
14038 const previousMessage = meta instanceof Message ?
14039 meta :
14040 meta instanceof IcuPlaceholder ? meta.previousMessage : undefined;
14041 message.legacyIds = previousMessage ? previousMessage.legacyIds : [];
14042 }
14043 }
14044 _reportError(node, msg) {
14045 this._errors.push(new I18nError(node.sourceSpan, msg));
14046 }
14047 }
14048 /** I18n separators for metadata **/
14049 const I18N_MEANING_SEPARATOR = '|';
14050 const I18N_ID_SEPARATOR = '@@';
14051 /**
14052 * Parses i18n metas like:
14053 * - "@@id",
14054 * - "description[@@id]",
14055 * - "meaning|description[@@id]"
14056 * and returns an object with parsed output.
14057 *
14058 * @param meta String that represents i18n meta
14059 * @returns Object with id, meaning and description fields
14060 */
14061 function parseI18nMeta(meta = '') {
14062 let customId;
14063 let meaning;
14064 let description;
14065 meta = meta.trim();
14066 if (meta) {
14067 const idIndex = meta.indexOf(I18N_ID_SEPARATOR);
14068 const descIndex = meta.indexOf(I18N_MEANING_SEPARATOR);
14069 let meaningAndDesc;
14070 [meaningAndDesc, customId] =
14071 (idIndex > -1) ? [meta.slice(0, idIndex), meta.slice(idIndex + 2)] : [meta, ''];
14072 [meaning, description] = (descIndex > -1) ?
14073 [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] :
14074 ['', meaningAndDesc];
14075 }
14076 return { customId, meaning, description };
14077 }
14078 // Converts i18n meta information for a message (id, description, meaning)
14079 // to a JsDoc statement formatted as expected by the Closure compiler.
14080 function i18nMetaToJSDoc(meta) {
14081 const tags = [];
14082 if (meta.description) {
14083 tags.push({ tagName: "desc" /* Desc */, text: meta.description });
14084 }
14085 if (meta.meaning) {
14086 tags.push({ tagName: "meaning" /* Meaning */, text: meta.meaning });
14087 }
14088 return tags.length == 0 ? null : jsDocComment(tags);
14089 }
14090
14091 /** Closure uses `goog.getMsg(message)` to lookup translations */
14092 const GOOG_GET_MSG = 'goog.getMsg';
14093 function createGoogleGetMsgStatements(variable$1, message, closureVar, params) {
14094 const messageString = serializeI18nMessageForGetMsg(message);
14095 const args = [literal(messageString)];
14096 if (Object.keys(params).length) {
14097 args.push(mapLiteral(params, true));
14098 }
14099 // /**
14100 // * @desc description of message
14101 // * @meaning meaning of message
14102 // */
14103 // const MSG_... = goog.getMsg(..);
14104 // I18N_X = MSG_...;
14105 const googGetMsgStmt = closureVar.set(variable(GOOG_GET_MSG).callFn(args)).toConstDecl();
14106 const metaComment = i18nMetaToJSDoc(message);
14107 if (metaComment !== null) {
14108 googGetMsgStmt.addLeadingComment(metaComment);
14109 }
14110 const i18nAssignmentStmt = new ExpressionStatement(variable$1.set(closureVar));
14111 return [googGetMsgStmt, i18nAssignmentStmt];
14112 }
14113 /**
14114 * This visitor walks over i18n tree and generates its string representation, including ICUs and
14115 * placeholders in `{$placeholder}` (for plain messages) or `{PLACEHOLDER}` (inside ICUs) format.
14116 */
14117 class GetMsgSerializerVisitor {
14118 formatPh(value) {
14119 return `{$${formatI18nPlaceholderName(value)}}`;
14120 }
14121 visitText(text) {
14122 return text.value;
14123 }
14124 visitContainer(container) {
14125 return container.children.map(child => child.visit(this)).join('');
14126 }
14127 visitIcu(icu) {
14128 return serializeIcuNode(icu);
14129 }
14130 visitTagPlaceholder(ph) {
14131 return ph.isVoid ?
14132 this.formatPh(ph.startName) :
14133 `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
14134 }
14135 visitPlaceholder(ph) {
14136 return this.formatPh(ph.name);
14137 }
14138 visitIcuPlaceholder(ph, context) {
14139 return this.formatPh(ph.name);
14140 }
14141 }
14142 const serializerVisitor$1 = new GetMsgSerializerVisitor();
14143 function serializeI18nMessageForGetMsg(message) {
14144 return message.nodes.map(node => node.visit(serializerVisitor$1, null)).join('');
14145 }
14146
14147 function createLocalizeStatements(variable, message, params) {
14148 const { messageParts, placeHolders } = serializeI18nMessageForLocalize(message);
14149 const sourceSpan = getSourceSpan(message);
14150 const expressions = placeHolders.map(ph => params[ph.text]);
14151 const localizedString$1 = localizedString(message, messageParts, placeHolders, expressions, sourceSpan);
14152 const variableInitialization = variable.set(localizedString$1);
14153 return [new ExpressionStatement(variableInitialization)];
14154 }
14155 /**
14156 * This visitor walks over an i18n tree, capturing literal strings and placeholders.
14157 *
14158 * The result can be used for generating the `$localize` tagged template literals.
14159 */
14160 class LocalizeSerializerVisitor {
14161 visitText(text, context) {
14162 if (context[context.length - 1] instanceof LiteralPiece) {
14163 // Two literal pieces in a row means that there was some comment node in-between.
14164 context[context.length - 1].text += text.value;
14165 }
14166 else {
14167 context.push(new LiteralPiece(text.value, text.sourceSpan));
14168 }
14169 }
14170 visitContainer(container, context) {
14171 container.children.forEach(child => child.visit(this, context));
14172 }
14173 visitIcu(icu, context) {
14174 context.push(new LiteralPiece(serializeIcuNode(icu), icu.sourceSpan));
14175 }
14176 visitTagPlaceholder(ph, context) {
14177 var _a, _b;
14178 context.push(this.createPlaceholderPiece(ph.startName, (_a = ph.startSourceSpan) !== null && _a !== void 0 ? _a : ph.sourceSpan));
14179 if (!ph.isVoid) {
14180 ph.children.forEach(child => child.visit(this, context));
14181 context.push(this.createPlaceholderPiece(ph.closeName, (_b = ph.endSourceSpan) !== null && _b !== void 0 ? _b : ph.sourceSpan));
14182 }
14183 }
14184 visitPlaceholder(ph, context) {
14185 context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
14186 }
14187 visitIcuPlaceholder(ph, context) {
14188 context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
14189 }
14190 createPlaceholderPiece(name, sourceSpan) {
14191 return new PlaceholderPiece(formatI18nPlaceholderName(name, /* useCamelCase */ false), sourceSpan);
14192 }
14193 }
14194 const serializerVisitor$2 = new LocalizeSerializerVisitor();
14195 /**
14196 * Serialize an i18n message into two arrays: messageParts and placeholders.
14197 *
14198 * These arrays will be used to generate `$localize` tagged template literals.
14199 *
14200 * @param message The message to be serialized.
14201 * @returns an object containing the messageParts and placeholders.
14202 */
14203 function serializeI18nMessageForLocalize(message) {
14204 const pieces = [];
14205 message.nodes.forEach(node => node.visit(serializerVisitor$2, pieces));
14206 return processMessagePieces(pieces);
14207 }
14208 function getSourceSpan(message) {
14209 const startNode = message.nodes[0];
14210 const endNode = message.nodes[message.nodes.length - 1];
14211 return new ParseSourceSpan(startNode.sourceSpan.start, endNode.sourceSpan.end, startNode.sourceSpan.fullStart, startNode.sourceSpan.details);
14212 }
14213 /**
14214 * Convert the list of serialized MessagePieces into two arrays.
14215 *
14216 * One contains the literal string pieces and the other the placeholders that will be replaced by
14217 * expressions when rendering `$localize` tagged template literals.
14218 *
14219 * @param pieces The pieces to process.
14220 * @returns an object containing the messageParts and placeholders.
14221 */
14222 function processMessagePieces(pieces) {
14223 const messageParts = [];
14224 const placeHolders = [];
14225 if (pieces[0] instanceof PlaceholderPiece) {
14226 // The first piece was a placeholder so we need to add an initial empty message part.
14227 messageParts.push(createEmptyMessagePart(pieces[0].sourceSpan.start));
14228 }
14229 for (let i = 0; i < pieces.length; i++) {
14230 const part = pieces[i];
14231 if (part instanceof LiteralPiece) {
14232 messageParts.push(part);
14233 }
14234 else {
14235 placeHolders.push(part);
14236 if (pieces[i - 1] instanceof PlaceholderPiece) {
14237 // There were two placeholders in a row, so we need to add an empty message part.
14238 messageParts.push(createEmptyMessagePart(pieces[i - 1].sourceSpan.end));
14239 }
14240 }
14241 }
14242 if (pieces[pieces.length - 1] instanceof PlaceholderPiece) {
14243 // The last piece was a placeholder so we need to add a final empty message part.
14244 messageParts.push(createEmptyMessagePart(pieces[pieces.length - 1].sourceSpan.end));
14245 }
14246 return { messageParts, placeHolders };
14247 }
14248 function createEmptyMessagePart(location) {
14249 return new LiteralPiece('', new ParseSourceSpan(location, location));
14250 }
14251
14252 /**
14253 * @license
14254 * Copyright Google LLC All Rights Reserved.
14255 *
14256 * Use of this source code is governed by an MIT-style license that can be
14257 * found in the LICENSE file at https://angular.io/license
14258 */
14259 // Selector attribute name of `<ng-content>`
14260 const NG_CONTENT_SELECT_ATTR$1 = 'select';
14261 // Attribute name of `ngProjectAs`.
14262 const NG_PROJECT_AS_ATTR_NAME = 'ngProjectAs';
14263 // Global symbols available only inside event bindings.
14264 const EVENT_BINDING_SCOPE_GLOBALS = new Set(['$event']);
14265 // List of supported global targets for event listeners
14266 const GLOBAL_TARGET_RESOLVERS = new Map([['window', Identifiers$1.resolveWindow], ['document', Identifiers$1.resolveDocument], ['body', Identifiers$1.resolveBody]]);
14267 const LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t'];
14268 // if (rf & flags) { .. }
14269 function renderFlagCheckIfStmt(flags, statements) {
14270 return ifStmt(variable(RENDER_FLAGS).bitwiseAnd(literal(flags), null, false), statements);
14271 }
14272 function prepareEventListenerParameters(eventAst, handlerName = null, scope = null) {
14273 const { type, name, target, phase, handler } = eventAst;
14274 if (target && !GLOBAL_TARGET_RESOLVERS.has(target)) {
14275 throw new Error(`Unexpected global target '${target}' defined for '${name}' event.
14276 Supported list of global targets: ${Array.from(GLOBAL_TARGET_RESOLVERS.keys())}.`);
14277 }
14278 const eventArgumentName = '$event';
14279 const implicitReceiverAccesses = new Set();
14280 const implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ?
14281 variable(CONTEXT_NAME) :
14282 scope.getOrCreateSharedContextVar(0);
14283 const bindingExpr = convertActionBinding(scope, implicitReceiverExpr, handler, 'b', () => error('Unexpected interpolation'), eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
14284 const statements = [];
14285 if (scope) {
14286 statements.push(...scope.restoreViewStatement());
14287 statements.push(...scope.variableDeclarations());
14288 }
14289 statements.push(...bindingExpr.render3Stmts);
14290 const eventName = type === 1 /* Animation */ ? prepareSyntheticListenerName(name, phase) : name;
14291 const fnName = handlerName && sanitizeIdentifier(handlerName);
14292 const fnArgs = [];
14293 if (implicitReceiverAccesses.has(eventArgumentName)) {
14294 fnArgs.push(new FnParam(eventArgumentName, DYNAMIC_TYPE));
14295 }
14296 const handlerFn = fn(fnArgs, statements, INFERRED_TYPE, null, fnName);
14297 const params = [literal(eventName), handlerFn];
14298 if (target) {
14299 params.push(literal(false), // `useCapture` flag, defaults to `false`
14300 importExpr(GLOBAL_TARGET_RESOLVERS.get(target)));
14301 }
14302 return params;
14303 }
14304 function createComponentDefConsts() {
14305 return {
14306 prepareStatements: [],
14307 constExpressions: [],
14308 i18nVarRefsCache: new Map(),
14309 };
14310 }
14311 class TemplateDefinitionBuilder {
14312 constructor(constantPool, parentBindingScope, level = 0, contextName, i18nContext, templateIndex, templateName, directiveMatcher, directives, pipeTypeByName, pipes, _namespace, relativeContextFilePath, i18nUseExternalIds, _constants = createComponentDefConsts()) {
14313 this.constantPool = constantPool;
14314 this.level = level;
14315 this.contextName = contextName;
14316 this.i18nContext = i18nContext;
14317 this.templateIndex = templateIndex;
14318 this.templateName = templateName;
14319 this.directiveMatcher = directiveMatcher;
14320 this.directives = directives;
14321 this.pipeTypeByName = pipeTypeByName;
14322 this.pipes = pipes;
14323 this._namespace = _namespace;
14324 this.i18nUseExternalIds = i18nUseExternalIds;
14325 this._constants = _constants;
14326 this._dataIndex = 0;
14327 this._bindingContext = 0;
14328 this._prefixCode = [];
14329 /**
14330 * List of callbacks to generate creation mode instructions. We store them here as we process
14331 * the template so bindings in listeners are resolved only once all nodes have been visited.
14332 * This ensures all local refs and context variables are available for matching.
14333 */
14334 this._creationCodeFns = [];
14335 /**
14336 * List of callbacks to generate update mode instructions. We store them here as we process
14337 * the template so bindings are resolved only once all nodes have been visited. This ensures
14338 * all local refs and context variables are available for matching.
14339 */
14340 this._updateCodeFns = [];
14341 /** Index of the currently-selected node. */
14342 this._currentIndex = 0;
14343 /** Temporary variable declarations generated from visiting pipes, literals, etc. */
14344 this._tempVariables = [];
14345 /**
14346 * List of callbacks to build nested templates. Nested templates must not be visited until
14347 * after the parent template has finished visiting all of its nodes. This ensures that all
14348 * local ref bindings in nested templates are able to find local ref values if the refs
14349 * are defined after the template declaration.
14350 */
14351 this._nestedTemplateFns = [];
14352 this._unsupported = unsupported;
14353 // i18n context local to this template
14354 this.i18n = null;
14355 // Number of slots to reserve for pureFunctions
14356 this._pureFunctionSlots = 0;
14357 // Number of binding slots
14358 this._bindingSlots = 0;
14359 // Projection slots found in the template. Projection slots can distribute projected
14360 // nodes based on a selector, or can just use the wildcard selector to match
14361 // all nodes which aren't matching any selector.
14362 this._ngContentReservedSlots = [];
14363 // Number of non-default selectors found in all parent templates of this template. We need to
14364 // track it to properly adjust projection slot index in the `projection` instruction.
14365 this._ngContentSelectorsOffset = 0;
14366 // Expression that should be used as implicit receiver when converting template
14367 // expressions to output AST.
14368 this._implicitReceiverExpr = null;
14369 // These should be handled in the template or element directly.
14370 this.visitReference = invalid$1;
14371 this.visitVariable = invalid$1;
14372 this.visitTextAttribute = invalid$1;
14373 this.visitBoundAttribute = invalid$1;
14374 this.visitBoundEvent = invalid$1;
14375 this._bindingScope = parentBindingScope.nestedScope(level);
14376 // Turn the relative context file path into an identifier by replacing non-alphanumeric
14377 // characters with underscores.
14378 this.fileBasedI18nSuffix = relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_') + '_';
14379 this._valueConverter = new ValueConverter(constantPool, () => this.allocateDataSlot(), (numSlots) => this.allocatePureFunctionSlots(numSlots), (name, localName, slot, value) => {
14380 const pipeType = pipeTypeByName.get(name);
14381 if (pipeType) {
14382 this.pipes.add(pipeType);
14383 }
14384 this._bindingScope.set(this.level, localName, value);
14385 this.creationInstruction(null, Identifiers$1.pipe, [literal(slot), literal(name)]);
14386 });
14387 }
14388 buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n) {
14389 this._ngContentSelectorsOffset = ngContentSelectorsOffset;
14390 if (this._namespace !== Identifiers$1.namespaceHTML) {
14391 this.creationInstruction(null, this._namespace);
14392 }
14393 // Create variable bindings
14394 variables.forEach(v => this.registerContextVariables(v));
14395 // Initiate i18n context in case:
14396 // - this template has parent i18n context
14397 // - or the template has i18n meta associated with it,
14398 // but it's not initiated by the Element (e.g. <ng-template i18n>)
14399 const initI18nContext = this.i18nContext ||
14400 (isI18nRootNode(i18n) && !isSingleI18nIcu(i18n) &&
14401 !(isSingleElementTemplate(nodes) && nodes[0].i18n === i18n));
14402 const selfClosingI18nInstruction = hasTextChildrenOnly(nodes);
14403 if (initI18nContext) {
14404 this.i18nStart(null, i18n, selfClosingI18nInstruction);
14405 }
14406 // This is the initial pass through the nodes of this template. In this pass, we
14407 // queue all creation mode and update mode instructions for generation in the second
14408 // pass. It's necessary to separate the passes to ensure local refs are defined before
14409 // resolving bindings. We also count bindings in this pass as we walk bound expressions.
14410 visitAll(this, nodes);
14411 // Add total binding count to pure function count so pure function instructions are
14412 // generated with the correct slot offset when update instructions are processed.
14413 this._pureFunctionSlots += this._bindingSlots;
14414 // Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
14415 // `pipeBind` update instructions), so we have to update the slot offsets manually
14416 // to account for bindings.
14417 this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
14418 // Nested templates must be processed before creation instructions so template()
14419 // instructions can be generated with the correct internal const count.
14420 this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
14421 // Output the `projectionDef` instruction when some `<ng-content>` tags are present.
14422 // The `projectionDef` instruction is only emitted for the component template and
14423 // is skipped for nested templates (<ng-template> tags).
14424 if (this.level === 0 && this._ngContentReservedSlots.length) {
14425 const parameters = [];
14426 // By default the `projectionDef` instructions creates one slot for the wildcard
14427 // selector if no parameters are passed. Therefore we only want to allocate a new
14428 // array for the projection slots if the default projection slot is not sufficient.
14429 if (this._ngContentReservedSlots.length > 1 || this._ngContentReservedSlots[0] !== '*') {
14430 const r3ReservedSlots = this._ngContentReservedSlots.map(s => s !== '*' ? parseSelectorToR3Selector(s) : s);
14431 parameters.push(this.constantPool.getConstLiteral(asLiteral(r3ReservedSlots), true));
14432 }
14433 // Since we accumulate ngContent selectors while processing template elements,
14434 // we *prepend* `projectionDef` to creation instructions block, to put it before
14435 // any `projection` instructions
14436 this.creationInstruction(null, Identifiers$1.projectionDef, parameters, /* prepend */ true);
14437 }
14438 if (initI18nContext) {
14439 this.i18nEnd(null, selfClosingI18nInstruction);
14440 }
14441 // Generate all the creation mode instructions (e.g. resolve bindings in listeners)
14442 const creationStatements = this._creationCodeFns.map((fn) => fn());
14443 // Generate all the update mode instructions (e.g. resolve property or text bindings)
14444 const updateStatements = this._updateCodeFns.map((fn) => fn());
14445 // Variable declaration must occur after binding resolution so we can generate context
14446 // instructions that build on each other.
14447 // e.g. const b = nextContext().$implicit(); const b = nextContext();
14448 const creationVariables = this._bindingScope.viewSnapshotStatements();
14449 const updateVariables = this._bindingScope.variableDeclarations().concat(this._tempVariables);
14450 const creationBlock = creationStatements.length > 0 ?
14451 [renderFlagCheckIfStmt(1 /* Create */, creationVariables.concat(creationStatements))] :
14452 [];
14453 const updateBlock = updateStatements.length > 0 ?
14454 [renderFlagCheckIfStmt(2 /* Update */, updateVariables.concat(updateStatements))] :
14455 [];
14456 return fn(
14457 // i.e. (rf: RenderFlags, ctx: any)
14458 [new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
14459 // Temporary variable declarations for query refresh (i.e. let _t: any;)
14460 ...this._prefixCode,
14461 // Creating mode (i.e. if (rf & RenderFlags.Create) { ... })
14462 ...creationBlock,
14463 // Binding and refresh mode (i.e. if (rf & RenderFlags.Update) {...})
14464 ...updateBlock,
14465 ], INFERRED_TYPE, null, this.templateName);
14466 }
14467 // LocalResolver
14468 getLocal(name) {
14469 return this._bindingScope.get(name);
14470 }
14471 // LocalResolver
14472 notifyImplicitReceiverUse() {
14473 this._bindingScope.notifyImplicitReceiverUse();
14474 }
14475 i18nTranslate(message, params = {}, ref, transformFn) {
14476 const _ref = ref || this.i18nGenerateMainBlockVar();
14477 // Closure Compiler requires const names to start with `MSG_` but disallows any other const to
14478 // start with `MSG_`. We define a variable starting with `MSG_` just for the `goog.getMsg` call
14479 const closureVar = this.i18nGenerateClosureVar(message.id);
14480 const statements = getTranslationDeclStmts(message, _ref, closureVar, params, transformFn);
14481 this._constants.prepareStatements.push(...statements);
14482 return _ref;
14483 }
14484 registerContextVariables(variable$1) {
14485 const scopedName = this._bindingScope.freshReferenceName();
14486 const retrievalLevel = this.level;
14487 const lhs = variable(variable$1.name + scopedName);
14488 this._bindingScope.set(retrievalLevel, variable$1.name, lhs, 1 /* CONTEXT */, (scope, relativeLevel) => {
14489 let rhs;
14490 if (scope.bindingLevel === retrievalLevel) {
14491 // e.g. ctx
14492 rhs = variable(CONTEXT_NAME);
14493 }
14494 else {
14495 const sharedCtxVar = scope.getSharedContextName(retrievalLevel);
14496 // e.g. ctx_r0 OR x(2);
14497 rhs = sharedCtxVar ? sharedCtxVar : generateNextContextExpr(relativeLevel);
14498 }
14499 // e.g. const $item$ = x(2).$implicit;
14500 return [lhs.set(rhs.prop(variable$1.value || IMPLICIT_REFERENCE)).toConstDecl()];
14501 });
14502 }
14503 i18nAppendBindings(expressions) {
14504 if (expressions.length > 0) {
14505 expressions.forEach(expression => this.i18n.appendBinding(expression));
14506 }
14507 }
14508 i18nBindProps(props) {
14509 const bound = {};
14510 Object.keys(props).forEach(key => {
14511 const prop = props[key];
14512 if (prop instanceof Text) {
14513 bound[key] = literal(prop.value);
14514 }
14515 else {
14516 const value = prop.value.visit(this._valueConverter);
14517 this.allocateBindingSlots(value);
14518 if (value instanceof Interpolation) {
14519 const { strings, expressions } = value;
14520 const { id, bindings } = this.i18n;
14521 const label = assembleI18nBoundString(strings, bindings.size, id);
14522 this.i18nAppendBindings(expressions);
14523 bound[key] = literal(label);
14524 }
14525 }
14526 });
14527 return bound;
14528 }
14529 // Generates top level vars for i18n blocks (i.e. `i18n_N`).
14530 i18nGenerateMainBlockVar() {
14531 return variable(this.constantPool.uniqueName(TRANSLATION_VAR_PREFIX));
14532 }
14533 // Generates vars with Closure-specific names for i18n blocks (i.e. `MSG_XXX`).
14534 i18nGenerateClosureVar(messageId) {
14535 let name;
14536 const suffix = this.fileBasedI18nSuffix.toUpperCase();
14537 if (this.i18nUseExternalIds) {
14538 const prefix = getTranslationConstPrefix(`EXTERNAL_`);
14539 const uniqueSuffix = this.constantPool.uniqueName(suffix);
14540 name = `${prefix}${sanitizeIdentifier(messageId)}$$${uniqueSuffix}`;
14541 }
14542 else {
14543 const prefix = getTranslationConstPrefix(suffix);
14544 name = this.constantPool.uniqueName(prefix);
14545 }
14546 return variable(name);
14547 }
14548 i18nUpdateRef(context) {
14549 const { icus, meta, isRoot, isResolved, isEmitted } = context;
14550 if (isRoot && isResolved && !isEmitted && !isSingleI18nIcu(meta)) {
14551 context.isEmitted = true;
14552 const placeholders = context.getSerializedPlaceholders();
14553 let icuMapping = {};
14554 let params = placeholders.size ? placeholdersToParams(placeholders) : {};
14555 if (icus.size) {
14556 icus.forEach((refs, key) => {
14557 if (refs.length === 1) {
14558 // if we have one ICU defined for a given
14559 // placeholder - just output its reference
14560 params[key] = refs[0];
14561 }
14562 else {
14563 // ... otherwise we need to activate post-processing
14564 // to replace ICU placeholders with proper values
14565 const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
14566 params[key] = literal(placeholder);
14567 icuMapping[key] = literalArr(refs);
14568 }
14569 });
14570 }
14571 // translation requires post processing in 2 cases:
14572 // - if we have placeholders with multiple values (ex. `START_DIV`: [�#1�, �#2�, ...])
14573 // - if we have multiple ICUs that refer to the same placeholder name
14574 const needsPostprocessing = Array.from(placeholders.values()).some((value) => value.length > 1) ||
14575 Object.keys(icuMapping).length;
14576 let transformFn;
14577 if (needsPostprocessing) {
14578 transformFn = (raw) => {
14579 const args = [raw];
14580 if (Object.keys(icuMapping).length) {
14581 args.push(mapLiteral(icuMapping, true));
14582 }
14583 return instruction(null, Identifiers$1.i18nPostprocess, args);
14584 };
14585 }
14586 this.i18nTranslate(meta, params, context.ref, transformFn);
14587 }
14588 }
14589 i18nStart(span = null, meta, selfClosing) {
14590 const index = this.allocateDataSlot();
14591 this.i18n = this.i18nContext ?
14592 this.i18nContext.forkChildContext(index, this.templateIndex, meta) :
14593 new I18nContext(index, this.i18nGenerateMainBlockVar(), 0, this.templateIndex, meta);
14594 // generate i18nStart instruction
14595 const { id, ref } = this.i18n;
14596 const params = [literal(index), this.addToConsts(ref)];
14597 if (id > 0) {
14598 // do not push 3rd argument (sub-block id)
14599 // into i18nStart call for top level i18n context
14600 params.push(literal(id));
14601 }
14602 this.creationInstruction(span, selfClosing ? Identifiers$1.i18n : Identifiers$1.i18nStart, params);
14603 }
14604 i18nEnd(span = null, selfClosing) {
14605 if (!this.i18n) {
14606 throw new Error('i18nEnd is executed with no i18n context present');
14607 }
14608 if (this.i18nContext) {
14609 this.i18nContext.reconcileChildContext(this.i18n);
14610 this.i18nUpdateRef(this.i18nContext);
14611 }
14612 else {
14613 this.i18nUpdateRef(this.i18n);
14614 }
14615 // setup accumulated bindings
14616 const { index, bindings } = this.i18n;
14617 if (bindings.size) {
14618 const chainBindings = [];
14619 bindings.forEach(binding => {
14620 chainBindings.push({ sourceSpan: span, value: () => this.convertPropertyBinding(binding) });
14621 });
14622 // for i18n block, advance to the most recent element index (by taking the current number of
14623 // elements and subtracting one) before invoking `i18nExp` instructions, to make sure the
14624 // necessary lifecycle hooks of components/directives are properly flushed.
14625 this.updateInstructionChainWithAdvance(this.getConstCount() - 1, Identifiers$1.i18nExp, chainBindings);
14626 this.updateInstruction(span, Identifiers$1.i18nApply, [literal(index)]);
14627 }
14628 if (!selfClosing) {
14629 this.creationInstruction(span, Identifiers$1.i18nEnd);
14630 }
14631 this.i18n = null; // reset local i18n context
14632 }
14633 i18nAttributesInstruction(nodeIndex, attrs, sourceSpan) {
14634 let hasBindings = false;
14635 const i18nAttrArgs = [];
14636 const bindings = [];
14637 attrs.forEach(attr => {
14638 const message = attr.i18n;
14639 const converted = attr.value.visit(this._valueConverter);
14640 this.allocateBindingSlots(converted);
14641 if (converted instanceof Interpolation) {
14642 const placeholders = assembleBoundTextPlaceholders(message);
14643 const params = placeholdersToParams(placeholders);
14644 i18nAttrArgs.push(literal(attr.name), this.i18nTranslate(message, params));
14645 converted.expressions.forEach(expression => {
14646 hasBindings = true;
14647 bindings.push({
14648 sourceSpan,
14649 value: () => this.convertPropertyBinding(expression),
14650 });
14651 });
14652 }
14653 });
14654 if (bindings.length > 0) {
14655 this.updateInstructionChainWithAdvance(nodeIndex, Identifiers$1.i18nExp, bindings);
14656 }
14657 if (i18nAttrArgs.length > 0) {
14658 const index = literal(this.allocateDataSlot());
14659 const constIndex = this.addToConsts(literalArr(i18nAttrArgs));
14660 this.creationInstruction(sourceSpan, Identifiers$1.i18nAttributes, [index, constIndex]);
14661 if (hasBindings) {
14662 this.updateInstruction(sourceSpan, Identifiers$1.i18nApply, [index]);
14663 }
14664 }
14665 }
14666 getNamespaceInstruction(namespaceKey) {
14667 switch (namespaceKey) {
14668 case 'math':
14669 return Identifiers$1.namespaceMathML;
14670 case 'svg':
14671 return Identifiers$1.namespaceSVG;
14672 default:
14673 return Identifiers$1.namespaceHTML;
14674 }
14675 }
14676 addNamespaceInstruction(nsInstruction, element) {
14677 this._namespace = nsInstruction;
14678 this.creationInstruction(element.startSourceSpan, nsInstruction);
14679 }
14680 /**
14681 * Adds an update instruction for an interpolated property or attribute, such as
14682 * `prop="{{value}}"` or `attr.title="{{value}}"`
14683 */
14684 interpolatedUpdateInstruction(instruction, elementIndex, attrName, input, value, params) {
14685 this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, instruction, () => [literal(attrName), ...this.getUpdateInstructionArguments(value), ...params]);
14686 }
14687 visitContent(ngContent) {
14688 const slot = this.allocateDataSlot();
14689 const projectionSlotIdx = this._ngContentSelectorsOffset + this._ngContentReservedSlots.length;
14690 const parameters = [literal(slot)];
14691 this._ngContentReservedSlots.push(ngContent.selector);
14692 const nonContentSelectAttributes = ngContent.attributes.filter(attr => attr.name.toLowerCase() !== NG_CONTENT_SELECT_ATTR$1);
14693 const attributes = this.getAttributeExpressions(ngContent.name, nonContentSelectAttributes, [], []);
14694 if (attributes.length > 0) {
14695 parameters.push(literal(projectionSlotIdx), literalArr(attributes));
14696 }
14697 else if (projectionSlotIdx !== 0) {
14698 parameters.push(literal(projectionSlotIdx));
14699 }
14700 this.creationInstruction(ngContent.sourceSpan, Identifiers$1.projection, parameters);
14701 if (this.i18n) {
14702 this.i18n.appendProjection(ngContent.i18n, slot);
14703 }
14704 }
14705 visitElement(element) {
14706 var _a, _b;
14707 const elementIndex = this.allocateDataSlot();
14708 const stylingBuilder = new StylingBuilder(null);
14709 let isNonBindableMode = false;
14710 const isI18nRootElement = isI18nRootNode(element.i18n) && !isSingleI18nIcu(element.i18n);
14711 const outputAttrs = [];
14712 const [namespaceKey, elementName] = splitNsName(element.name);
14713 const isNgContainer$1 = isNgContainer(element.name);
14714 // Handle styling, i18n, ngNonBindable attributes
14715 for (const attr of element.attributes) {
14716 const { name, value } = attr;
14717 if (name === NON_BINDABLE_ATTR) {
14718 isNonBindableMode = true;
14719 }
14720 else if (name === 'style') {
14721 stylingBuilder.registerStyleAttr(value);
14722 }
14723 else if (name === 'class') {
14724 stylingBuilder.registerClassAttr(value);
14725 }
14726 else {
14727 outputAttrs.push(attr);
14728 }
14729 }
14730 // Match directives on non i18n attributes
14731 this.matchDirectives(element.name, element);
14732 // Regular element or ng-container creation mode
14733 const parameters = [literal(elementIndex)];
14734 if (!isNgContainer$1) {
14735 parameters.push(literal(elementName));
14736 }
14737 // Add the attributes
14738 const allOtherInputs = [];
14739 const boundI18nAttrs = [];
14740 element.inputs.forEach(input => {
14741 const stylingInputWasSet = stylingBuilder.registerBoundInput(input);
14742 if (!stylingInputWasSet) {
14743 if (input.type === 0 /* Property */ && input.i18n) {
14744 boundI18nAttrs.push(input);
14745 }
14746 else {
14747 allOtherInputs.push(input);
14748 }
14749 }
14750 });
14751 // add attributes for directive and projection matching purposes
14752 const attributes = this.getAttributeExpressions(element.name, outputAttrs, allOtherInputs, element.outputs, stylingBuilder, [], boundI18nAttrs);
14753 parameters.push(this.addAttrsToConsts(attributes));
14754 // local refs (ex.: <div #foo #bar="baz">)
14755 const refs = this.prepareRefsArray(element.references);
14756 parameters.push(this.addToConsts(refs));
14757 const wasInNamespace = this._namespace;
14758 const currentNamespace = this.getNamespaceInstruction(namespaceKey);
14759 // If the namespace is changing now, include an instruction to change it
14760 // during element creation.
14761 if (currentNamespace !== wasInNamespace) {
14762 this.addNamespaceInstruction(currentNamespace, element);
14763 }
14764 if (this.i18n) {
14765 this.i18n.appendElement(element.i18n, elementIndex);
14766 }
14767 // Note that we do not append text node instructions and ICUs inside i18n section,
14768 // so we exclude them while calculating whether current element has children
14769 const hasChildren = (!isI18nRootElement && this.i18n) ? !hasTextChildrenOnly(element.children) :
14770 element.children.length > 0;
14771 const createSelfClosingInstruction = !stylingBuilder.hasBindingsWithPipes &&
14772 element.outputs.length === 0 && boundI18nAttrs.length === 0 && !hasChildren;
14773 const createSelfClosingI18nInstruction = !createSelfClosingInstruction && hasTextChildrenOnly(element.children);
14774 if (createSelfClosingInstruction) {
14775 this.creationInstruction(element.sourceSpan, isNgContainer$1 ? Identifiers$1.elementContainer : Identifiers$1.element, trimTrailingNulls(parameters));
14776 }
14777 else {
14778 this.creationInstruction(element.startSourceSpan, isNgContainer$1 ? Identifiers$1.elementContainerStart : Identifiers$1.elementStart, trimTrailingNulls(parameters));
14779 if (isNonBindableMode) {
14780 this.creationInstruction(element.startSourceSpan, Identifiers$1.disableBindings);
14781 }
14782 if (boundI18nAttrs.length > 0) {
14783 this.i18nAttributesInstruction(elementIndex, boundI18nAttrs, (_a = element.startSourceSpan) !== null && _a !== void 0 ? _a : element.sourceSpan);
14784 }
14785 // Generate Listeners (outputs)
14786 if (element.outputs.length > 0) {
14787 const listeners = element.outputs.map((outputAst) => ({
14788 sourceSpan: outputAst.sourceSpan,
14789 params: this.prepareListenerParameter(element.name, outputAst, elementIndex)
14790 }));
14791 this.creationInstructionChain(Identifiers$1.listener, listeners);
14792 }
14793 // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
14794 // listeners, to make sure i18nAttributes instruction targets current element at runtime.
14795 if (isI18nRootElement) {
14796 this.i18nStart(element.startSourceSpan, element.i18n, createSelfClosingI18nInstruction);
14797 }
14798 }
14799 // the code here will collect all update-level styling instructions and add them to the
14800 // update block of the template function AOT code. Instructions like `styleProp`,
14801 // `styleMap`, `classMap`, `classProp`
14802 // are all generated and assigned in the code below.
14803 const stylingInstructions = stylingBuilder.buildUpdateLevelInstructions(this._valueConverter);
14804 const limit = stylingInstructions.length - 1;
14805 for (let i = 0; i <= limit; i++) {
14806 const instruction = stylingInstructions[i];
14807 this._bindingSlots += this.processStylingUpdateInstruction(elementIndex, instruction);
14808 }
14809 // the reason why `undefined` is used is because the renderer understands this as a
14810 // special value to symbolize that there is no RHS to this binding
14811 // TODO (matsko): revisit this once FW-959 is approached
14812 const emptyValueBindInstruction = literal(undefined);
14813 const propertyBindings = [];
14814 const attributeBindings = [];
14815 // Generate element input bindings
14816 allOtherInputs.forEach(input => {
14817 const inputType = input.type;
14818 if (inputType === 4 /* Animation */) {
14819 const value = input.value.visit(this._valueConverter);
14820 // animation bindings can be presented in the following formats:
14821 // 1. [@binding]="fooExp"
14822 // 2. [@binding]="{value:fooExp, params:{...}}"
14823 // 3. [@binding]
14824 // 4. @binding
14825 // All formats will be valid for when a synthetic binding is created.
14826 // The reasoning for this is because the renderer should get each
14827 // synthetic binding value in the order of the array that they are
14828 // defined in...
14829 const hasValue = value instanceof LiteralPrimitive ? !!value.value : true;
14830 this.allocateBindingSlots(value);
14831 propertyBindings.push({
14832 name: prepareSyntheticPropertyName(input.name),
14833 sourceSpan: input.sourceSpan,
14834 value: () => hasValue ? this.convertPropertyBinding(value) : emptyValueBindInstruction
14835 });
14836 }
14837 else {
14838 // we must skip attributes with associated i18n context, since these attributes are handled
14839 // separately and corresponding `i18nExp` and `i18nApply` instructions will be generated
14840 if (input.i18n)
14841 return;
14842 const value = input.value.visit(this._valueConverter);
14843 if (value !== undefined) {
14844 const params = [];
14845 const [attrNamespace, attrName] = splitNsName(input.name);
14846 const isAttributeBinding = inputType === 1 /* Attribute */;
14847 const sanitizationRef = resolveSanitizationFn(input.securityContext, isAttributeBinding);
14848 if (sanitizationRef)
14849 params.push(sanitizationRef);
14850 if (attrNamespace) {
14851 const namespaceLiteral = literal(attrNamespace);
14852 if (sanitizationRef) {
14853 params.push(namespaceLiteral);
14854 }
14855 else {
14856 // If there wasn't a sanitization ref, we need to add
14857 // an extra param so that we can pass in the namespace.
14858 params.push(literal(null), namespaceLiteral);
14859 }
14860 }
14861 this.allocateBindingSlots(value);
14862 if (inputType === 0 /* Property */) {
14863 if (value instanceof Interpolation) {
14864 // prop="{{value}}" and friends
14865 this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), elementIndex, attrName, input, value, params);
14866 }
14867 else {
14868 // [prop]="value"
14869 // Collect all the properties so that we can chain into a single function at the end.
14870 propertyBindings.push({
14871 name: attrName,
14872 sourceSpan: input.sourceSpan,
14873 value: () => this.convertPropertyBinding(value),
14874 params
14875 });
14876 }
14877 }
14878 else if (inputType === 1 /* Attribute */) {
14879 if (value instanceof Interpolation && getInterpolationArgsLength(value) > 1) {
14880 // attr.name="text{{value}}" and friends
14881 this.interpolatedUpdateInstruction(getAttributeInterpolationExpression(value), elementIndex, attrName, input, value, params);
14882 }
14883 else {
14884 const boundValue = value instanceof Interpolation ? value.expressions[0] : value;
14885 // [attr.name]="value" or attr.name="{{value}}"
14886 // Collect the attribute bindings so that they can be chained at the end.
14887 attributeBindings.push({
14888 name: attrName,
14889 sourceSpan: input.sourceSpan,
14890 value: () => this.convertPropertyBinding(boundValue),
14891 params
14892 });
14893 }
14894 }
14895 else {
14896 // class prop
14897 this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, Identifiers$1.classProp, () => {
14898 return [
14899 literal(elementIndex), literal(attrName), this.convertPropertyBinding(value),
14900 ...params
14901 ];
14902 });
14903 }
14904 }
14905 }
14906 });
14907 if (propertyBindings.length > 0) {
14908 this.updateInstructionChainWithAdvance(elementIndex, Identifiers$1.property, propertyBindings);
14909 }
14910 if (attributeBindings.length > 0) {
14911 this.updateInstructionChainWithAdvance(elementIndex, Identifiers$1.attribute, attributeBindings);
14912 }
14913 // Traverse element child nodes
14914 visitAll(this, element.children);
14915 if (!isI18nRootElement && this.i18n) {
14916 this.i18n.appendElement(element.i18n, elementIndex, true);
14917 }
14918 if (!createSelfClosingInstruction) {
14919 // Finish element construction mode.
14920 const span = (_b = element.endSourceSpan) !== null && _b !== void 0 ? _b : element.sourceSpan;
14921 if (isI18nRootElement) {
14922 this.i18nEnd(span, createSelfClosingI18nInstruction);
14923 }
14924 if (isNonBindableMode) {
14925 this.creationInstruction(span, Identifiers$1.enableBindings);
14926 }
14927 this.creationInstruction(span, isNgContainer$1 ? Identifiers$1.elementContainerEnd : Identifiers$1.elementEnd);
14928 }
14929 }
14930 visitTemplate(template) {
14931 var _a;
14932 const NG_TEMPLATE_TAG_NAME = 'ng-template';
14933 const templateIndex = this.allocateDataSlot();
14934 if (this.i18n) {
14935 this.i18n.appendTemplate(template.i18n, templateIndex);
14936 }
14937 const tagName = sanitizeIdentifier(template.tagName || '');
14938 const contextName = `${this.contextName}${tagName ? '_' + tagName : ''}_${templateIndex}`;
14939 const templateName = `${contextName}_Template`;
14940 const parameters = [
14941 literal(templateIndex),
14942 variable(templateName),
14943 // We don't care about the tag's namespace here, because we infer
14944 // it based on the parent nodes inside the template instruction.
14945 literal(template.tagName ? splitNsName(template.tagName)[1] : template.tagName),
14946 ];
14947 // find directives matching on a given <ng-template> node
14948 this.matchDirectives(NG_TEMPLATE_TAG_NAME, template);
14949 // prepare attributes parameter (including attributes used for directive matching)
14950 const attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs);
14951 parameters.push(this.addAttrsToConsts(attrsExprs));
14952 // local refs (ex.: <ng-template #foo>)
14953 if (template.references && template.references.length) {
14954 const refs = this.prepareRefsArray(template.references);
14955 parameters.push(this.addToConsts(refs));
14956 parameters.push(importExpr(Identifiers$1.templateRefExtractor));
14957 }
14958 // Create the template function
14959 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);
14960 // Nested templates must not be visited until after their parent templates have completed
14961 // processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
14962 // be able to support bindings in nested templates to local refs that occur after the
14963 // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div>
14964 this._nestedTemplateFns.push(() => {
14965 const templateFunctionExpr = templateVisitor.buildTemplateFunction(template.children, template.variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, template.i18n);
14966 this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName));
14967 if (templateVisitor._ngContentReservedSlots.length) {
14968 this._ngContentReservedSlots.push(...templateVisitor._ngContentReservedSlots);
14969 }
14970 });
14971 // e.g. template(1, MyComp_Template_1)
14972 this.creationInstruction(template.sourceSpan, Identifiers$1.templateCreate, () => {
14973 parameters.splice(2, 0, literal(templateVisitor.getConstCount()), literal(templateVisitor.getVarCount()));
14974 return trimTrailingNulls(parameters);
14975 });
14976 // handle property bindings e.g. ɵɵproperty('ngForOf', ctx.items), et al;
14977 this.templatePropertyBindings(templateIndex, template.templateAttrs);
14978 // Only add normal input/output binding instructions on explicit <ng-template> elements.
14979 if (template.tagName === NG_TEMPLATE_TAG_NAME) {
14980 const [i18nInputs, inputs] = partitionArray(template.inputs, hasI18nMeta);
14981 // Add i18n attributes that may act as inputs to directives. If such attributes are present,
14982 // generate `i18nAttributes` instruction. Note: we generate it only for explicit <ng-template>
14983 // elements, in case of inline templates, corresponding instructions will be generated in the
14984 // nested template function.
14985 if (i18nInputs.length > 0) {
14986 this.i18nAttributesInstruction(templateIndex, i18nInputs, (_a = template.startSourceSpan) !== null && _a !== void 0 ? _a : template.sourceSpan);
14987 }
14988 // Add the input bindings
14989 if (inputs.length > 0) {
14990 this.templatePropertyBindings(templateIndex, inputs);
14991 }
14992 // Generate listeners for directive output
14993 if (template.outputs.length > 0) {
14994 const listeners = template.outputs.map((outputAst) => ({
14995 sourceSpan: outputAst.sourceSpan,
14996 params: this.prepareListenerParameter('ng_template', outputAst, templateIndex)
14997 }));
14998 this.creationInstructionChain(Identifiers$1.listener, listeners);
14999 }
15000 }
15001 }
15002 visitBoundText(text) {
15003 if (this.i18n) {
15004 const value = text.value.visit(this._valueConverter);
15005 this.allocateBindingSlots(value);
15006 if (value instanceof Interpolation) {
15007 this.i18n.appendBoundText(text.i18n);
15008 this.i18nAppendBindings(value.expressions);
15009 }
15010 return;
15011 }
15012 const nodeIndex = this.allocateDataSlot();
15013 this.creationInstruction(text.sourceSpan, Identifiers$1.text, [literal(nodeIndex)]);
15014 const value = text.value.visit(this._valueConverter);
15015 this.allocateBindingSlots(value);
15016 if (value instanceof Interpolation) {
15017 this.updateInstructionWithAdvance(nodeIndex, text.sourceSpan, getTextInterpolationExpression(value), () => this.getUpdateInstructionArguments(value));
15018 }
15019 else {
15020 error('Text nodes should be interpolated and never bound directly.');
15021 }
15022 }
15023 visitText(text) {
15024 // when a text element is located within a translatable
15025 // block, we exclude this text element from instructions set,
15026 // since it will be captured in i18n content and processed at runtime
15027 if (!this.i18n) {
15028 this.creationInstruction(text.sourceSpan, Identifiers$1.text, [literal(this.allocateDataSlot()), literal(text.value)]);
15029 }
15030 }
15031 visitIcu(icu) {
15032 let initWasInvoked = false;
15033 // if an ICU was created outside of i18n block, we still treat
15034 // it as a translatable entity and invoke i18nStart and i18nEnd
15035 // to generate i18n context and the necessary instructions
15036 if (!this.i18n) {
15037 initWasInvoked = true;
15038 this.i18nStart(null, icu.i18n, true);
15039 }
15040 const i18n = this.i18n;
15041 const vars = this.i18nBindProps(icu.vars);
15042 const placeholders = this.i18nBindProps(icu.placeholders);
15043 // output ICU directly and keep ICU reference in context
15044 const message = icu.i18n;
15045 // we always need post-processing function for ICUs, to make sure that:
15046 // - all placeholders in a form of {PLACEHOLDER} are replaced with actual values (note:
15047 // `goog.getMsg` does not process ICUs and uses the `{PLACEHOLDER}` format for placeholders
15048 // inside ICUs)
15049 // - all ICU vars (such as `VAR_SELECT` or `VAR_PLURAL`) are replaced with correct values
15050 const transformFn = (raw) => {
15051 const params = Object.assign(Object.assign({}, vars), placeholders);
15052 const formatted = i18nFormatPlaceholderNames(params, /* useCamelCase */ false);
15053 return instruction(null, Identifiers$1.i18nPostprocess, [raw, mapLiteral(formatted, true)]);
15054 };
15055 // in case the whole i18n message is a single ICU - we do not need to
15056 // create a separate top-level translation, we can use the root ref instead
15057 // and make this ICU a top-level translation
15058 // note: ICU placeholders are replaced with actual values in `i18nPostprocess` function
15059 // separately, so we do not pass placeholders into `i18nTranslate` function.
15060 if (isSingleI18nIcu(i18n.meta)) {
15061 this.i18nTranslate(message, /* placeholders */ {}, i18n.ref, transformFn);
15062 }
15063 else {
15064 // output ICU directly and keep ICU reference in context
15065 const ref = this.i18nTranslate(message, /* placeholders */ {}, /* ref */ undefined, transformFn);
15066 i18n.appendIcu(icuFromI18nMessage(message).name, ref);
15067 }
15068 if (initWasInvoked) {
15069 this.i18nEnd(null, true);
15070 }
15071 return null;
15072 }
15073 allocateDataSlot() {
15074 return this._dataIndex++;
15075 }
15076 getConstCount() {
15077 return this._dataIndex;
15078 }
15079 getVarCount() {
15080 return this._pureFunctionSlots;
15081 }
15082 getConsts() {
15083 return this._constants;
15084 }
15085 getNgContentSelectors() {
15086 return this._ngContentReservedSlots.length ?
15087 this.constantPool.getConstLiteral(asLiteral(this._ngContentReservedSlots), true) :
15088 null;
15089 }
15090 bindingContext() {
15091 return `${this._bindingContext++}`;
15092 }
15093 templatePropertyBindings(templateIndex, attrs) {
15094 const propertyBindings = [];
15095 attrs.forEach(input => {
15096 if (input instanceof BoundAttribute) {
15097 const value = input.value.visit(this._valueConverter);
15098 if (value !== undefined) {
15099 this.allocateBindingSlots(value);
15100 if (value instanceof Interpolation) {
15101 // Params typically contain attribute namespace and value sanitizer, which is applicable
15102 // for regular HTML elements, but not applicable for <ng-template> (since props act as
15103 // inputs to directives), so keep params array empty.
15104 const params = [];
15105 // prop="{{value}}" case
15106 this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), templateIndex, input.name, input, value, params);
15107 }
15108 else {
15109 // [prop]="value" case
15110 propertyBindings.push({
15111 name: input.name,
15112 sourceSpan: input.sourceSpan,
15113 value: () => this.convertPropertyBinding(value)
15114 });
15115 }
15116 }
15117 }
15118 });
15119 if (propertyBindings.length > 0) {
15120 this.updateInstructionChainWithAdvance(templateIndex, Identifiers$1.property, propertyBindings);
15121 }
15122 }
15123 // Bindings must only be resolved after all local refs have been visited, so all
15124 // instructions are queued in callbacks that execute once the initial pass has completed.
15125 // Otherwise, we wouldn't be able to support local refs that are defined after their
15126 // bindings. e.g. {{ foo }} <div #foo></div>
15127 instructionFn(fns, span, reference, paramsOrFn, prepend = false) {
15128 fns[prepend ? 'unshift' : 'push'](() => {
15129 const params = Array.isArray(paramsOrFn) ? paramsOrFn : paramsOrFn();
15130 return instruction(span, reference, params).toStmt();
15131 });
15132 }
15133 processStylingUpdateInstruction(elementIndex, instruction) {
15134 let allocateBindingSlots = 0;
15135 if (instruction) {
15136 const calls = [];
15137 instruction.calls.forEach(call => {
15138 allocateBindingSlots += call.allocateBindingSlots;
15139 calls.push({
15140 sourceSpan: call.sourceSpan,
15141 value: () => {
15142 return call.params(value => (call.supportsInterpolation && value instanceof Interpolation) ?
15143 this.getUpdateInstructionArguments(value) :
15144 this.convertPropertyBinding(value));
15145 }
15146 });
15147 });
15148 this.updateInstructionChainWithAdvance(elementIndex, instruction.reference, calls);
15149 }
15150 return allocateBindingSlots;
15151 }
15152 creationInstruction(span, reference, paramsOrFn, prepend) {
15153 this.instructionFn(this._creationCodeFns, span, reference, paramsOrFn || [], prepend);
15154 }
15155 creationInstructionChain(reference, calls) {
15156 const span = calls.length ? calls[0].sourceSpan : null;
15157 this._creationCodeFns.push(() => {
15158 return chainedInstruction(reference, calls.map(call => call.params()), span).toStmt();
15159 });
15160 }
15161 updateInstructionWithAdvance(nodeIndex, span, reference, paramsOrFn) {
15162 this.addAdvanceInstructionIfNecessary(nodeIndex, span);
15163 this.updateInstruction(span, reference, paramsOrFn);
15164 }
15165 updateInstruction(span, reference, paramsOrFn) {
15166 this.instructionFn(this._updateCodeFns, span, reference, paramsOrFn || []);
15167 }
15168 updateInstructionChain(reference, bindings) {
15169 const span = bindings.length ? bindings[0].sourceSpan : null;
15170 this._updateCodeFns.push(() => {
15171 const calls = bindings.map(property => {
15172 const value = property.value();
15173 const fnParams = Array.isArray(value) ? value : [value];
15174 if (property.params) {
15175 fnParams.push(...property.params);
15176 }
15177 if (property.name) {
15178 // We want the property name to always be the first function parameter.
15179 fnParams.unshift(literal(property.name));
15180 }
15181 return fnParams;
15182 });
15183 return chainedInstruction(reference, calls, span).toStmt();
15184 });
15185 }
15186 updateInstructionChainWithAdvance(nodeIndex, reference, bindings) {
15187 this.addAdvanceInstructionIfNecessary(nodeIndex, bindings.length ? bindings[0].sourceSpan : null);
15188 this.updateInstructionChain(reference, bindings);
15189 }
15190 addAdvanceInstructionIfNecessary(nodeIndex, span) {
15191 if (nodeIndex !== this._currentIndex) {
15192 const delta = nodeIndex - this._currentIndex;
15193 if (delta < 1) {
15194 throw new Error('advance instruction can only go forwards');
15195 }
15196 this.instructionFn(this._updateCodeFns, span, Identifiers$1.advance, [literal(delta)]);
15197 this._currentIndex = nodeIndex;
15198 }
15199 }
15200 allocatePureFunctionSlots(numSlots) {
15201 const originalSlots = this._pureFunctionSlots;
15202 this._pureFunctionSlots += numSlots;
15203 return originalSlots;
15204 }
15205 allocateBindingSlots(value) {
15206 this._bindingSlots += value instanceof Interpolation ? value.expressions.length : 1;
15207 }
15208 /**
15209 * Gets an expression that refers to the implicit receiver. The implicit
15210 * receiver is always the root level context.
15211 */
15212 getImplicitReceiverExpr() {
15213 if (this._implicitReceiverExpr) {
15214 return this._implicitReceiverExpr;
15215 }
15216 return this._implicitReceiverExpr = this.level === 0 ?
15217 variable(CONTEXT_NAME) :
15218 this._bindingScope.getOrCreateSharedContextVar(0);
15219 }
15220 convertPropertyBinding(value) {
15221 const convertedPropertyBinding = convertPropertyBinding(this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.Expression, () => error('Unexpected interpolation'));
15222 const valExpr = convertedPropertyBinding.currValExpr;
15223 this._tempVariables.push(...convertedPropertyBinding.stmts);
15224 return valExpr;
15225 }
15226 /**
15227 * Gets a list of argument expressions to pass to an update instruction expression. Also updates
15228 * the temp variables state with temp variables that were identified as needing to be created
15229 * while visiting the arguments.
15230 * @param value The original expression we will be resolving an arguments list from.
15231 */
15232 getUpdateInstructionArguments(value) {
15233 const { args, stmts } = convertUpdateArguments(this, this.getImplicitReceiverExpr(), value, this.bindingContext());
15234 this._tempVariables.push(...stmts);
15235 return args;
15236 }
15237 matchDirectives(elementName, elOrTpl) {
15238 if (this.directiveMatcher) {
15239 const selector = createCssSelector(elementName, getAttrsForDirectiveMatching(elOrTpl));
15240 this.directiveMatcher.match(selector, (cssSelector, staticType) => {
15241 this.directives.add(staticType);
15242 });
15243 }
15244 }
15245 /**
15246 * Prepares all attribute expression values for the `TAttributes` array.
15247 *
15248 * The purpose of this function is to properly construct an attributes array that
15249 * is passed into the `elementStart` (or just `element`) functions. Because there
15250 * are many different types of attributes, the array needs to be constructed in a
15251 * special way so that `elementStart` can properly evaluate them.
15252 *
15253 * The format looks like this:
15254 *
15255 * ```
15256 * attrs = [prop, value, prop2, value2,
15257 * PROJECT_AS, selector,
15258 * CLASSES, class1, class2,
15259 * STYLES, style1, value1, style2, value2,
15260 * BINDINGS, name1, name2, name3,
15261 * TEMPLATE, name4, name5, name6,
15262 * I18N, name7, name8, ...]
15263 * ```
15264 *
15265 * Note that this function will fully ignore all synthetic (@foo) attribute values
15266 * because those values are intended to always be generated as property instructions.
15267 */
15268 getAttributeExpressions(elementName, renderAttributes, inputs, outputs, styles, templateAttrs = [], boundI18nAttrs = []) {
15269 const alreadySeen = new Set();
15270 const attrExprs = [];
15271 let ngProjectAsAttr;
15272 for (const attr of renderAttributes) {
15273 if (attr.name === NG_PROJECT_AS_ATTR_NAME) {
15274 ngProjectAsAttr = attr;
15275 }
15276 // Note that static i18n attributes aren't in the i18n array,
15277 // because they're treated in the same way as regular attributes.
15278 if (attr.i18n) {
15279 // When i18n attributes are present on elements with structural directives
15280 // (e.g. `<div *ngIf title="Hello" i18n-title>`), we want to avoid generating
15281 // duplicate i18n translation blocks for `ɵɵtemplate` and `ɵɵelement` instruction
15282 // attributes. So we do a cache lookup to see if suitable i18n translation block
15283 // already exists.
15284 const { i18nVarRefsCache } = this._constants;
15285 let i18nVarRef;
15286 if (i18nVarRefsCache.has(attr.i18n)) {
15287 i18nVarRef = i18nVarRefsCache.get(attr.i18n);
15288 }
15289 else {
15290 i18nVarRef = this.i18nTranslate(attr.i18n);
15291 i18nVarRefsCache.set(attr.i18n, i18nVarRef);
15292 }
15293 attrExprs.push(literal(attr.name), i18nVarRef);
15294 }
15295 else {
15296 attrExprs.push(...getAttributeNameLiterals(attr.name), trustedConstAttribute(elementName, attr));
15297 }
15298 }
15299 // Keep ngProjectAs next to the other name, value pairs so we can verify that we match
15300 // ngProjectAs marker in the attribute name slot.
15301 if (ngProjectAsAttr) {
15302 attrExprs.push(...getNgProjectAsLiteral(ngProjectAsAttr));
15303 }
15304 function addAttrExpr(key, value) {
15305 if (typeof key === 'string') {
15306 if (!alreadySeen.has(key)) {
15307 attrExprs.push(...getAttributeNameLiterals(key));
15308 value !== undefined && attrExprs.push(value);
15309 alreadySeen.add(key);
15310 }
15311 }
15312 else {
15313 attrExprs.push(literal(key));
15314 }
15315 }
15316 // it's important that this occurs before BINDINGS and TEMPLATE because once `elementStart`
15317 // comes across the BINDINGS or TEMPLATE markers then it will continue reading each value as
15318 // as single property value cell by cell.
15319 if (styles) {
15320 styles.populateInitialStylingAttrs(attrExprs);
15321 }
15322 if (inputs.length || outputs.length) {
15323 const attrsLengthBeforeInputs = attrExprs.length;
15324 for (let i = 0; i < inputs.length; i++) {
15325 const input = inputs[i];
15326 // We don't want the animation and attribute bindings in the
15327 // attributes array since they aren't used for directive matching.
15328 if (input.type !== 4 /* Animation */ && input.type !== 1 /* Attribute */) {
15329 addAttrExpr(input.name);
15330 }
15331 }
15332 for (let i = 0; i < outputs.length; i++) {
15333 const output = outputs[i];
15334 if (output.type !== 1 /* Animation */) {
15335 addAttrExpr(output.name);
15336 }
15337 }
15338 // this is a cheap way of adding the marker only after all the input/output
15339 // values have been filtered (by not including the animation ones) and added
15340 // to the expressions. The marker is important because it tells the runtime
15341 // code that this is where attributes without values start...
15342 if (attrExprs.length !== attrsLengthBeforeInputs) {
15343 attrExprs.splice(attrsLengthBeforeInputs, 0, literal(3 /* Bindings */));
15344 }
15345 }
15346 if (templateAttrs.length) {
15347 attrExprs.push(literal(4 /* Template */));
15348 templateAttrs.forEach(attr => addAttrExpr(attr.name));
15349 }
15350 if (boundI18nAttrs.length) {
15351 attrExprs.push(literal(6 /* I18n */));
15352 boundI18nAttrs.forEach(attr => addAttrExpr(attr.name));
15353 }
15354 return attrExprs;
15355 }
15356 addToConsts(expression) {
15357 if (isNull(expression)) {
15358 return TYPED_NULL_EXPR;
15359 }
15360 const consts = this._constants.constExpressions;
15361 // Try to reuse a literal that's already in the array, if possible.
15362 for (let i = 0; i < consts.length; i++) {
15363 if (consts[i].isEquivalent(expression)) {
15364 return literal(i);
15365 }
15366 }
15367 return literal(consts.push(expression) - 1);
15368 }
15369 addAttrsToConsts(attrs) {
15370 return attrs.length > 0 ? this.addToConsts(literalArr(attrs)) : TYPED_NULL_EXPR;
15371 }
15372 prepareRefsArray(references) {
15373 if (!references || references.length === 0) {
15374 return TYPED_NULL_EXPR;
15375 }
15376 const refsParam = flatten(references.map(reference => {
15377 const slot = this.allocateDataSlot();
15378 // Generate the update temporary.
15379 const variableName = this._bindingScope.freshReferenceName();
15380 const retrievalLevel = this.level;
15381 const lhs = variable(variableName);
15382 this._bindingScope.set(retrievalLevel, reference.name, lhs, 0 /* DEFAULT */, (scope, relativeLevel) => {
15383 // e.g. nextContext(2);
15384 const nextContextStmt = relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : [];
15385 // e.g. const $foo$ = reference(1);
15386 const refExpr = lhs.set(importExpr(Identifiers$1.reference).callFn([literal(slot)]));
15387 return nextContextStmt.concat(refExpr.toConstDecl());
15388 }, true);
15389 return [reference.name, reference.value];
15390 }));
15391 return asLiteral(refsParam);
15392 }
15393 prepareListenerParameter(tagName, outputAst, index) {
15394 return () => {
15395 const eventName = outputAst.name;
15396 const bindingFnName = outputAst.type === 1 /* Animation */ ?
15397 // synthetic @listener.foo values are treated the exact same as are standard listeners
15398 prepareSyntheticListenerFunctionName(eventName, outputAst.phase) :
15399 sanitizeIdentifier(eventName);
15400 const handlerName = `${this.templateName}_${tagName}_${bindingFnName}_${index}_listener`;
15401 const scope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel, EVENT_BINDING_SCOPE_GLOBALS);
15402 return prepareEventListenerParameters(outputAst, handlerName, scope);
15403 };
15404 }
15405 }
15406 class ValueConverter extends AstMemoryEfficientTransformer {
15407 constructor(constantPool, allocateSlot, allocatePureFunctionSlots, definePipe) {
15408 super();
15409 this.constantPool = constantPool;
15410 this.allocateSlot = allocateSlot;
15411 this.allocatePureFunctionSlots = allocatePureFunctionSlots;
15412 this.definePipe = definePipe;
15413 this._pipeBindExprs = [];
15414 }
15415 // AstMemoryEfficientTransformer
15416 visitPipe(pipe, context) {
15417 // Allocate a slot to create the pipe
15418 const slot = this.allocateSlot();
15419 const slotPseudoLocal = `PIPE:${slot}`;
15420 // Allocate one slot for the result plus one slot per pipe argument
15421 const pureFunctionSlot = this.allocatePureFunctionSlots(2 + pipe.args.length);
15422 const target = new PropertyRead(pipe.span, pipe.sourceSpan, pipe.nameSpan, new ImplicitReceiver(pipe.span, pipe.sourceSpan), slotPseudoLocal);
15423 const { identifier, isVarLength } = pipeBindingCallInfo(pipe.args);
15424 this.definePipe(pipe.name, slotPseudoLocal, slot, importExpr(identifier));
15425 const args = [pipe.exp, ...pipe.args];
15426 const convertedArgs = isVarLength ?
15427 this.visitAll([new LiteralArray(pipe.span, pipe.sourceSpan, args)]) :
15428 this.visitAll(args);
15429 const pipeBindExpr = new FunctionCall(pipe.span, pipe.sourceSpan, target, [
15430 new LiteralPrimitive(pipe.span, pipe.sourceSpan, slot),
15431 new LiteralPrimitive(pipe.span, pipe.sourceSpan, pureFunctionSlot),
15432 ...convertedArgs,
15433 ]);
15434 this._pipeBindExprs.push(pipeBindExpr);
15435 return pipeBindExpr;
15436 }
15437 updatePipeSlotOffsets(bindingSlots) {
15438 this._pipeBindExprs.forEach((pipe) => {
15439 // update the slot offset arg (index 1) to account for binding slots
15440 const slotOffset = pipe.args[1];
15441 slotOffset.value += bindingSlots;
15442 });
15443 }
15444 visitLiteralArray(array, context) {
15445 return new BuiltinFunctionCall(array.span, array.sourceSpan, this.visitAll(array.expressions), values => {
15446 // If the literal has calculated (non-literal) elements transform it into
15447 // calls to literal factories that compose the literal and will cache intermediate
15448 // values.
15449 const literal = literalArr(values);
15450 return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
15451 });
15452 }
15453 visitLiteralMap(map, context) {
15454 return new BuiltinFunctionCall(map.span, map.sourceSpan, this.visitAll(map.values), values => {
15455 // If the literal has calculated (non-literal) elements transform it into
15456 // calls to literal factories that compose the literal and will cache intermediate
15457 // values.
15458 const literal = literalMap(values.map((value, index) => ({ key: map.keys[index].key, value, quoted: map.keys[index].quoted })));
15459 return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
15460 });
15461 }
15462 }
15463 // Pipes always have at least one parameter, the value they operate on
15464 const pipeBindingIdentifiers = [Identifiers$1.pipeBind1, Identifiers$1.pipeBind2, Identifiers$1.pipeBind3, Identifiers$1.pipeBind4];
15465 function pipeBindingCallInfo(args) {
15466 const identifier = pipeBindingIdentifiers[args.length];
15467 return {
15468 identifier: identifier || Identifiers$1.pipeBindV,
15469 isVarLength: !identifier,
15470 };
15471 }
15472 const pureFunctionIdentifiers = [
15473 Identifiers$1.pureFunction0, Identifiers$1.pureFunction1, Identifiers$1.pureFunction2, Identifiers$1.pureFunction3, Identifiers$1.pureFunction4,
15474 Identifiers$1.pureFunction5, Identifiers$1.pureFunction6, Identifiers$1.pureFunction7, Identifiers$1.pureFunction8
15475 ];
15476 function pureFunctionCallInfo(args) {
15477 const identifier = pureFunctionIdentifiers[args.length];
15478 return {
15479 identifier: identifier || Identifiers$1.pureFunctionV,
15480 isVarLength: !identifier,
15481 };
15482 }
15483 function instruction(span, reference, params) {
15484 return importExpr(reference, null, span).callFn(params, span);
15485 }
15486 // e.g. x(2);
15487 function generateNextContextExpr(relativeLevelDiff) {
15488 return importExpr(Identifiers$1.nextContext)
15489 .callFn(relativeLevelDiff > 1 ? [literal(relativeLevelDiff)] : []);
15490 }
15491 function getLiteralFactory(constantPool, literal$1, allocateSlots) {
15492 const { literalFactory, literalFactoryArguments } = constantPool.getLiteralFactory(literal$1);
15493 // Allocate 1 slot for the result plus 1 per argument
15494 const startSlot = allocateSlots(1 + literalFactoryArguments.length);
15495 const { identifier, isVarLength } = pureFunctionCallInfo(literalFactoryArguments);
15496 // Literal factories are pure functions that only need to be re-invoked when the parameters
15497 // change.
15498 const args = [literal(startSlot), literalFactory];
15499 if (isVarLength) {
15500 args.push(literalArr(literalFactoryArguments));
15501 }
15502 else {
15503 args.push(...literalFactoryArguments);
15504 }
15505 return importExpr(identifier).callFn(args);
15506 }
15507 /**
15508 * Gets an array of literals that can be added to an expression
15509 * to represent the name and namespace of an attribute. E.g.
15510 * `:xlink:href` turns into `[AttributeMarker.NamespaceURI, 'xlink', 'href']`.
15511 *
15512 * @param name Name of the attribute, including the namespace.
15513 */
15514 function getAttributeNameLiterals(name) {
15515 const [attributeNamespace, attributeName] = splitNsName(name);
15516 const nameLiteral = literal(attributeName);
15517 if (attributeNamespace) {
15518 return [
15519 literal(0 /* NamespaceURI */), literal(attributeNamespace), nameLiteral
15520 ];
15521 }
15522 return [nameLiteral];
15523 }
15524 /** The prefix used to get a shared context in BindingScope's map. */
15525 const SHARED_CONTEXT_KEY = '$$shared_ctx$$';
15526 class BindingScope {
15527 constructor(bindingLevel = 0, parent = null, globals) {
15528 this.bindingLevel = bindingLevel;
15529 this.parent = parent;
15530 this.globals = globals;
15531 /** Keeps a map from local variables to their BindingData. */
15532 this.map = new Map();
15533 this.referenceNameIndex = 0;
15534 this.restoreViewVariable = null;
15535 if (globals !== undefined) {
15536 for (const name of globals) {
15537 this.set(0, name, variable(name));
15538 }
15539 }
15540 }
15541 static createRootScope() {
15542 return new BindingScope();
15543 }
15544 get(name) {
15545 let current = this;
15546 while (current) {
15547 let value = current.map.get(name);
15548 if (value != null) {
15549 if (current !== this) {
15550 // make a local copy and reset the `declare` state
15551 value = {
15552 retrievalLevel: value.retrievalLevel,
15553 lhs: value.lhs,
15554 declareLocalCallback: value.declareLocalCallback,
15555 declare: false,
15556 priority: value.priority,
15557 localRef: value.localRef
15558 };
15559 // Cache the value locally.
15560 this.map.set(name, value);
15561 // Possibly generate a shared context var
15562 this.maybeGenerateSharedContextVar(value);
15563 this.maybeRestoreView(value.retrievalLevel, value.localRef);
15564 }
15565 if (value.declareLocalCallback && !value.declare) {
15566 value.declare = true;
15567 }
15568 return value.lhs;
15569 }
15570 current = current.parent;
15571 }
15572 // If we get to this point, we are looking for a property on the top level component
15573 // - If level === 0, we are on the top and don't need to re-declare `ctx`.
15574 // - If level > 0, we are in an embedded view. We need to retrieve the name of the
15575 // local var we used to store the component context, e.g. const $comp$ = x();
15576 return this.bindingLevel === 0 ? null : this.getComponentProperty(name);
15577 }
15578 /**
15579 * Create a local variable for later reference.
15580 *
15581 * @param retrievalLevel The level from which this value can be retrieved
15582 * @param name Name of the variable.
15583 * @param lhs AST representing the left hand side of the `let lhs = rhs;`.
15584 * @param priority The sorting priority of this var
15585 * @param declareLocalCallback The callback to invoke when declaring this local var
15586 * @param localRef Whether or not this is a local ref
15587 */
15588 set(retrievalLevel, name, lhs, priority = 0 /* DEFAULT */, declareLocalCallback, localRef) {
15589 if (this.map.has(name)) {
15590 if (localRef) {
15591 // Do not throw an error if it's a local ref and do not update existing value,
15592 // so the first defined ref is always returned.
15593 return this;
15594 }
15595 error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`);
15596 }
15597 this.map.set(name, {
15598 retrievalLevel: retrievalLevel,
15599 lhs: lhs,
15600 declare: false,
15601 declareLocalCallback: declareLocalCallback,
15602 priority: priority,
15603 localRef: localRef || false
15604 });
15605 return this;
15606 }
15607 // Implemented as part of LocalResolver.
15608 getLocal(name) {
15609 return this.get(name);
15610 }
15611 // Implemented as part of LocalResolver.
15612 notifyImplicitReceiverUse() {
15613 if (this.bindingLevel !== 0) {
15614 // Since the implicit receiver is accessed in an embedded view, we need to
15615 // ensure that we declare a shared context variable for the current template
15616 // in the update variables.
15617 this.map.get(SHARED_CONTEXT_KEY + 0).declare = true;
15618 }
15619 }
15620 nestedScope(level, globals) {
15621 const newScope = new BindingScope(level, this, globals);
15622 if (level > 0)
15623 newScope.generateSharedContextVar(0);
15624 return newScope;
15625 }
15626 /**
15627 * Gets or creates a shared context variable and returns its expression. Note that
15628 * this does not mean that the shared variable will be declared. Variables in the
15629 * binding scope will be only declared if they are used.
15630 */
15631 getOrCreateSharedContextVar(retrievalLevel) {
15632 const bindingKey = SHARED_CONTEXT_KEY + retrievalLevel;
15633 if (!this.map.has(bindingKey)) {
15634 this.generateSharedContextVar(retrievalLevel);
15635 }
15636 // Shared context variables are always generated as "ReadVarExpr".
15637 return this.map.get(bindingKey).lhs;
15638 }
15639 getSharedContextName(retrievalLevel) {
15640 const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + retrievalLevel);
15641 // Shared context variables are always generated as "ReadVarExpr".
15642 return sharedCtxObj && sharedCtxObj.declare ? sharedCtxObj.lhs : null;
15643 }
15644 maybeGenerateSharedContextVar(value) {
15645 if (value.priority === 1 /* CONTEXT */ &&
15646 value.retrievalLevel < this.bindingLevel) {
15647 const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + value.retrievalLevel);
15648 if (sharedCtxObj) {
15649 sharedCtxObj.declare = true;
15650 }
15651 else {
15652 this.generateSharedContextVar(value.retrievalLevel);
15653 }
15654 }
15655 }
15656 generateSharedContextVar(retrievalLevel) {
15657 const lhs = variable(CONTEXT_NAME + this.freshReferenceName());
15658 this.map.set(SHARED_CONTEXT_KEY + retrievalLevel, {
15659 retrievalLevel: retrievalLevel,
15660 lhs: lhs,
15661 declareLocalCallback: (scope, relativeLevel) => {
15662 // const ctx_r0 = nextContext(2);
15663 return [lhs.set(generateNextContextExpr(relativeLevel)).toConstDecl()];
15664 },
15665 declare: false,
15666 priority: 2 /* SHARED_CONTEXT */,
15667 localRef: false
15668 });
15669 }
15670 getComponentProperty(name) {
15671 const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0);
15672 componentValue.declare = true;
15673 this.maybeRestoreView(0, false);
15674 return componentValue.lhs.prop(name);
15675 }
15676 maybeRestoreView(retrievalLevel, localRefLookup) {
15677 // We want to restore the current view in listener fns if:
15678 // 1 - we are accessing a value in a parent view, which requires walking the view tree rather
15679 // than using the ctx arg. In this case, the retrieval and binding level will be different.
15680 // 2 - we are looking up a local ref, which requires restoring the view where the local
15681 // ref is stored
15682 if (this.isListenerScope() && (retrievalLevel < this.bindingLevel || localRefLookup)) {
15683 if (!this.parent.restoreViewVariable) {
15684 // parent saves variable to generate a shared `const $s$ = getCurrentView();` instruction
15685 this.parent.restoreViewVariable = variable(this.parent.freshReferenceName());
15686 }
15687 this.restoreViewVariable = this.parent.restoreViewVariable;
15688 }
15689 }
15690 restoreViewStatement() {
15691 // restoreView($state$);
15692 return this.restoreViewVariable ?
15693 [instruction(null, Identifiers$1.restoreView, [this.restoreViewVariable]).toStmt()] :
15694 [];
15695 }
15696 viewSnapshotStatements() {
15697 // const $state$ = getCurrentView();
15698 const getCurrentViewInstruction = instruction(null, Identifiers$1.getCurrentView, []);
15699 return this.restoreViewVariable ?
15700 [this.restoreViewVariable.set(getCurrentViewInstruction).toConstDecl()] :
15701 [];
15702 }
15703 isListenerScope() {
15704 return this.parent && this.parent.bindingLevel === this.bindingLevel;
15705 }
15706 variableDeclarations() {
15707 let currentContextLevel = 0;
15708 return Array.from(this.map.values())
15709 .filter(value => value.declare)
15710 .sort((a, b) => b.retrievalLevel - a.retrievalLevel || b.priority - a.priority)
15711 .reduce((stmts, value) => {
15712 const levelDiff = this.bindingLevel - value.retrievalLevel;
15713 const currStmts = value.declareLocalCallback(this, levelDiff - currentContextLevel);
15714 currentContextLevel = levelDiff;
15715 return stmts.concat(currStmts);
15716 }, []);
15717 }
15718 freshReferenceName() {
15719 let current = this;
15720 // Find the top scope as it maintains the global reference count
15721 while (current.parent)
15722 current = current.parent;
15723 const ref = `${REFERENCE_PREFIX}${current.referenceNameIndex++}`;
15724 return ref;
15725 }
15726 }
15727 /**
15728 * Creates a `CssSelector` given a tag name and a map of attributes
15729 */
15730 function createCssSelector(elementName, attributes) {
15731 const cssSelector = new CssSelector();
15732 const elementNameNoNs = splitNsName(elementName)[1];
15733 cssSelector.setElement(elementNameNoNs);
15734 Object.getOwnPropertyNames(attributes).forEach((name) => {
15735 const nameNoNs = splitNsName(name)[1];
15736 const value = attributes[name];
15737 cssSelector.addAttribute(nameNoNs, value);
15738 if (name.toLowerCase() === 'class') {
15739 const classes = value.trim().split(/\s+/);
15740 classes.forEach(className => cssSelector.addClassName(className));
15741 }
15742 });
15743 return cssSelector;
15744 }
15745 /**
15746 * Creates an array of expressions out of an `ngProjectAs` attributes
15747 * which can be added to the instruction parameters.
15748 */
15749 function getNgProjectAsLiteral(attribute) {
15750 // Parse the attribute value into a CssSelectorList. Note that we only take the
15751 // first selector, because we don't support multiple selectors in ngProjectAs.
15752 const parsedR3Selector = parseSelectorToR3Selector(attribute.value)[0];
15753 return [literal(5 /* ProjectAs */), asLiteral(parsedR3Selector)];
15754 }
15755 /**
15756 * Gets the instruction to generate for an interpolated property
15757 * @param interpolation An Interpolation AST
15758 */
15759 function getPropertyInterpolationExpression(interpolation) {
15760 switch (getInterpolationArgsLength(interpolation)) {
15761 case 1:
15762 return Identifiers$1.propertyInterpolate;
15763 case 3:
15764 return Identifiers$1.propertyInterpolate1;
15765 case 5:
15766 return Identifiers$1.propertyInterpolate2;
15767 case 7:
15768 return Identifiers$1.propertyInterpolate3;
15769 case 9:
15770 return Identifiers$1.propertyInterpolate4;
15771 case 11:
15772 return Identifiers$1.propertyInterpolate5;
15773 case 13:
15774 return Identifiers$1.propertyInterpolate6;
15775 case 15:
15776 return Identifiers$1.propertyInterpolate7;
15777 case 17:
15778 return Identifiers$1.propertyInterpolate8;
15779 default:
15780 return Identifiers$1.propertyInterpolateV;
15781 }
15782 }
15783 /**
15784 * Gets the instruction to generate for an interpolated attribute
15785 * @param interpolation An Interpolation AST
15786 */
15787 function getAttributeInterpolationExpression(interpolation) {
15788 switch (getInterpolationArgsLength(interpolation)) {
15789 case 3:
15790 return Identifiers$1.attributeInterpolate1;
15791 case 5:
15792 return Identifiers$1.attributeInterpolate2;
15793 case 7:
15794 return Identifiers$1.attributeInterpolate3;
15795 case 9:
15796 return Identifiers$1.attributeInterpolate4;
15797 case 11:
15798 return Identifiers$1.attributeInterpolate5;
15799 case 13:
15800 return Identifiers$1.attributeInterpolate6;
15801 case 15:
15802 return Identifiers$1.attributeInterpolate7;
15803 case 17:
15804 return Identifiers$1.attributeInterpolate8;
15805 default:
15806 return Identifiers$1.attributeInterpolateV;
15807 }
15808 }
15809 /**
15810 * Gets the instruction to generate for interpolated text.
15811 * @param interpolation An Interpolation AST
15812 */
15813 function getTextInterpolationExpression(interpolation) {
15814 switch (getInterpolationArgsLength(interpolation)) {
15815 case 1:
15816 return Identifiers$1.textInterpolate;
15817 case 3:
15818 return Identifiers$1.textInterpolate1;
15819 case 5:
15820 return Identifiers$1.textInterpolate2;
15821 case 7:
15822 return Identifiers$1.textInterpolate3;
15823 case 9:
15824 return Identifiers$1.textInterpolate4;
15825 case 11:
15826 return Identifiers$1.textInterpolate5;
15827 case 13:
15828 return Identifiers$1.textInterpolate6;
15829 case 15:
15830 return Identifiers$1.textInterpolate7;
15831 case 17:
15832 return Identifiers$1.textInterpolate8;
15833 default:
15834 return Identifiers$1.textInterpolateV;
15835 }
15836 }
15837 /**
15838 * Parse a template into render3 `Node`s and additional metadata, with no other dependencies.
15839 *
15840 * @param template text of the template to parse
15841 * @param templateUrl URL to use for source mapping of the parsed template
15842 * @param options options to modify how the template is parsed
15843 */
15844 function parseTemplate(template, templateUrl, options = {}) {
15845 var _a;
15846 const { interpolationConfig, preserveWhitespaces, enableI18nLegacyMessageIdFormat } = options;
15847 const isInline = (_a = options.isInline) !== null && _a !== void 0 ? _a : false;
15848 const bindingParser = makeBindingParser(interpolationConfig);
15849 const htmlParser = new HtmlParser();
15850 const parseResult = htmlParser.parse(template, templateUrl, Object.assign(Object.assign({ leadingTriviaChars: LEADING_TRIVIA_CHARS }, options), { tokenizeExpansionForms: true }));
15851 if (parseResult.errors && parseResult.errors.length > 0) {
15852 // TODO(ayazhafiz): we may not always want to bail out at this point (e.g. in
15853 // the context of a language service).
15854 return {
15855 interpolationConfig,
15856 preserveWhitespaces,
15857 template,
15858 templateUrl,
15859 isInline,
15860 errors: parseResult.errors,
15861 nodes: [],
15862 styleUrls: [],
15863 styles: [],
15864 ngContentSelectors: []
15865 };
15866 }
15867 let rootNodes = parseResult.rootNodes;
15868 // process i18n meta information (scan attributes, generate ids)
15869 // before we run whitespace removal process, because existing i18n
15870 // extraction process (ng extract-i18n) relies on a raw content to generate
15871 // message ids
15872 const i18nMetaVisitor = new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ !preserveWhitespaces, enableI18nLegacyMessageIdFormat);
15873 const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes);
15874 if (i18nMetaResult.errors && i18nMetaResult.errors.length > 0) {
15875 return {
15876 interpolationConfig,
15877 preserveWhitespaces,
15878 template,
15879 templateUrl,
15880 isInline,
15881 errors: i18nMetaResult.errors,
15882 nodes: [],
15883 styleUrls: [],
15884 styles: [],
15885 ngContentSelectors: []
15886 };
15887 }
15888 rootNodes = i18nMetaResult.rootNodes;
15889 if (!preserveWhitespaces) {
15890 rootNodes = visitAll$1(new WhitespaceVisitor(), rootNodes);
15891 // run i18n meta visitor again in case whitespaces are removed (because that might affect
15892 // generated i18n message content) and first pass indicated that i18n content is present in a
15893 // template. During this pass i18n IDs generated at the first pass will be preserved, so we can
15894 // mimic existing extraction process (ng extract-i18n)
15895 if (i18nMetaVisitor.hasI18nMeta) {
15896 rootNodes = visitAll$1(new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ false), rootNodes);
15897 }
15898 }
15899 const { nodes, errors, styleUrls, styles, ngContentSelectors } = htmlAstToRender3Ast(rootNodes, bindingParser);
15900 return {
15901 interpolationConfig,
15902 preserveWhitespaces,
15903 errors: errors.length > 0 ? errors : null,
15904 template,
15905 templateUrl,
15906 isInline,
15907 nodes,
15908 styleUrls,
15909 styles,
15910 ngContentSelectors
15911 };
15912 }
15913 const elementRegistry = new DomElementSchemaRegistry();
15914 /**
15915 * Construct a `BindingParser` with a default configuration.
15916 */
15917 function makeBindingParser(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
15918 return new BindingParser(new IvyParser(new Lexer()), interpolationConfig, elementRegistry, null, []);
15919 }
15920 function resolveSanitizationFn(context, isAttribute) {
15921 switch (context) {
15922 case SecurityContext.HTML:
15923 return importExpr(Identifiers$1.sanitizeHtml);
15924 case SecurityContext.SCRIPT:
15925 return importExpr(Identifiers$1.sanitizeScript);
15926 case SecurityContext.STYLE:
15927 // the compiler does not fill in an instruction for [style.prop?] binding
15928 // values because the style algorithm knows internally what props are subject
15929 // to sanitization (only [attr.style] values are explicitly sanitized)
15930 return isAttribute ? importExpr(Identifiers$1.sanitizeStyle) : null;
15931 case SecurityContext.URL:
15932 return importExpr(Identifiers$1.sanitizeUrl);
15933 case SecurityContext.RESOURCE_URL:
15934 return importExpr(Identifiers$1.sanitizeResourceUrl);
15935 default:
15936 return null;
15937 }
15938 }
15939 function trustedConstAttribute(tagName, attr) {
15940 const value = asLiteral(attr.value);
15941 if (isTrustedTypesSink(tagName, attr.name)) {
15942 switch (elementRegistry.securityContext(tagName, attr.name, /* isAttribute */ true)) {
15943 case SecurityContext.HTML:
15944 return taggedTemplate(importExpr(Identifiers$1.trustConstantHtml), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan);
15945 // NB: no SecurityContext.SCRIPT here, as the corresponding tags are stripped by the compiler.
15946 case SecurityContext.RESOURCE_URL:
15947 return taggedTemplate(importExpr(Identifiers$1.trustConstantResourceUrl), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan);
15948 default:
15949 return value;
15950 }
15951 }
15952 else {
15953 return value;
15954 }
15955 }
15956 function isSingleElementTemplate(children) {
15957 return children.length === 1 && children[0] instanceof Element;
15958 }
15959 function isTextNode(node) {
15960 return node instanceof Text || node instanceof BoundText || node instanceof Icu;
15961 }
15962 function hasTextChildrenOnly(children) {
15963 return children.every(isTextNode);
15964 }
15965 /** Name of the global variable that is used to determine if we use Closure translations or not */
15966 const NG_I18N_CLOSURE_MODE = 'ngI18nClosureMode';
15967 /**
15968 * Generate statements that define a given translation message.
15969 *
15970 * ```
15971 * var I18N_1;
15972 * if (typeof ngI18nClosureMode !== undefined && ngI18nClosureMode) {
15973 * var MSG_EXTERNAL_XXX = goog.getMsg(
15974 * "Some message with {$interpolation}!",
15975 * { "interpolation": "\uFFFD0\uFFFD" }
15976 * );
15977 * I18N_1 = MSG_EXTERNAL_XXX;
15978 * }
15979 * else {
15980 * I18N_1 = $localize`Some message with ${'\uFFFD0\uFFFD'}!`;
15981 * }
15982 * ```
15983 *
15984 * @param message The original i18n AST message node
15985 * @param variable The variable that will be assigned the translation, e.g. `I18N_1`.
15986 * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
15987 * @param params Object mapping placeholder names to their values (e.g.
15988 * `{ "interpolation": "\uFFFD0\uFFFD" }`).
15989 * @param transformFn Optional transformation function that will be applied to the translation (e.g.
15990 * post-processing).
15991 * @returns An array of statements that defined a given translation.
15992 */
15993 function getTranslationDeclStmts(message, variable, closureVar, params = {}, transformFn) {
15994 const statements = [
15995 declareI18nVariable(variable),
15996 ifStmt(createClosureModeGuard(), createGoogleGetMsgStatements(variable, message, closureVar, i18nFormatPlaceholderNames(params, /* useCamelCase */ true)), createLocalizeStatements(variable, message, i18nFormatPlaceholderNames(params, /* useCamelCase */ false))),
15997 ];
15998 if (transformFn) {
15999 statements.push(new ExpressionStatement(variable.set(transformFn(variable))));
16000 }
16001 return statements;
16002 }
16003 /**
16004 * Create the expression that will be used to guard the closure mode block
16005 * It is equivalent to:
16006 *
16007 * ```
16008 * typeof ngI18nClosureMode !== undefined && ngI18nClosureMode
16009 * ```
16010 */
16011 function createClosureModeGuard() {
16012 return typeofExpr(variable(NG_I18N_CLOSURE_MODE))
16013 .notIdentical(literal('undefined', STRING_TYPE))
16014 .and(variable(NG_I18N_CLOSURE_MODE));
16015 }
16016
16017 /**
16018 * @license
16019 * Copyright Google LLC All Rights Reserved.
16020 *
16021 * Use of this source code is governed by an MIT-style license that can be
16022 * found in the LICENSE file at https://angular.io/license
16023 */
16024 // This regex matches any binding names that contain the "attr." prefix, e.g. "attr.required"
16025 // If there is a match, the first matching group will contain the attribute name to bind.
16026 const ATTR_REGEX = /attr\.([^\]]+)/;
16027 function baseDirectiveFields(meta, constantPool, bindingParser) {
16028 const definitionMap = new DefinitionMap();
16029 const selectors = parseSelectorToR3Selector(meta.selector);
16030 // e.g. `type: MyDirective`
16031 definitionMap.set('type', meta.internalType);
16032 // e.g. `selectors: [['', 'someDir', '']]`
16033 if (selectors.length > 0) {
16034 definitionMap.set('selectors', asLiteral(selectors));
16035 }
16036 if (meta.queries.length > 0) {
16037 // e.g. `contentQueries: (rf, ctx, dirIndex) => { ... }
16038 definitionMap.set('contentQueries', createContentQueriesFunction(meta.queries, constantPool, meta.name));
16039 }
16040 if (meta.viewQueries.length) {
16041 definitionMap.set('viewQuery', createViewQueriesFunction(meta.viewQueries, constantPool, meta.name));
16042 }
16043 // e.g. `hostBindings: (rf, ctx) => { ... }
16044 definitionMap.set('hostBindings', createHostBindingsFunction(meta.host, meta.typeSourceSpan, bindingParser, constantPool, meta.selector || '', meta.name, definitionMap));
16045 // e.g 'inputs: {a: 'a'}`
16046 definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
16047 // e.g 'outputs: {a: 'a'}`
16048 definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
16049 if (meta.exportAs !== null) {
16050 definitionMap.set('exportAs', literalArr(meta.exportAs.map(e => literal(e))));
16051 }
16052 return definitionMap;
16053 }
16054 /**
16055 * Add features to the definition map.
16056 */
16057 function addFeatures(definitionMap, meta) {
16058 // e.g. `features: [NgOnChangesFeature]`
16059 const features = [];
16060 const providers = meta.providers;
16061 const viewProviders = meta.viewProviders;
16062 if (providers || viewProviders) {
16063 const args = [providers || new LiteralArrayExpr([])];
16064 if (viewProviders) {
16065 args.push(viewProviders);
16066 }
16067 features.push(importExpr(Identifiers$1.ProvidersFeature).callFn(args));
16068 }
16069 if (meta.usesInheritance) {
16070 features.push(importExpr(Identifiers$1.InheritDefinitionFeature));
16071 }
16072 if (meta.fullInheritance) {
16073 features.push(importExpr(Identifiers$1.CopyDefinitionFeature));
16074 }
16075 if (meta.lifecycle.usesOnChanges) {
16076 features.push(importExpr(Identifiers$1.NgOnChangesFeature));
16077 }
16078 if (features.length) {
16079 definitionMap.set('features', literalArr(features));
16080 }
16081 }
16082 /**
16083 * Compile a directive for the render3 runtime as defined by the `R3DirectiveMetadata`.
16084 */
16085 function compileDirectiveFromMetadata(meta, constantPool, bindingParser) {
16086 const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
16087 addFeatures(definitionMap, meta);
16088 const expression = importExpr(Identifiers$1.defineDirective).callFn([definitionMap.toLiteralMap()]);
16089 const type = createDirectiveType(meta);
16090 return { expression, type };
16091 }
16092 /**
16093 * Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`.
16094 */
16095 function compileComponentFromMetadata(meta, constantPool, bindingParser) {
16096 const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
16097 addFeatures(definitionMap, meta);
16098 const selector = meta.selector && CssSelector.parse(meta.selector);
16099 const firstSelector = selector && selector[0];
16100 // e.g. `attr: ["class", ".my.app"]`
16101 // This is optional an only included if the first selector of a component specifies attributes.
16102 if (firstSelector) {
16103 const selectorAttributes = firstSelector.getAttrs();
16104 if (selectorAttributes.length) {
16105 definitionMap.set('attrs', constantPool.getConstLiteral(literalArr(selectorAttributes.map(value => value != null ? literal(value) : literal(undefined))),
16106 /* forceShared */ true));
16107 }
16108 }
16109 // Generate the CSS matcher that recognize directive
16110 let directiveMatcher = null;
16111 if (meta.directives.length > 0) {
16112 const matcher = new SelectorMatcher();
16113 for (const { selector, type } of meta.directives) {
16114 matcher.addSelectables(CssSelector.parse(selector), type);
16115 }
16116 directiveMatcher = matcher;
16117 }
16118 // e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
16119 const templateTypeName = meta.name;
16120 const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
16121 const directivesUsed = new Set();
16122 const pipesUsed = new Set();
16123 const changeDetection = meta.changeDetection;
16124 const template = meta.template;
16125 const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, directiveMatcher, directivesUsed, meta.pipes, pipesUsed, Identifiers$1.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds);
16126 const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
16127 // We need to provide this so that dynamically generated components know what
16128 // projected content blocks to pass through to the component when it is instantiated.
16129 const ngContentSelectors = templateBuilder.getNgContentSelectors();
16130 if (ngContentSelectors) {
16131 definitionMap.set('ngContentSelectors', ngContentSelectors);
16132 }
16133 // e.g. `decls: 2`
16134 definitionMap.set('decls', literal(templateBuilder.getConstCount()));
16135 // e.g. `vars: 2`
16136 definitionMap.set('vars', literal(templateBuilder.getVarCount()));
16137 // Generate `consts` section of ComponentDef:
16138 // - either as an array:
16139 // `consts: [['one', 'two'], ['three', 'four']]`
16140 // - or as a factory function in case additional statements are present (to support i18n):
16141 // `consts: function() { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0]; }`
16142 const { constExpressions, prepareStatements } = templateBuilder.getConsts();
16143 if (constExpressions.length > 0) {
16144 let constsExpr = literalArr(constExpressions);
16145 // Prepare statements are present - turn `consts` into a function.
16146 if (prepareStatements.length > 0) {
16147 constsExpr = fn([], [...prepareStatements, new ReturnStatement(constsExpr)]);
16148 }
16149 definitionMap.set('consts', constsExpr);
16150 }
16151 definitionMap.set('template', templateFunctionExpression);
16152 // e.g. `directives: [MyDirective]`
16153 if (directivesUsed.size) {
16154 const directivesList = literalArr(Array.from(directivesUsed));
16155 const directivesExpr = compileDeclarationList(directivesList, meta.declarationListEmitMode);
16156 definitionMap.set('directives', directivesExpr);
16157 }
16158 // e.g. `pipes: [MyPipe]`
16159 if (pipesUsed.size) {
16160 const pipesList = literalArr(Array.from(pipesUsed));
16161 const pipesExpr = compileDeclarationList(pipesList, meta.declarationListEmitMode);
16162 definitionMap.set('pipes', pipesExpr);
16163 }
16164 if (meta.encapsulation === null) {
16165 meta.encapsulation = ViewEncapsulation.Emulated;
16166 }
16167 // e.g. `styles: [str1, str2]`
16168 if (meta.styles && meta.styles.length) {
16169 const styleValues = meta.encapsulation == ViewEncapsulation.Emulated ?
16170 compileStyles(meta.styles, CONTENT_ATTR, HOST_ATTR) :
16171 meta.styles;
16172 const strings = styleValues.map(str => constantPool.getConstLiteral(literal(str)));
16173 definitionMap.set('styles', literalArr(strings));
16174 }
16175 else if (meta.encapsulation === ViewEncapsulation.Emulated) {
16176 // If there is no style, don't generate css selectors on elements
16177 meta.encapsulation = ViewEncapsulation.None;
16178 }
16179 // Only set view encapsulation if it's not the default value
16180 if (meta.encapsulation !== ViewEncapsulation.Emulated) {
16181 definitionMap.set('encapsulation', literal(meta.encapsulation));
16182 }
16183 // e.g. `animation: [trigger('123', [])]`
16184 if (meta.animations !== null) {
16185 definitionMap.set('data', literalMap([{ key: 'animation', value: meta.animations, quoted: false }]));
16186 }
16187 // Only set the change detection flag if it's defined and it's not the default.
16188 if (changeDetection != null && changeDetection !== ChangeDetectionStrategy.Default) {
16189 definitionMap.set('changeDetection', literal(changeDetection));
16190 }
16191 const expression = importExpr(Identifiers$1.defineComponent).callFn([definitionMap.toLiteralMap()]);
16192 const type = createComponentType(meta);
16193 return { expression, type };
16194 }
16195 /**
16196 * Creates the type specification from the component meta. This type is inserted into .d.ts files
16197 * to be consumed by upstream compilations.
16198 */
16199 function createComponentType(meta) {
16200 const typeParams = createDirectiveTypeParams(meta);
16201 typeParams.push(stringArrayAsType(meta.template.ngContentSelectors));
16202 return expressionType(importExpr(Identifiers$1.ComponentDefWithMeta, typeParams));
16203 }
16204 /**
16205 * Compiles the array literal of declarations into an expression according to the provided emit
16206 * mode.
16207 */
16208 function compileDeclarationList(list, mode) {
16209 switch (mode) {
16210 case 0 /* Direct */:
16211 // directives: [MyDir],
16212 return list;
16213 case 1 /* Closure */:
16214 // directives: function () { return [MyDir]; }
16215 return fn([], [new ReturnStatement(list)]);
16216 case 2 /* ClosureResolved */:
16217 // directives: function () { return [MyDir].map(ng.resolveForwardRef); }
16218 const resolvedList = list.callMethod('map', [importExpr(Identifiers$1.resolveForwardRef)]);
16219 return fn([], [new ReturnStatement(resolvedList)]);
16220 }
16221 }
16222 function prepareQueryParams(query, constantPool) {
16223 const parameters = [getQueryPredicate(query, constantPool), literal(toQueryFlags(query))];
16224 if (query.read) {
16225 parameters.push(query.read);
16226 }
16227 return parameters;
16228 }
16229 /**
16230 * Translates query flags into `TQueryFlags` type in packages/core/src/render3/interfaces/query.ts
16231 * @param query
16232 */
16233 function toQueryFlags(query) {
16234 return (query.descendants ? 1 /* descendants */ : 0 /* none */) |
16235 (query.static ? 2 /* isStatic */ : 0 /* none */) |
16236 (query.emitDistinctChangesOnly ? 4 /* emitDistinctChangesOnly */ : 0 /* none */);
16237 }
16238 function convertAttributesToExpressions(attributes) {
16239 const values = [];
16240 for (let key of Object.getOwnPropertyNames(attributes)) {
16241 const value = attributes[key];
16242 values.push(literal(key), value);
16243 }
16244 return values;
16245 }
16246 // Define and update any content queries
16247 function createContentQueriesFunction(queries, constantPool, name) {
16248 const createStatements = [];
16249 const updateStatements = [];
16250 const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
16251 for (const query of queries) {
16252 // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null);
16253 createStatements.push(importExpr(Identifiers$1.contentQuery)
16254 .callFn([variable('dirIndex'), ...prepareQueryParams(query, constantPool)])
16255 .toStmt());
16256 // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
16257 const temporary = tempAllocator();
16258 const getQueryList = importExpr(Identifiers$1.loadQuery).callFn([]);
16259 const refresh = importExpr(Identifiers$1.queryRefresh).callFn([temporary.set(getQueryList)]);
16260 const updateDirective = variable(CONTEXT_NAME)
16261 .prop(query.propertyName)
16262 .set(query.first ? temporary.prop('first') : temporary);
16263 updateStatements.push(refresh.and(updateDirective).toStmt());
16264 }
16265 const contentQueriesFnName = name ? `${name}_ContentQueries` : null;
16266 return fn([
16267 new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null),
16268 new FnParam('dirIndex', null)
16269 ], [
16270 renderFlagCheckIfStmt(1 /* Create */, createStatements),
16271 renderFlagCheckIfStmt(2 /* Update */, updateStatements)
16272 ], INFERRED_TYPE, null, contentQueriesFnName);
16273 }
16274 function stringAsType(str) {
16275 return expressionType(literal(str));
16276 }
16277 function stringMapAsType(map) {
16278 const mapValues = Object.keys(map).map(key => {
16279 const value = Array.isArray(map[key]) ? map[key][0] : map[key];
16280 return {
16281 key,
16282 value: literal(value),
16283 quoted: true,
16284 };
16285 });
16286 return expressionType(literalMap(mapValues));
16287 }
16288 function stringArrayAsType(arr) {
16289 return arr.length > 0 ? expressionType(literalArr(arr.map(value => literal(value)))) :
16290 NONE_TYPE;
16291 }
16292 function createDirectiveTypeParams(meta) {
16293 // On the type side, remove newlines from the selector as it will need to fit into a TypeScript
16294 // string literal, which must be on one line.
16295 const selectorForType = meta.selector !== null ? meta.selector.replace(/\n/g, '') : null;
16296 return [
16297 typeWithParameters(meta.type.type, meta.typeArgumentCount),
16298 selectorForType !== null ? stringAsType(selectorForType) : NONE_TYPE,
16299 meta.exportAs !== null ? stringArrayAsType(meta.exportAs) : NONE_TYPE,
16300 stringMapAsType(meta.inputs),
16301 stringMapAsType(meta.outputs),
16302 stringArrayAsType(meta.queries.map(q => q.propertyName)),
16303 ];
16304 }
16305 /**
16306 * Creates the type specification from the directive meta. This type is inserted into .d.ts files
16307 * to be consumed by upstream compilations.
16308 */
16309 function createDirectiveType(meta) {
16310 const typeParams = createDirectiveTypeParams(meta);
16311 return expressionType(importExpr(Identifiers$1.DirectiveDefWithMeta, typeParams));
16312 }
16313 // Define and update any view queries
16314 function createViewQueriesFunction(viewQueries, constantPool, name) {
16315 const createStatements = [];
16316 const updateStatements = [];
16317 const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
16318 viewQueries.forEach((query) => {
16319 // creation, e.g. r3.viewQuery(somePredicate, true);
16320 const queryDefinition = importExpr(Identifiers$1.viewQuery).callFn(prepareQueryParams(query, constantPool));
16321 createStatements.push(queryDefinition.toStmt());
16322 // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
16323 const temporary = tempAllocator();
16324 const getQueryList = importExpr(Identifiers$1.loadQuery).callFn([]);
16325 const refresh = importExpr(Identifiers$1.queryRefresh).callFn([temporary.set(getQueryList)]);
16326 const updateDirective = variable(CONTEXT_NAME)
16327 .prop(query.propertyName)
16328 .set(query.first ? temporary.prop('first') : temporary);
16329 updateStatements.push(refresh.and(updateDirective).toStmt());
16330 });
16331 const viewQueryFnName = name ? `${name}_Query` : null;
16332 return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
16333 renderFlagCheckIfStmt(1 /* Create */, createStatements),
16334 renderFlagCheckIfStmt(2 /* Update */, updateStatements)
16335 ], INFERRED_TYPE, null, viewQueryFnName);
16336 }
16337 // Return a host binding function or null if one is not necessary.
16338 function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindingParser, constantPool, selector, name, definitionMap) {
16339 const bindingContext = variable(CONTEXT_NAME);
16340 const styleBuilder = new StylingBuilder(bindingContext);
16341 const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
16342 if (styleAttr !== undefined) {
16343 styleBuilder.registerStyleAttr(styleAttr);
16344 }
16345 if (classAttr !== undefined) {
16346 styleBuilder.registerClassAttr(classAttr);
16347 }
16348 const createStatements = [];
16349 const updateStatements = [];
16350 const hostBindingSourceSpan = typeSourceSpan;
16351 const directiveSummary = metadataAsSummary(hostBindingsMetadata);
16352 // Calculate host event bindings
16353 const eventBindings = bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
16354 if (eventBindings && eventBindings.length) {
16355 const listeners = createHostListeners(eventBindings, name);
16356 createStatements.push(...listeners);
16357 }
16358 // Calculate the host property bindings
16359 const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
16360 const allOtherBindings = [];
16361 // We need to calculate the total amount of binding slots required by
16362 // all the instructions together before any value conversions happen.
16363 // Value conversions may require additional slots for interpolation and
16364 // bindings with pipes. These calculates happen after this block.
16365 let totalHostVarsCount = 0;
16366 bindings && bindings.forEach((binding) => {
16367 const stylingInputWasSet = styleBuilder.registerInputBasedOnName(binding.name, binding.expression, hostBindingSourceSpan);
16368 if (stylingInputWasSet) {
16369 totalHostVarsCount += MIN_STYLING_BINDING_SLOTS_REQUIRED;
16370 }
16371 else {
16372 allOtherBindings.push(binding);
16373 totalHostVarsCount++;
16374 }
16375 });
16376 let valueConverter;
16377 const getValueConverter = () => {
16378 if (!valueConverter) {
16379 const hostVarsCountFn = (numSlots) => {
16380 const originalVarsCount = totalHostVarsCount;
16381 totalHostVarsCount += numSlots;
16382 return originalVarsCount;
16383 };
16384 valueConverter = new ValueConverter(constantPool, () => error('Unexpected node'), // new nodes are illegal here
16385 hostVarsCountFn, () => error('Unexpected pipe')); // pipes are illegal here
16386 }
16387 return valueConverter;
16388 };
16389 const propertyBindings = [];
16390 const attributeBindings = [];
16391 const syntheticHostBindings = [];
16392 allOtherBindings.forEach((binding) => {
16393 // resolve literal arrays and literal objects
16394 const value = binding.expression.visit(getValueConverter());
16395 const bindingExpr = bindingFn(bindingContext, value);
16396 const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
16397 const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
16398 .filter(context => context !== SecurityContext.NONE);
16399 let sanitizerFn = null;
16400 if (securityContexts.length) {
16401 if (securityContexts.length === 2 &&
16402 securityContexts.indexOf(SecurityContext.URL) > -1 &&
16403 securityContexts.indexOf(SecurityContext.RESOURCE_URL) > -1) {
16404 // Special case for some URL attributes (such as "src" and "href") that may be a part
16405 // of different security contexts. In this case we use special santitization function and
16406 // select the actual sanitizer at runtime based on a tag name that is provided while
16407 // invoking sanitization function.
16408 sanitizerFn = importExpr(Identifiers$1.sanitizeUrlOrResourceUrl);
16409 }
16410 else {
16411 sanitizerFn = resolveSanitizationFn(securityContexts[0], isAttribute);
16412 }
16413 }
16414 const instructionParams = [literal(bindingName), bindingExpr.currValExpr];
16415 if (sanitizerFn) {
16416 instructionParams.push(sanitizerFn);
16417 }
16418 updateStatements.push(...bindingExpr.stmts);
16419 if (instruction === Identifiers$1.hostProperty) {
16420 propertyBindings.push(instructionParams);
16421 }
16422 else if (instruction === Identifiers$1.attribute) {
16423 attributeBindings.push(instructionParams);
16424 }
16425 else if (instruction === Identifiers$1.syntheticHostProperty) {
16426 syntheticHostBindings.push(instructionParams);
16427 }
16428 else {
16429 updateStatements.push(importExpr(instruction).callFn(instructionParams).toStmt());
16430 }
16431 });
16432 if (propertyBindings.length > 0) {
16433 updateStatements.push(chainedInstruction(Identifiers$1.hostProperty, propertyBindings).toStmt());
16434 }
16435 if (attributeBindings.length > 0) {
16436 updateStatements.push(chainedInstruction(Identifiers$1.attribute, attributeBindings).toStmt());
16437 }
16438 if (syntheticHostBindings.length > 0) {
16439 updateStatements.push(chainedInstruction(Identifiers$1.syntheticHostProperty, syntheticHostBindings).toStmt());
16440 }
16441 // since we're dealing with directives/components and both have hostBinding
16442 // functions, we need to generate a special hostAttrs instruction that deals
16443 // with both the assignment of styling as well as static attributes to the host
16444 // element. The instruction below will instruct all initial styling (styling
16445 // that is inside of a host binding within a directive/component) to be attached
16446 // to the host element alongside any of the provided host attributes that were
16447 // collected earlier.
16448 const hostAttrs = convertAttributesToExpressions(hostBindingsMetadata.attributes);
16449 styleBuilder.assignHostAttrs(hostAttrs, definitionMap);
16450 if (styleBuilder.hasBindings) {
16451 // finally each binding that was registered in the statement above will need to be added to
16452 // the update block of a component/directive templateFn/hostBindingsFn so that the bindings
16453 // are evaluated and updated for the element.
16454 styleBuilder.buildUpdateLevelInstructions(getValueConverter()).forEach(instruction => {
16455 if (instruction.calls.length > 0) {
16456 const calls = [];
16457 instruction.calls.forEach(call => {
16458 // we subtract a value of `1` here because the binding slot was already allocated
16459 // at the top of this method when all the input bindings were counted.
16460 totalHostVarsCount +=
16461 Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
16462 calls.push(convertStylingCall(call, bindingContext, bindingFn));
16463 });
16464 updateStatements.push(chainedInstruction(instruction.reference, calls).toStmt());
16465 }
16466 });
16467 }
16468 if (totalHostVarsCount) {
16469 definitionMap.set('hostVars', literal(totalHostVarsCount));
16470 }
16471 if (createStatements.length > 0 || updateStatements.length > 0) {
16472 const hostBindingsFnName = name ? `${name}_HostBindings` : null;
16473 const statements = [];
16474 if (createStatements.length > 0) {
16475 statements.push(renderFlagCheckIfStmt(1 /* Create */, createStatements));
16476 }
16477 if (updateStatements.length > 0) {
16478 statements.push(renderFlagCheckIfStmt(2 /* Update */, updateStatements));
16479 }
16480 return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], statements, INFERRED_TYPE, null, hostBindingsFnName);
16481 }
16482 return null;
16483 }
16484 function bindingFn(implicit, value) {
16485 return convertPropertyBinding(null, implicit, value, 'b', BindingForm.Expression, () => error('Unexpected interpolation'));
16486 }
16487 function convertStylingCall(call, bindingContext, bindingFn) {
16488 return call.params(value => bindingFn(bindingContext, value).currValExpr);
16489 }
16490 function getBindingNameAndInstruction(binding) {
16491 let bindingName = binding.name;
16492 let instruction;
16493 // Check to see if this is an attr binding or a property binding
16494 const attrMatches = bindingName.match(ATTR_REGEX);
16495 if (attrMatches) {
16496 bindingName = attrMatches[1];
16497 instruction = Identifiers$1.attribute;
16498 }
16499 else {
16500 if (binding.isAnimation) {
16501 bindingName = prepareSyntheticPropertyName(bindingName);
16502 // host bindings that have a synthetic property (e.g. @foo) should always be rendered
16503 // in the context of the component and not the parent. Therefore there is a special
16504 // compatibility instruction available for this purpose.
16505 instruction = Identifiers$1.syntheticHostProperty;
16506 }
16507 else {
16508 instruction = Identifiers$1.hostProperty;
16509 }
16510 }
16511 return { bindingName, instruction, isAttribute: !!attrMatches };
16512 }
16513 function createHostListeners(eventBindings, name) {
16514 const listeners = [];
16515 const syntheticListeners = [];
16516 const instructions = [];
16517 eventBindings.forEach(binding => {
16518 let bindingName = binding.name && sanitizeIdentifier(binding.name);
16519 const bindingFnName = binding.type === 1 /* Animation */ ?
16520 prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase) :
16521 bindingName;
16522 const handlerName = name && bindingName ? `${name}_${bindingFnName}_HostBindingHandler` : null;
16523 const params = prepareEventListenerParameters(BoundEvent.fromParsedEvent(binding), handlerName);
16524 if (binding.type == 1 /* Animation */) {
16525 syntheticListeners.push(params);
16526 }
16527 else {
16528 listeners.push(params);
16529 }
16530 });
16531 if (syntheticListeners.length > 0) {
16532 instructions.push(chainedInstruction(Identifiers$1.syntheticHostListener, syntheticListeners).toStmt());
16533 }
16534 if (listeners.length > 0) {
16535 instructions.push(chainedInstruction(Identifiers$1.listener, listeners).toStmt());
16536 }
16537 return instructions;
16538 }
16539 function metadataAsSummary(meta) {
16540 // clang-format off
16541 return {
16542 // This is used by the BindingParser, which only deals with listeners and properties. There's no
16543 // need to pass attributes to it.
16544 hostAttributes: {},
16545 hostListeners: meta.listeners,
16546 hostProperties: meta.properties,
16547 };
16548 // clang-format on
16549 }
16550 const HOST_REG_EXP = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/;
16551 function parseHostBindings(host) {
16552 const attributes = {};
16553 const listeners = {};
16554 const properties = {};
16555 const specialAttributes = {};
16556 for (const key of Object.keys(host)) {
16557 const value = host[key];
16558 const matches = key.match(HOST_REG_EXP);
16559 if (matches === null) {
16560 switch (key) {
16561 case 'class':
16562 if (typeof value !== 'string') {
16563 // TODO(alxhub): make this a diagnostic.
16564 throw new Error(`Class binding must be string`);
16565 }
16566 specialAttributes.classAttr = value;
16567 break;
16568 case 'style':
16569 if (typeof value !== 'string') {
16570 // TODO(alxhub): make this a diagnostic.
16571 throw new Error(`Style binding must be string`);
16572 }
16573 specialAttributes.styleAttr = value;
16574 break;
16575 default:
16576 if (typeof value === 'string') {
16577 attributes[key] = literal(value);
16578 }
16579 else {
16580 attributes[key] = value;
16581 }
16582 }
16583 }
16584 else if (matches[1 /* Binding */] != null) {
16585 if (typeof value !== 'string') {
16586 // TODO(alxhub): make this a diagnostic.
16587 throw new Error(`Property binding must be string`);
16588 }
16589 // synthetic properties (the ones that have a `@` as a prefix)
16590 // are still treated the same as regular properties. Therefore
16591 // there is no point in storing them in a separate map.
16592 properties[matches[1 /* Binding */]] = value;
16593 }
16594 else if (matches[2 /* Event */] != null) {
16595 if (typeof value !== 'string') {
16596 // TODO(alxhub): make this a diagnostic.
16597 throw new Error(`Event binding must be string`);
16598 }
16599 listeners[matches[2 /* Event */]] = value;
16600 }
16601 }
16602 return { attributes, listeners, properties, specialAttributes };
16603 }
16604 /**
16605 * Verifies host bindings and returns the list of errors (if any). Empty array indicates that a
16606 * given set of host bindings has no errors.
16607 *
16608 * @param bindings set of host bindings to verify.
16609 * @param sourceSpan source span where host bindings were defined.
16610 * @returns array of errors associated with a given set of host bindings.
16611 */
16612 function verifyHostBindings(bindings, sourceSpan) {
16613 const summary = metadataAsSummary(bindings);
16614 // TODO: abstract out host bindings verification logic and use it instead of
16615 // creating events and properties ASTs to detect errors (FW-996)
16616 const bindingParser = makeBindingParser();
16617 bindingParser.createDirectiveHostEventAsts(summary, sourceSpan);
16618 bindingParser.createBoundHostProperties(summary, sourceSpan);
16619 return bindingParser.errors;
16620 }
16621 function compileStyles(styles, selector, hostSelector) {
16622 const shadowCss = new ShadowCss();
16623 return styles.map(style => {
16624 return shadowCss.shimCssText(style, selector, hostSelector);
16625 });
16626 }
16627
16628 /**
16629 * @license
16630 * Copyright Google LLC All Rights Reserved.
16631 *
16632 * Use of this source code is governed by an MIT-style license that can be
16633 * found in the LICENSE file at https://angular.io/license
16634 */
16635 /**
16636 * An interface for retrieving documents by URL that the compiler uses
16637 * to load templates.
16638 */
16639 class ResourceLoader {
16640 get(url) {
16641 return '';
16642 }
16643 }
16644
16645 /**
16646 * @license
16647 * Copyright Google LLC All Rights Reserved.
16648 *
16649 * Use of this source code is governed by an MIT-style license that can be
16650 * found in the LICENSE file at https://angular.io/license
16651 */
16652 class CompilerFacadeImpl {
16653 constructor(jitEvaluator = new JitEvaluator()) {
16654 this.jitEvaluator = jitEvaluator;
16655 this.R3ResolvedDependencyType = R3ResolvedDependencyType;
16656 this.R3FactoryTarget = R3FactoryTarget;
16657 this.ResourceLoader = ResourceLoader;
16658 this.elementSchemaRegistry = new DomElementSchemaRegistry();
16659 }
16660 compilePipe(angularCoreEnv, sourceMapUrl, facade) {
16661 const metadata = {
16662 name: facade.name,
16663 type: wrapReference(facade.type),
16664 internalType: new WrappedNodeExpr(facade.type),
16665 typeArgumentCount: facade.typeArgumentCount,
16666 deps: convertR3DependencyMetadataArray(facade.deps),
16667 pipeName: facade.pipeName,
16668 pure: facade.pure,
16669 };
16670 const res = compilePipeFromMetadata(metadata);
16671 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
16672 }
16673 compileInjectable(angularCoreEnv, sourceMapUrl, facade) {
16674 const { expression, statements } = compileInjectable({
16675 name: facade.name,
16676 type: wrapReference(facade.type),
16677 internalType: new WrappedNodeExpr(facade.type),
16678 typeArgumentCount: facade.typeArgumentCount,
16679 providedIn: computeProvidedIn(facade.providedIn),
16680 useClass: wrapExpression(facade, USE_CLASS),
16681 useFactory: wrapExpression(facade, USE_FACTORY),
16682 useValue: wrapExpression(facade, USE_VALUE),
16683 useExisting: wrapExpression(facade, USE_EXISTING),
16684 userDeps: convertR3DependencyMetadataArray(facade.userDeps) || undefined,
16685 });
16686 return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements);
16687 }
16688 compileInjector(angularCoreEnv, sourceMapUrl, facade) {
16689 const meta = {
16690 name: facade.name,
16691 type: wrapReference(facade.type),
16692 internalType: new WrappedNodeExpr(facade.type),
16693 deps: convertR3DependencyMetadataArray(facade.deps),
16694 providers: new WrappedNodeExpr(facade.providers),
16695 imports: facade.imports.map(i => new WrappedNodeExpr(i)),
16696 };
16697 const res = compileInjector(meta);
16698 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
16699 }
16700 compileNgModule(angularCoreEnv, sourceMapUrl, facade) {
16701 const meta = {
16702 type: wrapReference(facade.type),
16703 internalType: new WrappedNodeExpr(facade.type),
16704 adjacentType: new WrappedNodeExpr(facade.type),
16705 bootstrap: facade.bootstrap.map(wrapReference),
16706 declarations: facade.declarations.map(wrapReference),
16707 imports: facade.imports.map(wrapReference),
16708 exports: facade.exports.map(wrapReference),
16709 emitInline: true,
16710 containsForwardDecls: false,
16711 schemas: facade.schemas ? facade.schemas.map(wrapReference) : null,
16712 id: facade.id ? new WrappedNodeExpr(facade.id) : null,
16713 };
16714 const res = compileNgModule(meta);
16715 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
16716 }
16717 compileDirective(angularCoreEnv, sourceMapUrl, facade) {
16718 const meta = convertDirectiveFacadeToMetadata(facade);
16719 return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
16720 }
16721 compileDirectiveDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
16722 const typeSourceSpan = this.createParseSourceSpan('Directive', declaration.type.name, sourceMapUrl);
16723 const meta = convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan);
16724 return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
16725 }
16726 compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta) {
16727 const constantPool = new ConstantPool();
16728 const bindingParser = makeBindingParser();
16729 const res = compileDirectiveFromMetadata(meta, constantPool, bindingParser);
16730 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
16731 }
16732 compileComponent(angularCoreEnv, sourceMapUrl, facade) {
16733 // Parse the template and check for errors.
16734 const { template, interpolation } = parseJitTemplate(facade.template, facade.name, sourceMapUrl, facade.preserveWhitespaces, facade.interpolation);
16735 // Compile the component metadata, including template, into an expression.
16736 const meta = Object.assign(Object.assign(Object.assign({}, facade), convertDirectiveFacadeToMetadata(facade)), { selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(), template, declarationListEmitMode: 0 /* Direct */, styles: [...facade.styles, ...template.styles], encapsulation: facade.encapsulation, interpolation, changeDetection: facade.changeDetection, animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null, viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) :
16737 null, relativeContextFilePath: '', i18nUseExternalIds: true });
16738 const jitExpressionSourceMap = `ng:///${facade.name}.js`;
16739 return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
16740 }
16741 compileComponentDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
16742 const typeSourceSpan = this.createParseSourceSpan('Component', declaration.type.name, sourceMapUrl);
16743 const meta = convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl);
16744 return this.compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta);
16745 }
16746 compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta) {
16747 const constantPool = new ConstantPool();
16748 const bindingParser = makeBindingParser(meta.interpolation);
16749 const res = compileComponentFromMetadata(meta, constantPool, bindingParser);
16750 return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
16751 }
16752 compileFactory(angularCoreEnv, sourceMapUrl, meta) {
16753 const factoryRes = compileFactoryFunction({
16754 name: meta.name,
16755 type: wrapReference(meta.type),
16756 internalType: new WrappedNodeExpr(meta.type),
16757 typeArgumentCount: meta.typeArgumentCount,
16758 deps: convertR3DependencyMetadataArray(meta.deps),
16759 injectFn: meta.injectFn === 'directiveInject' ? Identifiers.directiveInject :
16760 Identifiers.inject,
16761 target: meta.target,
16762 });
16763 return this.jitExpression(factoryRes.factory, angularCoreEnv, sourceMapUrl, factoryRes.statements);
16764 }
16765 createParseSourceSpan(kind, typeName, sourceUrl) {
16766 return r3JitTypeSourceSpan(kind, typeName, sourceUrl);
16767 }
16768 /**
16769 * JIT compiles an expression and returns the result of executing that expression.
16770 *
16771 * @param def the definition which will be compiled and executed to get the value to patch
16772 * @param context an object map of @angular/core symbol names to symbols which will be available
16773 * in the context of the compiled expression
16774 * @param sourceUrl a URL to use for the source map of the compiled expression
16775 * @param preStatements a collection of statements that should be evaluated before the expression.
16776 */
16777 jitExpression(def, context, sourceUrl, preStatements) {
16778 // The ConstantPool may contain Statements which declare variables used in the final expression.
16779 // Therefore, its statements need to precede the actual JIT operation. The final statement is a
16780 // declaration of $def which is set to the expression being compiled.
16781 const statements = [
16782 ...preStatements,
16783 new DeclareVarStmt('$def', def, undefined, [StmtModifier.Exported]),
16784 ];
16785 const res = this.jitEvaluator.evaluateStatements(sourceUrl, statements, new R3JitReflector(context), /* enableSourceMaps */ true);
16786 return res['$def'];
16787 }
16788 }
16789 const USE_CLASS = Object.keys({ useClass: null })[0];
16790 const USE_FACTORY = Object.keys({ useFactory: null })[0];
16791 const USE_VALUE = Object.keys({ useValue: null })[0];
16792 const USE_EXISTING = Object.keys({ useExisting: null })[0];
16793 const wrapReference = function (value) {
16794 const wrapped = new WrappedNodeExpr(value);
16795 return { value: wrapped, type: wrapped };
16796 };
16797 function convertToR3QueryMetadata(facade) {
16798 return Object.assign(Object.assign({}, facade), { predicate: Array.isArray(facade.predicate) ? facade.predicate :
16799 new WrappedNodeExpr(facade.predicate), read: facade.read ? new WrappedNodeExpr(facade.read) : null, static: facade.static, emitDistinctChangesOnly: facade.emitDistinctChangesOnly });
16800 }
16801 function convertQueryDeclarationToMetadata(declaration) {
16802 var _a, _b, _c, _d;
16803 return {
16804 propertyName: declaration.propertyName,
16805 first: (_a = declaration.first) !== null && _a !== void 0 ? _a : false,
16806 predicate: Array.isArray(declaration.predicate) ? declaration.predicate :
16807 new WrappedNodeExpr(declaration.predicate),
16808 descendants: (_b = declaration.descendants) !== null && _b !== void 0 ? _b : false,
16809 read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
16810 static: (_c = declaration.static) !== null && _c !== void 0 ? _c : false,
16811 emitDistinctChangesOnly: (_d = declaration.emitDistinctChangesOnly) !== null && _d !== void 0 ? _d : true,
16812 };
16813 }
16814 function convertDirectiveFacadeToMetadata(facade) {
16815 const inputsFromMetadata = parseInputOutputs(facade.inputs || []);
16816 const outputsFromMetadata = parseInputOutputs(facade.outputs || []);
16817 const propMetadata = facade.propMetadata;
16818 const inputsFromType = {};
16819 const outputsFromType = {};
16820 for (const field in propMetadata) {
16821 if (propMetadata.hasOwnProperty(field)) {
16822 propMetadata[field].forEach(ann => {
16823 if (isInput(ann)) {
16824 inputsFromType[field] =
16825 ann.bindingPropertyName ? [ann.bindingPropertyName, field] : field;
16826 }
16827 else if (isOutput(ann)) {
16828 outputsFromType[field] = ann.bindingPropertyName || field;
16829 }
16830 });
16831 }
16832 }
16833 return Object.assign(Object.assign({}, facade), { typeSourceSpan: facade.typeSourceSpan, type: wrapReference(facade.type), internalType: new WrappedNodeExpr(facade.type), deps: convertR3DependencyMetadataArray(facade.deps), host: extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host), inputs: Object.assign(Object.assign({}, inputsFromMetadata), inputsFromType), outputs: Object.assign(Object.assign({}, outputsFromMetadata), outputsFromType), queries: facade.queries.map(convertToR3QueryMetadata), providers: facade.providers != null ? new WrappedNodeExpr(facade.providers) : null, viewQueries: facade.viewQueries.map(convertToR3QueryMetadata), fullInheritance: false });
16834 }
16835 function convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan) {
16836 var _a, _b, _c, _d, _e, _f, _g, _h;
16837 return {
16838 name: declaration.type.name,
16839 type: wrapReference(declaration.type),
16840 typeSourceSpan,
16841 internalType: new WrappedNodeExpr(declaration.type),
16842 selector: (_a = declaration.selector) !== null && _a !== void 0 ? _a : null,
16843 inputs: (_b = declaration.inputs) !== null && _b !== void 0 ? _b : {},
16844 outputs: (_c = declaration.outputs) !== null && _c !== void 0 ? _c : {},
16845 host: convertHostDeclarationToMetadata(declaration.host),
16846 queries: ((_d = declaration.queries) !== null && _d !== void 0 ? _d : []).map(convertQueryDeclarationToMetadata),
16847 viewQueries: ((_e = declaration.viewQueries) !== null && _e !== void 0 ? _e : []).map(convertQueryDeclarationToMetadata),
16848 providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) :
16849 null,
16850 exportAs: (_f = declaration.exportAs) !== null && _f !== void 0 ? _f : null,
16851 usesInheritance: (_g = declaration.usesInheritance) !== null && _g !== void 0 ? _g : false,
16852 lifecycle: { usesOnChanges: (_h = declaration.usesOnChanges) !== null && _h !== void 0 ? _h : false },
16853 deps: null,
16854 typeArgumentCount: 0,
16855 fullInheritance: false,
16856 };
16857 }
16858 function convertHostDeclarationToMetadata(host = {}) {
16859 var _a, _b, _c;
16860 return {
16861 attributes: convertOpaqueValuesToExpressions((_a = host.attributes) !== null && _a !== void 0 ? _a : {}),
16862 listeners: (_b = host.listeners) !== null && _b !== void 0 ? _b : {},
16863 properties: (_c = host.properties) !== null && _c !== void 0 ? _c : {},
16864 specialAttributes: {
16865 classAttr: host.classAttribute,
16866 styleAttr: host.styleAttribute,
16867 },
16868 };
16869 }
16870 function convertOpaqueValuesToExpressions(obj) {
16871 const result = {};
16872 for (const key of Object.keys(obj)) {
16873 result[key] = new WrappedNodeExpr(obj[key]);
16874 }
16875 return result;
16876 }
16877 function convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl) {
16878 var _a, _b, _c, _d, _e;
16879 const { template, interpolation } = parseJitTemplate(declaration.template, declaration.type.name, sourceMapUrl, (_a = declaration.preserveWhitespaces) !== null && _a !== void 0 ? _a : false, declaration.interpolation);
16880 return Object.assign(Object.assign({}, convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan)), { template, styles: (_b = declaration.styles) !== null && _b !== void 0 ? _b : [], directives: ((_c = declaration.directives) !== null && _c !== void 0 ? _c : []).map(convertUsedDirectiveDeclarationToMetadata), pipes: convertUsedPipesToMetadata(declaration.pipes), viewProviders: declaration.viewProviders !== undefined ?
16881 new WrappedNodeExpr(declaration.viewProviders) :
16882 null, animations: declaration.animations !== undefined ? new WrappedNodeExpr(declaration.animations) :
16883 null, changeDetection: (_d = declaration.changeDetection) !== null && _d !== void 0 ? _d : ChangeDetectionStrategy.Default, encapsulation: (_e = declaration.encapsulation) !== null && _e !== void 0 ? _e : ViewEncapsulation.Emulated, interpolation, declarationListEmitMode: 2 /* ClosureResolved */, relativeContextFilePath: '', i18nUseExternalIds: true });
16884 }
16885 function convertUsedDirectiveDeclarationToMetadata(declaration) {
16886 var _a, _b, _c;
16887 return {
16888 selector: declaration.selector,
16889 type: new WrappedNodeExpr(declaration.type),
16890 inputs: (_a = declaration.inputs) !== null && _a !== void 0 ? _a : [],
16891 outputs: (_b = declaration.outputs) !== null && _b !== void 0 ? _b : [],
16892 exportAs: (_c = declaration.exportAs) !== null && _c !== void 0 ? _c : null,
16893 };
16894 }
16895 function convertUsedPipesToMetadata(declaredPipes) {
16896 const pipes = new Map();
16897 if (declaredPipes === undefined) {
16898 return pipes;
16899 }
16900 for (const pipeName of Object.keys(declaredPipes)) {
16901 const pipeType = declaredPipes[pipeName];
16902 pipes.set(pipeName, new WrappedNodeExpr(pipeType));
16903 }
16904 return pipes;
16905 }
16906 function parseJitTemplate(template, typeName, sourceMapUrl, preserveWhitespaces, interpolation) {
16907 const interpolationConfig = interpolation ? InterpolationConfig.fromArray(interpolation) : DEFAULT_INTERPOLATION_CONFIG;
16908 // Parse the template and check for errors.
16909 const parsed = parseTemplate(template, sourceMapUrl, { preserveWhitespaces: preserveWhitespaces, interpolationConfig });
16910 if (parsed.errors !== null) {
16911 const errors = parsed.errors.map(err => err.toString()).join(', ');
16912 throw new Error(`Errors during JIT compilation of template for ${typeName}: ${errors}`);
16913 }
16914 return { template: parsed, interpolation: interpolationConfig };
16915 }
16916 function wrapExpression(obj, property) {
16917 if (obj.hasOwnProperty(property)) {
16918 return new WrappedNodeExpr(obj[property]);
16919 }
16920 else {
16921 return undefined;
16922 }
16923 }
16924 function computeProvidedIn(providedIn) {
16925 if (providedIn == null || typeof providedIn === 'string') {
16926 return new LiteralExpr(providedIn);
16927 }
16928 else {
16929 return new WrappedNodeExpr(providedIn);
16930 }
16931 }
16932 function convertR3DependencyMetadata(facade) {
16933 let tokenExpr;
16934 if (facade.token === null) {
16935 tokenExpr = new LiteralExpr(null);
16936 }
16937 else if (facade.resolved === R3ResolvedDependencyType.Attribute) {
16938 tokenExpr = new LiteralExpr(facade.token);
16939 }
16940 else {
16941 tokenExpr = new WrappedNodeExpr(facade.token);
16942 }
16943 return {
16944 token: tokenExpr,
16945 attribute: null,
16946 resolved: facade.resolved,
16947 host: facade.host,
16948 optional: facade.optional,
16949 self: facade.self,
16950 skipSelf: facade.skipSelf,
16951 };
16952 }
16953 function convertR3DependencyMetadataArray(facades) {
16954 return facades == null ? null : facades.map(convertR3DependencyMetadata);
16955 }
16956 function extractHostBindings(propMetadata, sourceSpan, host) {
16957 // First parse the declarations from the metadata.
16958 const bindings = parseHostBindings(host || {});
16959 // After that check host bindings for errors
16960 const errors = verifyHostBindings(bindings, sourceSpan);
16961 if (errors.length) {
16962 throw new Error(errors.map((error) => error.msg).join('\n'));
16963 }
16964 // Next, loop over the properties of the object, looking for @HostBinding and @HostListener.
16965 for (const field in propMetadata) {
16966 if (propMetadata.hasOwnProperty(field)) {
16967 propMetadata[field].forEach(ann => {
16968 if (isHostBinding(ann)) {
16969 // Since this is a decorator, we know that the value is a class member. Always access it
16970 // through `this` so that further down the line it can't be confused for a literal value
16971 // (e.g. if there's a property called `true`).
16972 bindings.properties[ann.hostPropertyName || field] =
16973 getSafePropertyAccessString('this', field);
16974 }
16975 else if (isHostListener(ann)) {
16976 bindings.listeners[ann.eventName || field] = `${field}(${(ann.args || []).join(',')})`;
16977 }
16978 });
16979 }
16980 }
16981 return bindings;
16982 }
16983 function isHostBinding(value) {
16984 return value.ngMetadataName === 'HostBinding';
16985 }
16986 function isHostListener(value) {
16987 return value.ngMetadataName === 'HostListener';
16988 }
16989 function isInput(value) {
16990 return value.ngMetadataName === 'Input';
16991 }
16992 function isOutput(value) {
16993 return value.ngMetadataName === 'Output';
16994 }
16995 function parseInputOutputs(values) {
16996 return values.reduce((map, value) => {
16997 const [field, property] = value.split(',').map(piece => piece.trim());
16998 map[field] = property || field;
16999 return map;
17000 }, {});
17001 }
17002 function publishFacade(global) {
17003 const ng = global.ng || (global.ng = {});
17004 ng.ɵcompilerFacade = new CompilerFacadeImpl();
17005 }
17006
17007 /**
17008 * @license
17009 * Copyright Google LLC All Rights Reserved.
17010 *
17011 * Use of this source code is governed by an MIT-style license that can be
17012 * found in the LICENSE file at https://angular.io/license
17013 */
17014 const VERSION$1 = new Version('11.2.0');
17015
17016 /**
17017 * @license
17018 * Copyright Google LLC All Rights Reserved.
17019 *
17020 * Use of this source code is governed by an MIT-style license that can be
17021 * found in the LICENSE file at https://angular.io/license
17022 */
17023 var _VisitorMode;
17024 (function (_VisitorMode) {
17025 _VisitorMode[_VisitorMode["Extract"] = 0] = "Extract";
17026 _VisitorMode[_VisitorMode["Merge"] = 1] = "Merge";
17027 })(_VisitorMode || (_VisitorMode = {}));
17028
17029 /**
17030 * @license
17031 * Copyright Google LLC All Rights Reserved.
17032 *
17033 * Use of this source code is governed by an MIT-style license that can be
17034 * found in the LICENSE file at https://angular.io/license
17035 */
17036 var LifecycleHooks;
17037 (function (LifecycleHooks) {
17038 LifecycleHooks[LifecycleHooks["OnInit"] = 0] = "OnInit";
17039 LifecycleHooks[LifecycleHooks["OnDestroy"] = 1] = "OnDestroy";
17040 LifecycleHooks[LifecycleHooks["DoCheck"] = 2] = "DoCheck";
17041 LifecycleHooks[LifecycleHooks["OnChanges"] = 3] = "OnChanges";
17042 LifecycleHooks[LifecycleHooks["AfterContentInit"] = 4] = "AfterContentInit";
17043 LifecycleHooks[LifecycleHooks["AfterContentChecked"] = 5] = "AfterContentChecked";
17044 LifecycleHooks[LifecycleHooks["AfterViewInit"] = 6] = "AfterViewInit";
17045 LifecycleHooks[LifecycleHooks["AfterViewChecked"] = 7] = "AfterViewChecked";
17046 })(LifecycleHooks || (LifecycleHooks = {}));
17047 const LIFECYCLE_HOOKS_VALUES = [
17048 LifecycleHooks.OnInit, LifecycleHooks.OnDestroy, LifecycleHooks.DoCheck, LifecycleHooks.OnChanges,
17049 LifecycleHooks.AfterContentInit, LifecycleHooks.AfterContentChecked, LifecycleHooks.AfterViewInit,
17050 LifecycleHooks.AfterViewChecked
17051 ];
17052
17053 /**
17054 * @license
17055 * Copyright Google LLC All Rights Reserved.
17056 *
17057 * Use of this source code is governed by an MIT-style license that can be
17058 * found in the LICENSE file at https://angular.io/license
17059 */
17060 const LOG_VAR = variable('_l');
17061
17062 /**
17063 * @license
17064 * Copyright Google LLC All Rights Reserved.
17065 *
17066 * Use of this source code is governed by an MIT-style license that can be
17067 * found in the LICENSE file at https://angular.io/license
17068 */
17069 const LOG_VAR$1 = variable('_l');
17070 const VIEW_VAR = variable('_v');
17071 const CHECK_VAR = variable('_ck');
17072 const COMP_VAR = variable('_co');
17073 const EVENT_NAME_VAR = variable('en');
17074 const ALLOW_DEFAULT_VAR = variable(`ad`);
17075
17076 /**
17077 * @license
17078 * Copyright Google LLC All Rights Reserved.
17079 *
17080 * Use of this source code is governed by an MIT-style license that can be
17081 * found in the LICENSE file at https://angular.io/license
17082 */
17083 /**
17084 * The index of each URI component in the return value of goog.uri.utils.split.
17085 * @enum {number}
17086 */
17087 var _ComponentIndex;
17088 (function (_ComponentIndex) {
17089 _ComponentIndex[_ComponentIndex["Scheme"] = 1] = "Scheme";
17090 _ComponentIndex[_ComponentIndex["UserInfo"] = 2] = "UserInfo";
17091 _ComponentIndex[_ComponentIndex["Domain"] = 3] = "Domain";
17092 _ComponentIndex[_ComponentIndex["Port"] = 4] = "Port";
17093 _ComponentIndex[_ComponentIndex["Path"] = 5] = "Path";
17094 _ComponentIndex[_ComponentIndex["QueryData"] = 6] = "QueryData";
17095 _ComponentIndex[_ComponentIndex["Fragment"] = 7] = "Fragment";
17096 })(_ComponentIndex || (_ComponentIndex = {}));
17097
17098 /**
17099 * @license
17100 * Copyright Google LLC All Rights Reserved.
17101 *
17102 * Use of this source code is governed by an MIT-style license that can be
17103 * found in the LICENSE file at https://angular.io/license
17104 */
17105 /**
17106 * Processes `Target`s with a given set of directives and performs a binding operation, which
17107 * returns an object similar to TypeScript's `ts.TypeChecker` that contains knowledge about the
17108 * target.
17109 */
17110 class R3TargetBinder {
17111 constructor(directiveMatcher) {
17112 this.directiveMatcher = directiveMatcher;
17113 }
17114 /**
17115 * Perform a binding operation on the given `Target` and return a `BoundTarget` which contains
17116 * metadata about the types referenced in the template.
17117 */
17118 bind(target) {
17119 if (!target.template) {
17120 // TODO(alxhub): handle targets which contain things like HostBindings, etc.
17121 throw new Error('Binding without a template not yet supported');
17122 }
17123 // First, parse the template into a `Scope` structure. This operation captures the syntactic
17124 // scopes in the template and makes them available for later use.
17125 const scope = Scope.apply(target.template);
17126 // Use the `Scope` to extract the entities present at every level of the template.
17127 const templateEntities = extractTemplateEntities(scope);
17128 // Next, perform directive matching on the template using the `DirectiveBinder`. This returns:
17129 // - directives: Map of nodes (elements & ng-templates) to the directives on them.
17130 // - bindings: Map of inputs, outputs, and attributes to the directive/element that claims
17131 // them. TODO(alxhub): handle multiple directives claiming an input/output/etc.
17132 // - references: Map of #references to their targets.
17133 const { directives, bindings, references } = DirectiveBinder.apply(target.template, this.directiveMatcher);
17134 // Finally, run the TemplateBinder to bind references, variables, and other entities within the
17135 // template. This extracts all the metadata that doesn't depend on directive matching.
17136 const { expressions, symbols, nestingLevel, usedPipes } = TemplateBinder.apply(target.template, scope);
17137 return new R3BoundTarget(target, directives, bindings, references, expressions, symbols, nestingLevel, templateEntities, usedPipes);
17138 }
17139 }
17140 /**
17141 * Represents a binding scope within a template.
17142 *
17143 * Any variables, references, or other named entities declared within the template will
17144 * be captured and available by name in `namedEntities`. Additionally, child templates will
17145 * be analyzed and have their child `Scope`s available in `childScopes`.
17146 */
17147 class Scope {
17148 constructor(parentScope, template) {
17149 this.parentScope = parentScope;
17150 this.template = template;
17151 /**
17152 * Named members of the `Scope`, such as `Reference`s or `Variable`s.
17153 */
17154 this.namedEntities = new Map();
17155 /**
17156 * Child `Scope`s for immediately nested `Template`s.
17157 */
17158 this.childScopes = new Map();
17159 }
17160 static newRootScope() {
17161 return new Scope(null, null);
17162 }
17163 /**
17164 * Process a template (either as a `Template` sub-template with variables, or a plain array of
17165 * template `Node`s) and construct its `Scope`.
17166 */
17167 static apply(template) {
17168 const scope = Scope.newRootScope();
17169 scope.ingest(template);
17170 return scope;
17171 }
17172 /**
17173 * Internal method to process the template and populate the `Scope`.
17174 */
17175 ingest(template) {
17176 if (template instanceof Template) {
17177 // Variables on an <ng-template> are defined in the inner scope.
17178 template.variables.forEach(node => this.visitVariable(node));
17179 // Process the nodes of the template.
17180 template.children.forEach(node => node.visit(this));
17181 }
17182 else {
17183 // No overarching `Template` instance, so process the nodes directly.
17184 template.forEach(node => node.visit(this));
17185 }
17186 }
17187 visitElement(element) {
17188 // `Element`s in the template may have `Reference`s which are captured in the scope.
17189 element.references.forEach(node => this.visitReference(node));
17190 // Recurse into the `Element`'s children.
17191 element.children.forEach(node => node.visit(this));
17192 }
17193 visitTemplate(template) {
17194 // References on a <ng-template> are defined in the outer scope, so capture them before
17195 // processing the template's child scope.
17196 template.references.forEach(node => this.visitReference(node));
17197 // Next, create an inner scope and process the template within it.
17198 const scope = new Scope(this, template);
17199 scope.ingest(template);
17200 this.childScopes.set(template, scope);
17201 }
17202 visitVariable(variable) {
17203 // Declare the variable if it's not already.
17204 this.maybeDeclare(variable);
17205 }
17206 visitReference(reference) {
17207 // Declare the variable if it's not already.
17208 this.maybeDeclare(reference);
17209 }
17210 // Unused visitors.
17211 visitContent(content) { }
17212 visitBoundAttribute(attr) { }
17213 visitBoundEvent(event) { }
17214 visitBoundText(text) { }
17215 visitText(text) { }
17216 visitTextAttribute(attr) { }
17217 visitIcu(icu) { }
17218 maybeDeclare(thing) {
17219 // Declare something with a name, as long as that name isn't taken.
17220 if (!this.namedEntities.has(thing.name)) {
17221 this.namedEntities.set(thing.name, thing);
17222 }
17223 }
17224 /**
17225 * Look up a variable within this `Scope`.
17226 *
17227 * This can recurse into a parent `Scope` if it's available.
17228 */
17229 lookup(name) {
17230 if (this.namedEntities.has(name)) {
17231 // Found in the local scope.
17232 return this.namedEntities.get(name);
17233 }
17234 else if (this.parentScope !== null) {
17235 // Not in the local scope, but there's a parent scope so check there.
17236 return this.parentScope.lookup(name);
17237 }
17238 else {
17239 // At the top level and it wasn't found.
17240 return null;
17241 }
17242 }
17243 /**
17244 * Get the child scope for a `Template`.
17245 *
17246 * This should always be defined.
17247 */
17248 getChildScope(template) {
17249 const res = this.childScopes.get(template);
17250 if (res === undefined) {
17251 throw new Error(`Assertion error: child scope for ${template} not found`);
17252 }
17253 return res;
17254 }
17255 }
17256 /**
17257 * Processes a template and matches directives on nodes (elements and templates).
17258 *
17259 * Usually used via the static `apply()` method.
17260 */
17261 class DirectiveBinder {
17262 constructor(matcher, directives, bindings, references) {
17263 this.matcher = matcher;
17264 this.directives = directives;
17265 this.bindings = bindings;
17266 this.references = references;
17267 }
17268 /**
17269 * Process a template (list of `Node`s) and perform directive matching against each node.
17270 *
17271 * @param template the list of template `Node`s to match (recursively).
17272 * @param selectorMatcher a `SelectorMatcher` containing the directives that are in scope for
17273 * this template.
17274 * @returns three maps which contain information about directives in the template: the
17275 * `directives` map which lists directives matched on each node, the `bindings` map which
17276 * indicates which directives claimed which bindings (inputs, outputs, etc), and the `references`
17277 * map which resolves #references (`Reference`s) within the template to the named directive or
17278 * template node.
17279 */
17280 static apply(template, selectorMatcher) {
17281 const directives = new Map();
17282 const bindings = new Map();
17283 const references = new Map();
17284 const matcher = new DirectiveBinder(selectorMatcher, directives, bindings, references);
17285 matcher.ingest(template);
17286 return { directives, bindings, references };
17287 }
17288 ingest(template) {
17289 template.forEach(node => node.visit(this));
17290 }
17291 visitElement(element) {
17292 this.visitElementOrTemplate(element.name, element);
17293 }
17294 visitTemplate(template) {
17295 this.visitElementOrTemplate('ng-template', template);
17296 }
17297 visitElementOrTemplate(elementName, node) {
17298 // First, determine the HTML shape of the node for the purpose of directive matching.
17299 // Do this by building up a `CssSelector` for the node.
17300 const cssSelector = createCssSelector(elementName, getAttrsForDirectiveMatching(node));
17301 // Next, use the `SelectorMatcher` to get the list of directives on the node.
17302 const directives = [];
17303 this.matcher.match(cssSelector, (_, directive) => directives.push(directive));
17304 if (directives.length > 0) {
17305 this.directives.set(node, directives);
17306 }
17307 // Resolve any references that are created on this node.
17308 node.references.forEach(ref => {
17309 let dirTarget = null;
17310 // If the reference expression is empty, then it matches the "primary" directive on the node
17311 // (if there is one). Otherwise it matches the host node itself (either an element or
17312 // <ng-template> node).
17313 if (ref.value.trim() === '') {
17314 // This could be a reference to a component if there is one.
17315 dirTarget = directives.find(dir => dir.isComponent) || null;
17316 }
17317 else {
17318 // This should be a reference to a directive exported via exportAs.
17319 dirTarget =
17320 directives.find(dir => dir.exportAs !== null && dir.exportAs.some(value => value === ref.value)) ||
17321 null;
17322 // Check if a matching directive was found.
17323 if (dirTarget === null) {
17324 // No matching directive was found - this reference points to an unknown target. Leave it
17325 // unmapped.
17326 return;
17327 }
17328 }
17329 if (dirTarget !== null) {
17330 // This reference points to a directive.
17331 this.references.set(ref, { directive: dirTarget, node });
17332 }
17333 else {
17334 // This reference points to the node itself.
17335 this.references.set(ref, node);
17336 }
17337 });
17338 const setAttributeBinding = (attribute, ioType) => {
17339 const dir = directives.find(dir => dir[ioType].hasBindingPropertyName(attribute.name));
17340 const binding = dir !== undefined ? dir : node;
17341 this.bindings.set(attribute, binding);
17342 };
17343 // Node inputs (bound attributes) and text attributes can be bound to an
17344 // input on a directive.
17345 node.inputs.forEach(input => setAttributeBinding(input, 'inputs'));
17346 node.attributes.forEach(attr => setAttributeBinding(attr, 'inputs'));
17347 if (node instanceof Template) {
17348 node.templateAttrs.forEach(attr => setAttributeBinding(attr, 'inputs'));
17349 }
17350 // Node outputs (bound events) can be bound to an output on a directive.
17351 node.outputs.forEach(output => setAttributeBinding(output, 'outputs'));
17352 // Recurse into the node's children.
17353 node.children.forEach(child => child.visit(this));
17354 }
17355 // Unused visitors.
17356 visitContent(content) { }
17357 visitVariable(variable) { }
17358 visitReference(reference) { }
17359 visitTextAttribute(attribute) { }
17360 visitBoundAttribute(attribute) { }
17361 visitBoundEvent(attribute) { }
17362 visitBoundAttributeOrEvent(node) { }
17363 visitText(text) { }
17364 visitBoundText(text) { }
17365 visitIcu(icu) { }
17366 }
17367 /**
17368 * Processes a template and extract metadata about expressions and symbols within.
17369 *
17370 * This is a companion to the `DirectiveBinder` that doesn't require knowledge of directives matched
17371 * within the template in order to operate.
17372 *
17373 * Expressions are visited by the superclass `RecursiveAstVisitor`, with custom logic provided
17374 * by overridden methods from that visitor.
17375 */
17376 class TemplateBinder extends RecursiveAstVisitor {
17377 constructor(bindings, symbols, usedPipes, nestingLevel, scope, template, level) {
17378 super();
17379 this.bindings = bindings;
17380 this.symbols = symbols;
17381 this.usedPipes = usedPipes;
17382 this.nestingLevel = nestingLevel;
17383 this.scope = scope;
17384 this.template = template;
17385 this.level = level;
17386 this.pipesUsed = [];
17387 // Save a bit of processing time by constructing this closure in advance.
17388 this.visitNode = (node) => node.visit(this);
17389 }
17390 // This method is defined to reconcile the type of TemplateBinder since both
17391 // RecursiveAstVisitor and Visitor define the visit() method in their
17392 // interfaces.
17393 visit(node, context) {
17394 if (node instanceof AST) {
17395 node.visit(this, context);
17396 }
17397 else {
17398 node.visit(this);
17399 }
17400 }
17401 /**
17402 * Process a template and extract metadata about expressions and symbols within.
17403 *
17404 * @param template the nodes of the template to process
17405 * @param scope the `Scope` of the template being processed.
17406 * @returns three maps which contain metadata about the template: `expressions` which interprets
17407 * special `AST` nodes in expressions as pointing to references or variables declared within the
17408 * template, `symbols` which maps those variables and references to the nested `Template` which
17409 * declares them, if any, and `nestingLevel` which associates each `Template` with a integer
17410 * nesting level (how many levels deep within the template structure the `Template` is), starting
17411 * at 1.
17412 */
17413 static apply(template, scope) {
17414 const expressions = new Map();
17415 const symbols = new Map();
17416 const nestingLevel = new Map();
17417 const usedPipes = new Set();
17418 // The top-level template has nesting level 0.
17419 const binder = new TemplateBinder(expressions, symbols, usedPipes, nestingLevel, scope, template instanceof Template ? template : null, 0);
17420 binder.ingest(template);
17421 return { expressions, symbols, nestingLevel, usedPipes };
17422 }
17423 ingest(template) {
17424 if (template instanceof Template) {
17425 // For <ng-template>s, process only variables and child nodes. Inputs, outputs, templateAttrs,
17426 // and references were all processed in the scope of the containing template.
17427 template.variables.forEach(this.visitNode);
17428 template.children.forEach(this.visitNode);
17429 // Set the nesting level.
17430 this.nestingLevel.set(template, this.level);
17431 }
17432 else {
17433 // Visit each node from the top-level template.
17434 template.forEach(this.visitNode);
17435 }
17436 }
17437 visitElement(element) {
17438 // Visit the inputs, outputs, and children of the element.
17439 element.inputs.forEach(this.visitNode);
17440 element.outputs.forEach(this.visitNode);
17441 element.children.forEach(this.visitNode);
17442 }
17443 visitTemplate(template) {
17444 // First, visit inputs, outputs and template attributes of the template node.
17445 template.inputs.forEach(this.visitNode);
17446 template.outputs.forEach(this.visitNode);
17447 template.templateAttrs.forEach(this.visitNode);
17448 // References are also evaluated in the outer context.
17449 template.references.forEach(this.visitNode);
17450 // Next, recurse into the template using its scope, and bumping the nesting level up by one.
17451 const childScope = this.scope.getChildScope(template);
17452 const binder = new TemplateBinder(this.bindings, this.symbols, this.usedPipes, this.nestingLevel, childScope, template, this.level + 1);
17453 binder.ingest(template);
17454 }
17455 visitVariable(variable) {
17456 // Register the `Variable` as a symbol in the current `Template`.
17457 if (this.template !== null) {
17458 this.symbols.set(variable, this.template);
17459 }
17460 }
17461 visitReference(reference) {
17462 // Register the `Reference` as a symbol in the current `Template`.
17463 if (this.template !== null) {
17464 this.symbols.set(reference, this.template);
17465 }
17466 }
17467 // Unused template visitors
17468 visitText(text) { }
17469 visitContent(content) { }
17470 visitTextAttribute(attribute) { }
17471 visitIcu(icu) {
17472 Object.keys(icu.vars).forEach(key => icu.vars[key].visit(this));
17473 Object.keys(icu.placeholders).forEach(key => icu.placeholders[key].visit(this));
17474 }
17475 // The remaining visitors are concerned with processing AST expressions within template bindings
17476 visitBoundAttribute(attribute) {
17477 attribute.value.visit(this);
17478 }
17479 visitBoundEvent(event) {
17480 event.handler.visit(this);
17481 }
17482 visitBoundText(text) {
17483 text.value.visit(this);
17484 }
17485 visitPipe(ast, context) {
17486 this.usedPipes.add(ast.name);
17487 return super.visitPipe(ast, context);
17488 }
17489 // These five types of AST expressions can refer to expression roots, which could be variables
17490 // or references in the current scope.
17491 visitPropertyRead(ast, context) {
17492 this.maybeMap(context, ast, ast.name);
17493 return super.visitPropertyRead(ast, context);
17494 }
17495 visitSafePropertyRead(ast, context) {
17496 this.maybeMap(context, ast, ast.name);
17497 return super.visitSafePropertyRead(ast, context);
17498 }
17499 visitPropertyWrite(ast, context) {
17500 this.maybeMap(context, ast, ast.name);
17501 return super.visitPropertyWrite(ast, context);
17502 }
17503 visitMethodCall(ast, context) {
17504 this.maybeMap(context, ast, ast.name);
17505 return super.visitMethodCall(ast, context);
17506 }
17507 visitSafeMethodCall(ast, context) {
17508 this.maybeMap(context, ast, ast.name);
17509 return super.visitSafeMethodCall(ast, context);
17510 }
17511 maybeMap(scope, ast, name) {
17512 // If the receiver of the expression isn't the `ImplicitReceiver`, this isn't the root of an
17513 // `AST` expression that maps to a `Variable` or `Reference`.
17514 if (!(ast.receiver instanceof ImplicitReceiver)) {
17515 return;
17516 }
17517 // Check whether the name exists in the current scope. If so, map it. Otherwise, the name is
17518 // probably a property on the top-level component context.
17519 let target = this.scope.lookup(name);
17520 if (target !== null) {
17521 this.bindings.set(ast, target);
17522 }
17523 }
17524 }
17525 /**
17526 * Metadata container for a `Target` that allows queries for specific bits of metadata.
17527 *
17528 * See `BoundTarget` for documentation on the individual methods.
17529 */
17530 class R3BoundTarget {
17531 constructor(target, directives, bindings, references, exprTargets, symbols, nestingLevel, templateEntities, usedPipes) {
17532 this.target = target;
17533 this.directives = directives;
17534 this.bindings = bindings;
17535 this.references = references;
17536 this.exprTargets = exprTargets;
17537 this.symbols = symbols;
17538 this.nestingLevel = nestingLevel;
17539 this.templateEntities = templateEntities;
17540 this.usedPipes = usedPipes;
17541 }
17542 getEntitiesInTemplateScope(template) {
17543 var _a;
17544 return (_a = this.templateEntities.get(template)) !== null && _a !== void 0 ? _a : new Set();
17545 }
17546 getDirectivesOfNode(node) {
17547 return this.directives.get(node) || null;
17548 }
17549 getReferenceTarget(ref) {
17550 return this.references.get(ref) || null;
17551 }
17552 getConsumerOfBinding(binding) {
17553 return this.bindings.get(binding) || null;
17554 }
17555 getExpressionTarget(expr) {
17556 return this.exprTargets.get(expr) || null;
17557 }
17558 getTemplateOfSymbol(symbol) {
17559 return this.symbols.get(symbol) || null;
17560 }
17561 getNestingLevel(template) {
17562 return this.nestingLevel.get(template) || 0;
17563 }
17564 getUsedDirectives() {
17565 const set = new Set();
17566 this.directives.forEach(dirs => dirs.forEach(dir => set.add(dir)));
17567 return Array.from(set.values());
17568 }
17569 getUsedPipes() {
17570 return Array.from(this.usedPipes);
17571 }
17572 }
17573 function extractTemplateEntities(rootScope) {
17574 const entityMap = new Map();
17575 function extractScopeEntities(scope) {
17576 if (entityMap.has(scope.template)) {
17577 return entityMap.get(scope.template);
17578 }
17579 const currentEntities = scope.namedEntities;
17580 let templateEntities;
17581 if (scope.parentScope !== null) {
17582 templateEntities = new Map([...extractScopeEntities(scope.parentScope), ...currentEntities]);
17583 }
17584 else {
17585 templateEntities = new Map(currentEntities);
17586 }
17587 entityMap.set(scope.template, templateEntities);
17588 return templateEntities;
17589 }
17590 const scopesToProcess = [rootScope];
17591 while (scopesToProcess.length > 0) {
17592 const scope = scopesToProcess.pop();
17593 for (const childScope of scope.childScopes.values()) {
17594 scopesToProcess.push(childScope);
17595 }
17596 extractScopeEntities(scope);
17597 }
17598 const templateEntities = new Map();
17599 for (const [template, entities] of entityMap) {
17600 templateEntities.set(template, new Set(entities.values()));
17601 }
17602 return templateEntities;
17603 }
17604
17605 /**
17606 * @license
17607 * Copyright Google LLC All Rights Reserved.
17608 *
17609 * Use of this source code is governed by an MIT-style license that can be
17610 * found in the LICENSE file at https://angular.io/license
17611 */
17612 /**
17613 * Creates an array literal expression from the given array, mapping all values to an expression
17614 * using the provided mapping function. If the array is empty or null, then null is returned.
17615 *
17616 * @param values The array to transfer into literal array expression.
17617 * @param mapper The logic to use for creating an expression for the array's values.
17618 * @returns An array literal expression representing `values`, or null if `values` is empty or
17619 * is itself null.
17620 */
17621 function toOptionalLiteralArray(values, mapper) {
17622 if (values === null || values.length === 0) {
17623 return null;
17624 }
17625 return literalArr(values.map(value => mapper(value)));
17626 }
17627 /**
17628 * Creates an object literal expression from the given object, mapping all values to an expression
17629 * using the provided mapping function. If the object has no keys, then null is returned.
17630 *
17631 * @param object The object to transfer into an object literal expression.
17632 * @param mapper The logic to use for creating an expression for the object's values.
17633 * @returns An object literal expression representing `object`, or null if `object` does not have
17634 * any keys.
17635 */
17636 function toOptionalLiteralMap(object, mapper) {
17637 const entries = Object.keys(object).map(key => {
17638 const value = object[key];
17639 return { key, value: mapper(value), quoted: true };
17640 });
17641 if (entries.length > 0) {
17642 return literalMap(entries);
17643 }
17644 else {
17645 return null;
17646 }
17647 }
17648
17649 /**
17650 * @license
17651 * Copyright Google LLC All Rights Reserved.
17652 *
17653 * Use of this source code is governed by an MIT-style license that can be
17654 * found in the LICENSE file at https://angular.io/license
17655 */
17656 /**
17657 * Compile a directive declaration defined by the `R3DirectiveMetadata`.
17658 */
17659 function compileDeclareDirectiveFromMetadata(meta) {
17660 const definitionMap = createDirectiveDefinitionMap(meta);
17661 const expression = importExpr(Identifiers$1.declareDirective).callFn([definitionMap.toLiteralMap()]);
17662 const type = createDirectiveType(meta);
17663 return { expression, type };
17664 }
17665 /**
17666 * Gathers the declaration fields for a directive into a `DefinitionMap`. This allows for reusing
17667 * this logic for components, as they extend the directive metadata.
17668 */
17669 function createDirectiveDefinitionMap(meta) {
17670 const definitionMap = new DefinitionMap();
17671 definitionMap.set('version', literal('11.2.0'));
17672 // e.g. `type: MyDirective`
17673 definitionMap.set('type', meta.internalType);
17674 // e.g. `selector: 'some-dir'`
17675 if (meta.selector !== null) {
17676 definitionMap.set('selector', literal(meta.selector));
17677 }
17678 definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
17679 definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
17680 definitionMap.set('host', compileHostMetadata(meta.host));
17681 definitionMap.set('providers', meta.providers);
17682 if (meta.queries.length > 0) {
17683 definitionMap.set('queries', literalArr(meta.queries.map(compileQuery)));
17684 }
17685 if (meta.viewQueries.length > 0) {
17686 definitionMap.set('viewQueries', literalArr(meta.viewQueries.map(compileQuery)));
17687 }
17688 if (meta.exportAs !== null) {
17689 definitionMap.set('exportAs', asLiteral(meta.exportAs));
17690 }
17691 if (meta.usesInheritance) {
17692 definitionMap.set('usesInheritance', literal(true));
17693 }
17694 if (meta.lifecycle.usesOnChanges) {
17695 definitionMap.set('usesOnChanges', literal(true));
17696 }
17697 definitionMap.set('ngImport', importExpr(Identifiers$1.core));
17698 return definitionMap;
17699 }
17700 /**
17701 * Compiles the metadata of a single query into its partial declaration form as declared
17702 * by `R3DeclareQueryMetadata`.
17703 */
17704 function compileQuery(query) {
17705 const meta = new DefinitionMap();
17706 meta.set('propertyName', literal(query.propertyName));
17707 if (query.first) {
17708 meta.set('first', literal(true));
17709 }
17710 meta.set('predicate', Array.isArray(query.predicate) ? asLiteral(query.predicate) : query.predicate);
17711 if (!query.emitDistinctChangesOnly) {
17712 // `emitDistinctChangesOnly` is special because in future we expect it to be `true`. For this
17713 // reason the absence should be interpreted as `true`.
17714 meta.set('emitDistinctChangesOnly', literal(false));
17715 }
17716 if (query.descendants) {
17717 meta.set('descendants', literal(true));
17718 }
17719 meta.set('read', query.read);
17720 if (query.static) {
17721 meta.set('static', literal(true));
17722 }
17723 return meta.toLiteralMap();
17724 }
17725 /**
17726 * Compiles the host metadata into its partial declaration form as declared
17727 * in `R3DeclareDirectiveMetadata['host']`
17728 */
17729 function compileHostMetadata(meta) {
17730 const hostMetadata = new DefinitionMap();
17731 hostMetadata.set('attributes', toOptionalLiteralMap(meta.attributes, expression => expression));
17732 hostMetadata.set('listeners', toOptionalLiteralMap(meta.listeners, literal));
17733 hostMetadata.set('properties', toOptionalLiteralMap(meta.properties, literal));
17734 if (meta.specialAttributes.styleAttr) {
17735 hostMetadata.set('styleAttribute', literal(meta.specialAttributes.styleAttr));
17736 }
17737 if (meta.specialAttributes.classAttr) {
17738 hostMetadata.set('classAttribute', literal(meta.specialAttributes.classAttr));
17739 }
17740 if (hostMetadata.values.length > 0) {
17741 return hostMetadata.toLiteralMap();
17742 }
17743 else {
17744 return null;
17745 }
17746 }
17747
17748 /**
17749 * @license
17750 * Copyright Google LLC All Rights Reserved.
17751 *
17752 * Use of this source code is governed by an MIT-style license that can be
17753 * found in the LICENSE file at https://angular.io/license
17754 */
17755 /**
17756 * Compile a component declaration defined by the `R3ComponentMetadata`.
17757 */
17758 function compileDeclareComponentFromMetadata(meta, template) {
17759 const definitionMap = createComponentDefinitionMap(meta, template);
17760 const expression = importExpr(Identifiers$1.declareComponent).callFn([definitionMap.toLiteralMap()]);
17761 const type = createComponentType(meta);
17762 return { expression, type };
17763 }
17764 /**
17765 * Gathers the declaration fields for a component into a `DefinitionMap`.
17766 */
17767 function createComponentDefinitionMap(meta, template) {
17768 const definitionMap = createDirectiveDefinitionMap(meta);
17769 definitionMap.set('template', getTemplateExpression(template));
17770 if (template.isInline) {
17771 definitionMap.set('isInline', literal(true));
17772 }
17773 definitionMap.set('styles', toOptionalLiteralArray(meta.styles, literal));
17774 definitionMap.set('directives', compileUsedDirectiveMetadata(meta));
17775 definitionMap.set('pipes', compileUsedPipeMetadata(meta));
17776 definitionMap.set('viewProviders', meta.viewProviders);
17777 definitionMap.set('animations', meta.animations);
17778 if (meta.changeDetection !== undefined) {
17779 definitionMap.set('changeDetection', importExpr(Identifiers$1.ChangeDetectionStrategy)
17780 .prop(ChangeDetectionStrategy[meta.changeDetection]));
17781 }
17782 if (meta.encapsulation !== ViewEncapsulation.Emulated) {
17783 definitionMap.set('encapsulation', importExpr(Identifiers$1.ViewEncapsulation).prop(ViewEncapsulation[meta.encapsulation]));
17784 }
17785 if (meta.interpolation !== DEFAULT_INTERPOLATION_CONFIG) {
17786 definitionMap.set('interpolation', literalArr([literal(meta.interpolation.start), literal(meta.interpolation.end)]));
17787 }
17788 if (template.preserveWhitespaces === true) {
17789 definitionMap.set('preserveWhitespaces', literal(true));
17790 }
17791 return definitionMap;
17792 }
17793 function getTemplateExpression(template) {
17794 if (typeof template.template === 'string') {
17795 if (template.isInline) {
17796 // The template is inline but not a simple literal string, so give up with trying to
17797 // source-map it and just return a simple literal here.
17798 return literal(template.template);
17799 }
17800 else {
17801 // The template is external so we must synthesize an expression node with the appropriate
17802 // source-span.
17803 const contents = template.template;
17804 const file = new ParseSourceFile(contents, template.templateUrl);
17805 const start = new ParseLocation(file, 0, 0, 0);
17806 const end = computeEndLocation(file, contents);
17807 const span = new ParseSourceSpan(start, end);
17808 return literal(contents, null, span);
17809 }
17810 }
17811 else {
17812 // The template is inline so we can just reuse the current expression node.
17813 return template.template;
17814 }
17815 }
17816 function computeEndLocation(file, contents) {
17817 const length = contents.length;
17818 let lineStart = 0;
17819 let lastLineStart = 0;
17820 let line = 0;
17821 do {
17822 lineStart = contents.indexOf('\n', lastLineStart);
17823 if (lineStart !== -1) {
17824 lastLineStart = lineStart + 1;
17825 line++;
17826 }
17827 } while (lineStart !== -1);
17828 return new ParseLocation(file, length, line, length - lastLineStart);
17829 }
17830 /**
17831 * Compiles the directives as registered in the component metadata into an array literal of the
17832 * individual directives. If the component does not use any directives, then null is returned.
17833 */
17834 function compileUsedDirectiveMetadata(meta) {
17835 const wrapType = meta.declarationListEmitMode !== 0 /* Direct */ ?
17836 generateForwardRef :
17837 (expr) => expr;
17838 return toOptionalLiteralArray(meta.directives, directive => {
17839 const dirMeta = new DefinitionMap();
17840 dirMeta.set('type', wrapType(directive.type));
17841 dirMeta.set('selector', literal(directive.selector));
17842 dirMeta.set('inputs', toOptionalLiteralArray(directive.inputs, literal));
17843 dirMeta.set('outputs', toOptionalLiteralArray(directive.outputs, literal));
17844 dirMeta.set('exportAs', toOptionalLiteralArray(directive.exportAs, literal));
17845 return dirMeta.toLiteralMap();
17846 });
17847 }
17848 /**
17849 * Compiles the pipes as registered in the component metadata into an object literal, where the
17850 * pipe's name is used as key and a reference to its type as value. If the component does not use
17851 * any pipes, then null is returned.
17852 */
17853 function compileUsedPipeMetadata(meta) {
17854 if (meta.pipes.size === 0) {
17855 return null;
17856 }
17857 const wrapType = meta.declarationListEmitMode !== 0 /* Direct */ ?
17858 generateForwardRef :
17859 (expr) => expr;
17860 const entries = [];
17861 for (const [name, pipe] of meta.pipes) {
17862 entries.push({ key: name, value: wrapType(pipe), quoted: true });
17863 }
17864 return literalMap(entries);
17865 }
17866 function generateForwardRef(expr) {
17867 return importExpr(Identifiers$1.forwardRef).callFn([fn([], [new ReturnStatement(expr)])]);
17868 }
17869
17870 /**
17871 * @license
17872 * Copyright Google LLC All Rights Reserved.
17873 *
17874 * Use of this source code is governed by an MIT-style license that can be
17875 * found in the LICENSE file at https://angular.io/license
17876 */
17877 // This file only reexports content of the `src` folder. Keep it that way.
17878 // This function call has a global side effects and publishes the compiler into global namespace for
17879 // the late binding of the Compiler to the @angular/core for jit compilation.
17880 publishFacade(_global);
17881
17882 /**
17883 * The default `FileSystem` that will always fail.
17884 *
17885 * This is a way of ensuring that the developer consciously chooses and
17886 * configures the `FileSystem` before using it; particularly important when
17887 * considering static functions like `absoluteFrom()` which rely on
17888 * the `FileSystem` under the hood.
17889 */
17890 class InvalidFileSystem {
17891 exists(path) {
17892 throw makeError();
17893 }
17894 readFile(path) {
17895 throw makeError();
17896 }
17897 readFileBuffer(path) {
17898 throw makeError();
17899 }
17900 writeFile(path, data, exclusive) {
17901 throw makeError();
17902 }
17903 removeFile(path) {
17904 throw makeError();
17905 }
17906 symlink(target, path) {
17907 throw makeError();
17908 }
17909 readdir(path) {
17910 throw makeError();
17911 }
17912 lstat(path) {
17913 throw makeError();
17914 }
17915 stat(path) {
17916 throw makeError();
17917 }
17918 pwd() {
17919 throw makeError();
17920 }
17921 chdir(path) {
17922 throw makeError();
17923 }
17924 extname(path) {
17925 throw makeError();
17926 }
17927 copyFile(from, to) {
17928 throw makeError();
17929 }
17930 moveFile(from, to) {
17931 throw makeError();
17932 }
17933 ensureDir(path) {
17934 throw makeError();
17935 }
17936 removeDeep(path) {
17937 throw makeError();
17938 }
17939 isCaseSensitive() {
17940 throw makeError();
17941 }
17942 resolve(...paths) {
17943 throw makeError();
17944 }
17945 dirname(file) {
17946 throw makeError();
17947 }
17948 join(basePath, ...paths) {
17949 throw makeError();
17950 }
17951 isRoot(path) {
17952 throw makeError();
17953 }
17954 isRooted(path) {
17955 throw makeError();
17956 }
17957 relative(from, to) {
17958 throw makeError();
17959 }
17960 basename(filePath, extension) {
17961 throw makeError();
17962 }
17963 realpath(filePath) {
17964 throw makeError();
17965 }
17966 getDefaultLibLocation() {
17967 throw makeError();
17968 }
17969 normalize(path) {
17970 throw makeError();
17971 }
17972 }
17973 function makeError() {
17974 return new Error('FileSystem has not been configured. Please call `setFileSystem()` before calling this method.');
17975 }
17976
17977 const TS_DTS_JS_EXTENSION = /(?:\.d)?\.ts$|\.js$/;
17978 /**
17979 * Remove a .ts, .d.ts, or .js extension from a file name.
17980 */
17981 function stripExtension(path) {
17982 return path.replace(TS_DTS_JS_EXTENSION, '');
17983 }
17984 function getSourceFileOrError(program, fileName) {
17985 const sf = program.getSourceFile(fileName);
17986 if (sf === undefined) {
17987 throw new Error(`Program does not contain "${fileName}" - available files are ${program.getSourceFiles().map(sf => sf.fileName).join(', ')}`);
17988 }
17989 return sf;
17990 }
17991
17992 let fs = new InvalidFileSystem();
17993 function getFileSystem() {
17994 return fs;
17995 }
17996 function setFileSystem(fileSystem) {
17997 fs = fileSystem;
17998 }
17999 /**
18000 * Convert the path `path` to an `AbsoluteFsPath`, throwing an error if it's not an absolute path.
18001 */
18002 function absoluteFrom(path) {
18003 if (!fs.isRooted(path)) {
18004 throw new Error(`Internal Error: absoluteFrom(${path}): path is not absolute`);
18005 }
18006 return fs.resolve(path);
18007 }
18008 /**
18009 * Extract an `AbsoluteFsPath` from a `ts.SourceFile`.
18010 */
18011 function absoluteFromSourceFile(sf) {
18012 return fs.resolve(sf.fileName);
18013 }
18014 /**
18015 * Static access to `dirname`.
18016 */
18017 function dirname(file) {
18018 return fs.dirname(file);
18019 }
18020 /**
18021 * Static access to `join`.
18022 */
18023 function join(basePath, ...paths) {
18024 return fs.join(basePath, ...paths);
18025 }
18026 /**
18027 * Static access to `resolve`s.
18028 */
18029 function resolve(basePath, ...paths) {
18030 return fs.resolve(basePath, ...paths);
18031 }
18032 /**
18033 * Static access to `isRooted`.
18034 */
18035 function isRooted(path) {
18036 return fs.isRooted(path);
18037 }
18038 /**
18039 * Static access to `relative`.
18040 */
18041 function relative(from, to) {
18042 return fs.relative(from, to);
18043 }
18044 /**
18045 * Returns true if the given path is locally relative.
18046 *
18047 * This is used to work out if the given path is relative (i.e. not absolute) but also is not
18048 * escaping the current directory.
18049 */
18050 function isLocalRelativePath(relativePath) {
18051 return !isRooted(relativePath) && !relativePath.startsWith('..');
18052 }
18053 /**
18054 * Converts a path to a form suitable for use as a relative module import specifier.
18055 *
18056 * In other words it adds the `./` to the path if it is locally relative.
18057 */
18058 function toRelativeImport(relativePath) {
18059 return isLocalRelativePath(relativePath) ? `./${relativePath}` : relativePath;
18060 }
18061
18062 const LogicalProjectPath = {
18063 /**
18064 * Get the relative path between two `LogicalProjectPath`s.
18065 *
18066 * This will return a `PathSegment` which would be a valid module specifier to use in `from` when
18067 * importing from `to`.
18068 */
18069 relativePathBetween: function (from, to) {
18070 const relativePath = relative(dirname(resolve(from)), resolve(to));
18071 return toRelativeImport(relativePath);
18072 },
18073 };
18074 /**
18075 * A utility class which can translate absolute paths to source files into logical paths in
18076 * TypeScript's logical file system, based on the root directories of the project.
18077 */
18078 class LogicalFileSystem {
18079 constructor(rootDirs, compilerHost) {
18080 this.compilerHost = compilerHost;
18081 /**
18082 * A cache of file paths to project paths, because computation of these paths is slightly
18083 * expensive.
18084 */
18085 this.cache = new Map();
18086 // Make a copy and sort it by length in reverse order (longest first). This speeds up lookups,
18087 // since there's no need to keep going through the array once a match is found.
18088 this.rootDirs = rootDirs.concat([]).sort((a, b) => b.length - a.length);
18089 this.canonicalRootDirs =
18090 this.rootDirs.map(dir => this.compilerHost.getCanonicalFileName(dir));
18091 }
18092 /**
18093 * Get the logical path in the project of a `ts.SourceFile`.
18094 *
18095 * This method is provided as a convenient alternative to calling
18096 * `logicalPathOfFile(absoluteFromSourceFile(sf))`.
18097 */
18098 logicalPathOfSf(sf) {
18099 return this.logicalPathOfFile(absoluteFrom(sf.fileName));
18100 }
18101 /**
18102 * Get the logical path in the project of a source file.
18103 *
18104 * @returns A `LogicalProjectPath` to the source file, or `null` if the source file is not in any
18105 * of the TS project's root directories.
18106 */
18107 logicalPathOfFile(physicalFile) {
18108 const canonicalFilePath = this.compilerHost.getCanonicalFileName(physicalFile);
18109 if (!this.cache.has(canonicalFilePath)) {
18110 let logicalFile = null;
18111 for (let i = 0; i < this.rootDirs.length; i++) {
18112 const rootDir = this.rootDirs[i];
18113 const canonicalRootDir = this.canonicalRootDirs[i];
18114 if (isWithinBasePath(canonicalRootDir, canonicalFilePath)) {
18115 // Note that we match against canonical paths but then create the logical path from
18116 // original paths.
18117 logicalFile = this.createLogicalProjectPath(physicalFile, rootDir);
18118 // The logical project does not include any special "node_modules" nested directories.
18119 if (logicalFile.indexOf('/node_modules/') !== -1) {
18120 logicalFile = null;
18121 }
18122 else {
18123 break;
18124 }
18125 }
18126 }
18127 this.cache.set(canonicalFilePath, logicalFile);
18128 }
18129 return this.cache.get(canonicalFilePath);
18130 }
18131 createLogicalProjectPath(file, rootDir) {
18132 const logicalPath = stripExtension(file.substr(rootDir.length));
18133 return (logicalPath.startsWith('/') ? logicalPath : '/' + logicalPath);
18134 }
18135 }
18136 /**
18137 * Is the `path` a descendant of the `base`?
18138 * E.g. `foo/bar/zee` is within `foo/bar` but not within `foo/car`.
18139 */
18140 function isWithinBasePath(base, path) {
18141 return isLocalRelativePath(relative(base, path));
18142 }
18143
18144 // simple mutable assign
18145 function assign () {
18146 const args = [].slice.call(arguments).filter(i => i);
18147 const dest = args.shift();
18148 args.forEach(src => {
18149 Object.keys(src).forEach(key => {
18150 dest[key] = src[key];
18151 });
18152 });
18153
18154 return dest
18155 }
18156
18157 var assign_1 = assign;
18158
18159 function createCommonjsModule(fn, module) {
18160 return module = { exports: {} }, fn(module, module.exports), module.exports;
18161 }
18162
18163 var fromCallback = function (fn) {
18164 return Object.defineProperty(function () {
18165 if (typeof arguments[arguments.length - 1] === 'function') fn.apply(this, arguments);
18166 else {
18167 return new Promise((resolve, reject) => {
18168 arguments[arguments.length] = (err, res) => {
18169 if (err) return reject(err)
18170 resolve(res);
18171 };
18172 arguments.length++;
18173 fn.apply(this, arguments);
18174 })
18175 }
18176 }, 'name', { value: fn.name })
18177 };
18178
18179 var fromPromise = function (fn) {
18180 return Object.defineProperty(function () {
18181 const cb = arguments[arguments.length - 1];
18182 if (typeof cb !== 'function') return fn.apply(this, arguments)
18183 else fn.apply(this, arguments).then(r => cb(null, r), cb);
18184 }, 'name', { value: fn.name })
18185 };
18186
18187 var universalify = {
18188 fromCallback: fromCallback,
18189 fromPromise: fromPromise
18190 };
18191
18192 var origCwd = process.cwd;
18193 var cwd = null;
18194
18195 var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform;
18196
18197 process.cwd = function() {
18198 if (!cwd)
18199 cwd = origCwd.call(process);
18200 return cwd
18201 };
18202 try {
18203 process.cwd();
18204 } catch (er) {}
18205
18206 var chdir = process.chdir;
18207 process.chdir = function(d) {
18208 cwd = null;
18209 chdir.call(process, d);
18210 };
18211
18212 var polyfills = patch;
18213
18214 function patch (fs) {
18215 // (re-)implement some things that are known busted or missing.
18216
18217 // lchmod, broken prior to 0.6.2
18218 // back-port the fix here.
18219 if (constants.hasOwnProperty('O_SYMLINK') &&
18220 process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
18221 patchLchmod(fs);
18222 }
18223
18224 // lutimes implementation, or no-op
18225 if (!fs.lutimes) {
18226 patchLutimes(fs);
18227 }
18228
18229 // https://github.com/isaacs/node-graceful-fs/issues/4
18230 // Chown should not fail on einval or eperm if non-root.
18231 // It should not fail on enosys ever, as this just indicates
18232 // that a fs doesn't support the intended operation.
18233
18234 fs.chown = chownFix(fs.chown);
18235 fs.fchown = chownFix(fs.fchown);
18236 fs.lchown = chownFix(fs.lchown);
18237
18238 fs.chmod = chmodFix(fs.chmod);
18239 fs.fchmod = chmodFix(fs.fchmod);
18240 fs.lchmod = chmodFix(fs.lchmod);
18241
18242 fs.chownSync = chownFixSync(fs.chownSync);
18243 fs.fchownSync = chownFixSync(fs.fchownSync);
18244 fs.lchownSync = chownFixSync(fs.lchownSync);
18245
18246 fs.chmodSync = chmodFixSync(fs.chmodSync);
18247 fs.fchmodSync = chmodFixSync(fs.fchmodSync);
18248 fs.lchmodSync = chmodFixSync(fs.lchmodSync);
18249
18250 fs.stat = statFix(fs.stat);
18251 fs.fstat = statFix(fs.fstat);
18252 fs.lstat = statFix(fs.lstat);
18253
18254 fs.statSync = statFixSync(fs.statSync);
18255 fs.fstatSync = statFixSync(fs.fstatSync);
18256 fs.lstatSync = statFixSync(fs.lstatSync);
18257
18258 // if lchmod/lchown do not exist, then make them no-ops
18259 if (!fs.lchmod) {
18260 fs.lchmod = function (path, mode, cb) {
18261 if (cb) process.nextTick(cb);
18262 };
18263 fs.lchmodSync = function () {};
18264 }
18265 if (!fs.lchown) {
18266 fs.lchown = function (path, uid, gid, cb) {
18267 if (cb) process.nextTick(cb);
18268 };
18269 fs.lchownSync = function () {};
18270 }
18271
18272 // on Windows, A/V software can lock the directory, causing this
18273 // to fail with an EACCES or EPERM if the directory contains newly
18274 // created files. Try again on failure, for up to 60 seconds.
18275
18276 // Set the timeout this long because some Windows Anti-Virus, such as Parity
18277 // bit9, may lock files for up to a minute, causing npm package install
18278 // failures. Also, take care to yield the scheduler. Windows scheduling gives
18279 // CPU to a busy looping process, which can cause the program causing the lock
18280 // contention to be starved of CPU by node, so the contention doesn't resolve.
18281 if (platform === "win32") {
18282 fs.rename = (function (fs$rename) { return function (from, to, cb) {
18283 var start = Date.now();
18284 var backoff = 0;
18285 fs$rename(from, to, function CB (er) {
18286 if (er
18287 && (er.code === "EACCES" || er.code === "EPERM")
18288 && Date.now() - start < 60000) {
18289 setTimeout(function() {
18290 fs.stat(to, function (stater, st) {
18291 if (stater && stater.code === "ENOENT")
18292 fs$rename(from, to, CB);
18293 else
18294 cb(er);
18295 });
18296 }, backoff);
18297 if (backoff < 100)
18298 backoff += 10;
18299 return;
18300 }
18301 if (cb) cb(er);
18302 });
18303 }})(fs.rename);
18304 }
18305
18306 // if read() returns EAGAIN, then just try it again.
18307 fs.read = (function (fs$read) {
18308 function read (fd, buffer, offset, length, position, callback_) {
18309 var callback;
18310 if (callback_ && typeof callback_ === 'function') {
18311 var eagCounter = 0;
18312 callback = function (er, _, __) {
18313 if (er && er.code === 'EAGAIN' && eagCounter < 10) {
18314 eagCounter ++;
18315 return fs$read.call(fs, fd, buffer, offset, length, position, callback)
18316 }
18317 callback_.apply(this, arguments);
18318 };
18319 }
18320 return fs$read.call(fs, fd, buffer, offset, length, position, callback)
18321 }
18322
18323 // This ensures `util.promisify` works as it does for native `fs.read`.
18324 read.__proto__ = fs$read;
18325 return read
18326 })(fs.read);
18327
18328 fs.readSync = (function (fs$readSync) { return function (fd, buffer, offset, length, position) {
18329 var eagCounter = 0;
18330 while (true) {
18331 try {
18332 return fs$readSync.call(fs, fd, buffer, offset, length, position)
18333 } catch (er) {
18334 if (er.code === 'EAGAIN' && eagCounter < 10) {
18335 eagCounter ++;
18336 continue
18337 }
18338 throw er
18339 }
18340 }
18341 }})(fs.readSync);
18342
18343 function patchLchmod (fs) {
18344 fs.lchmod = function (path, mode, callback) {
18345 fs.open( path
18346 , constants.O_WRONLY | constants.O_SYMLINK
18347 , mode
18348 , function (err, fd) {
18349 if (err) {
18350 if (callback) callback(err);
18351 return
18352 }
18353 // prefer to return the chmod error, if one occurs,
18354 // but still try to close, and report closing errors if they occur.
18355 fs.fchmod(fd, mode, function (err) {
18356 fs.close(fd, function(err2) {
18357 if (callback) callback(err || err2);
18358 });
18359 });
18360 });
18361 };
18362
18363 fs.lchmodSync = function (path, mode) {
18364 var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode);
18365
18366 // prefer to return the chmod error, if one occurs,
18367 // but still try to close, and report closing errors if they occur.
18368 var threw = true;
18369 var ret;
18370 try {
18371 ret = fs.fchmodSync(fd, mode);
18372 threw = false;
18373 } finally {
18374 if (threw) {
18375 try {
18376 fs.closeSync(fd);
18377 } catch (er) {}
18378 } else {
18379 fs.closeSync(fd);
18380 }
18381 }
18382 return ret
18383 };
18384 }
18385
18386 function patchLutimes (fs) {
18387 if (constants.hasOwnProperty("O_SYMLINK")) {
18388 fs.lutimes = function (path, at, mt, cb) {
18389 fs.open(path, constants.O_SYMLINK, function (er, fd) {
18390 if (er) {
18391 if (cb) cb(er);
18392 return
18393 }
18394 fs.futimes(fd, at, mt, function (er) {
18395 fs.close(fd, function (er2) {
18396 if (cb) cb(er || er2);
18397 });
18398 });
18399 });
18400 };
18401
18402 fs.lutimesSync = function (path, at, mt) {
18403 var fd = fs.openSync(path, constants.O_SYMLINK);
18404 var ret;
18405 var threw = true;
18406 try {
18407 ret = fs.futimesSync(fd, at, mt);
18408 threw = false;
18409 } finally {
18410 if (threw) {
18411 try {
18412 fs.closeSync(fd);
18413 } catch (er) {}
18414 } else {
18415 fs.closeSync(fd);
18416 }
18417 }
18418 return ret
18419 };
18420
18421 } else {
18422 fs.lutimes = function (_a, _b, _c, cb) { if (cb) process.nextTick(cb); };
18423 fs.lutimesSync = function () {};
18424 }
18425 }
18426
18427 function chmodFix (orig) {
18428 if (!orig) return orig
18429 return function (target, mode, cb) {
18430 return orig.call(fs, target, mode, function (er) {
18431 if (chownErOk(er)) er = null;
18432 if (cb) cb.apply(this, arguments);
18433 })
18434 }
18435 }
18436
18437 function chmodFixSync (orig) {
18438 if (!orig) return orig
18439 return function (target, mode) {
18440 try {
18441 return orig.call(fs, target, mode)
18442 } catch (er) {
18443 if (!chownErOk(er)) throw er
18444 }
18445 }
18446 }
18447
18448
18449 function chownFix (orig) {
18450 if (!orig) return orig
18451 return function (target, uid, gid, cb) {
18452 return orig.call(fs, target, uid, gid, function (er) {
18453 if (chownErOk(er)) er = null;
18454 if (cb) cb.apply(this, arguments);
18455 })
18456 }
18457 }
18458
18459 function chownFixSync (orig) {
18460 if (!orig) return orig
18461 return function (target, uid, gid) {
18462 try {
18463 return orig.call(fs, target, uid, gid)
18464 } catch (er) {
18465 if (!chownErOk(er)) throw er
18466 }
18467 }
18468 }
18469
18470 function statFix (orig) {
18471 if (!orig) return orig
18472 // Older versions of Node erroneously returned signed integers for
18473 // uid + gid.
18474 return function (target, options, cb) {
18475 if (typeof options === 'function') {
18476 cb = options;
18477 options = null;
18478 }
18479 function callback (er, stats) {
18480 if (stats) {
18481 if (stats.uid < 0) stats.uid += 0x100000000;
18482 if (stats.gid < 0) stats.gid += 0x100000000;
18483 }
18484 if (cb) cb.apply(this, arguments);
18485 }
18486 return options ? orig.call(fs, target, options, callback)
18487 : orig.call(fs, target, callback)
18488 }
18489 }
18490
18491 function statFixSync (orig) {
18492 if (!orig) return orig
18493 // Older versions of Node erroneously returned signed integers for
18494 // uid + gid.
18495 return function (target, options) {
18496 var stats = options ? orig.call(fs, target, options)
18497 : orig.call(fs, target);
18498 if (stats.uid < 0) stats.uid += 0x100000000;
18499 if (stats.gid < 0) stats.gid += 0x100000000;
18500 return stats;
18501 }
18502 }
18503
18504 // ENOSYS means that the fs doesn't support the op. Just ignore
18505 // that, because it doesn't matter.
18506 //
18507 // if there's no getuid, or if getuid() is something other
18508 // than 0, and the error is EINVAL or EPERM, then just ignore
18509 // it.
18510 //
18511 // This specific case is a silent failure in cp, install, tar,
18512 // and most other unix tools that manage permissions.
18513 //
18514 // When running as root, or if other types of errors are
18515 // encountered, then it's strict.
18516 function chownErOk (er) {
18517 if (!er)
18518 return true
18519
18520 if (er.code === "ENOSYS")
18521 return true
18522
18523 var nonroot = !process.getuid || process.getuid() !== 0;
18524 if (nonroot) {
18525 if (er.code === "EINVAL" || er.code === "EPERM")
18526 return true
18527 }
18528
18529 return false
18530 }
18531 }
18532
18533 var Stream = stream.Stream;
18534
18535 var legacyStreams = legacy;
18536
18537 function legacy (fs) {
18538 return {
18539 ReadStream: ReadStream,
18540 WriteStream: WriteStream
18541 }
18542
18543 function ReadStream (path, options) {
18544 if (!(this instanceof ReadStream)) return new ReadStream(path, options);
18545
18546 Stream.call(this);
18547
18548 var self = this;
18549
18550 this.path = path;
18551 this.fd = null;
18552 this.readable = true;
18553 this.paused = false;
18554
18555 this.flags = 'r';
18556 this.mode = 438; /*=0666*/
18557 this.bufferSize = 64 * 1024;
18558
18559 options = options || {};
18560
18561 // Mixin options into this
18562 var keys = Object.keys(options);
18563 for (var index = 0, length = keys.length; index < length; index++) {
18564 var key = keys[index];
18565 this[key] = options[key];
18566 }
18567
18568 if (this.encoding) this.setEncoding(this.encoding);
18569
18570 if (this.start !== undefined) {
18571 if ('number' !== typeof this.start) {
18572 throw TypeError('start must be a Number');
18573 }
18574 if (this.end === undefined) {
18575 this.end = Infinity;
18576 } else if ('number' !== typeof this.end) {
18577 throw TypeError('end must be a Number');
18578 }
18579
18580 if (this.start > this.end) {
18581 throw new Error('start must be <= end');
18582 }
18583
18584 this.pos = this.start;
18585 }
18586
18587 if (this.fd !== null) {
18588 process.nextTick(function() {
18589 self._read();
18590 });
18591 return;
18592 }
18593
18594 fs.open(this.path, this.flags, this.mode, function (err, fd) {
18595 if (err) {
18596 self.emit('error', err);
18597 self.readable = false;
18598 return;
18599 }
18600
18601 self.fd = fd;
18602 self.emit('open', fd);
18603 self._read();
18604 });
18605 }
18606
18607 function WriteStream (path, options) {
18608 if (!(this instanceof WriteStream)) return new WriteStream(path, options);
18609
18610 Stream.call(this);
18611
18612 this.path = path;
18613 this.fd = null;
18614 this.writable = true;
18615
18616 this.flags = 'w';
18617 this.encoding = 'binary';
18618 this.mode = 438; /*=0666*/
18619 this.bytesWritten = 0;
18620
18621 options = options || {};
18622
18623 // Mixin options into this
18624 var keys = Object.keys(options);
18625 for (var index = 0, length = keys.length; index < length; index++) {
18626 var key = keys[index];
18627 this[key] = options[key];
18628 }
18629
18630 if (this.start !== undefined) {
18631 if ('number' !== typeof this.start) {
18632 throw TypeError('start must be a Number');
18633 }
18634 if (this.start < 0) {
18635 throw new Error('start must be >= zero');
18636 }
18637
18638 this.pos = this.start;
18639 }
18640
18641 this.busy = false;
18642 this._queue = [];
18643
18644 if (this.fd === null) {
18645 this._open = fs.open;
18646 this._queue.push([this._open, this.path, this.flags, this.mode, undefined]);
18647 this.flush();
18648 }
18649 }
18650 }
18651
18652 var clone_1 = clone;
18653
18654 function clone (obj) {
18655 if (obj === null || typeof obj !== 'object')
18656 return obj
18657
18658 if (obj instanceof Object)
18659 var copy = { __proto__: obj.__proto__ };
18660 else
18661 var copy = Object.create(null);
18662
18663 Object.getOwnPropertyNames(obj).forEach(function (key) {
18664 Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key));
18665 });
18666
18667 return copy
18668 }
18669
18670 var gracefulFs = createCommonjsModule(function (module) {
18671 /* istanbul ignore next - node 0.x polyfill */
18672 var gracefulQueue;
18673 var previousSymbol;
18674
18675 /* istanbul ignore else - node 0.x polyfill */
18676 if (typeof Symbol === 'function' && typeof Symbol.for === 'function') {
18677 gracefulQueue = Symbol.for('graceful-fs.queue');
18678 // This is used in testing by future versions
18679 previousSymbol = Symbol.for('graceful-fs.previous');
18680 } else {
18681 gracefulQueue = '___graceful-fs.queue';
18682 previousSymbol = '___graceful-fs.previous';
18683 }
18684
18685 function noop () {}
18686
18687 var debug = noop;
18688 if (util.debuglog)
18689 debug = util.debuglog('gfs4');
18690 else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ''))
18691 debug = function() {
18692 var m = util.format.apply(util, arguments);
18693 m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ');
18694 console.error(m);
18695 };
18696
18697 // Once time initialization
18698 if (!global[gracefulQueue]) {
18699 // This queue can be shared by multiple loaded instances
18700 var queue = [];
18701 Object.defineProperty(global, gracefulQueue, {
18702 get: function() {
18703 return queue
18704 }
18705 });
18706
18707 // Patch fs.close/closeSync to shared queue version, because we need
18708 // to retry() whenever a close happens *anywhere* in the program.
18709 // This is essential when multiple graceful-fs instances are
18710 // in play at the same time.
18711 fs$2__default.close = (function (fs$close) {
18712 function close (fd, cb) {
18713 return fs$close.call(fs$2__default, fd, function (err) {
18714 // This function uses the graceful-fs shared queue
18715 if (!err) {
18716 retry();
18717 }
18718
18719 if (typeof cb === 'function')
18720 cb.apply(this, arguments);
18721 })
18722 }
18723
18724 Object.defineProperty(close, previousSymbol, {
18725 value: fs$close
18726 });
18727 return close
18728 })(fs$2__default.close);
18729
18730 fs$2__default.closeSync = (function (fs$closeSync) {
18731 function closeSync (fd) {
18732 // This function uses the graceful-fs shared queue
18733 fs$closeSync.apply(fs$2__default, arguments);
18734 retry();
18735 }
18736
18737 Object.defineProperty(closeSync, previousSymbol, {
18738 value: fs$closeSync
18739 });
18740 return closeSync
18741 })(fs$2__default.closeSync);
18742
18743 if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) {
18744 process.on('exit', function() {
18745 debug(global[gracefulQueue]);
18746 assert.equal(global[gracefulQueue].length, 0);
18747 });
18748 }
18749 }
18750
18751 module.exports = patch(clone_1(fs$2__default));
18752 if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs$2__default.__patched) {
18753 module.exports = patch(fs$2__default);
18754 fs$2__default.__patched = true;
18755 }
18756
18757 function patch (fs) {
18758 // Everything that references the open() function needs to be in here
18759 polyfills(fs);
18760 fs.gracefulify = patch;
18761
18762 fs.createReadStream = createReadStream;
18763 fs.createWriteStream = createWriteStream;
18764 var fs$readFile = fs.readFile;
18765 fs.readFile = readFile;
18766 function readFile (path, options, cb) {
18767 if (typeof options === 'function')
18768 cb = options, options = null;
18769
18770 return go$readFile(path, options, cb)
18771
18772 function go$readFile (path, options, cb) {
18773 return fs$readFile(path, options, function (err) {
18774 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
18775 enqueue([go$readFile, [path, options, cb]]);
18776 else {
18777 if (typeof cb === 'function')
18778 cb.apply(this, arguments);
18779 retry();
18780 }
18781 })
18782 }
18783 }
18784
18785 var fs$writeFile = fs.writeFile;
18786 fs.writeFile = writeFile;
18787 function writeFile (path, data, options, cb) {
18788 if (typeof options === 'function')
18789 cb = options, options = null;
18790
18791 return go$writeFile(path, data, options, cb)
18792
18793 function go$writeFile (path, data, options, cb) {
18794 return fs$writeFile(path, data, options, function (err) {
18795 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
18796 enqueue([go$writeFile, [path, data, options, cb]]);
18797 else {
18798 if (typeof cb === 'function')
18799 cb.apply(this, arguments);
18800 retry();
18801 }
18802 })
18803 }
18804 }
18805
18806 var fs$appendFile = fs.appendFile;
18807 if (fs$appendFile)
18808 fs.appendFile = appendFile;
18809 function appendFile (path, data, options, cb) {
18810 if (typeof options === 'function')
18811 cb = options, options = null;
18812
18813 return go$appendFile(path, data, options, cb)
18814
18815 function go$appendFile (path, data, options, cb) {
18816 return fs$appendFile(path, data, options, function (err) {
18817 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
18818 enqueue([go$appendFile, [path, data, options, cb]]);
18819 else {
18820 if (typeof cb === 'function')
18821 cb.apply(this, arguments);
18822 retry();
18823 }
18824 })
18825 }
18826 }
18827
18828 var fs$readdir = fs.readdir;
18829 fs.readdir = readdir;
18830 function readdir (path, options, cb) {
18831 var args = [path];
18832 if (typeof options !== 'function') {
18833 args.push(options);
18834 } else {
18835 cb = options;
18836 }
18837 args.push(go$readdir$cb);
18838
18839 return go$readdir(args)
18840
18841 function go$readdir$cb (err, files) {
18842 if (files && files.sort)
18843 files.sort();
18844
18845 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
18846 enqueue([go$readdir, [args]]);
18847
18848 else {
18849 if (typeof cb === 'function')
18850 cb.apply(this, arguments);
18851 retry();
18852 }
18853 }
18854 }
18855
18856 function go$readdir (args) {
18857 return fs$readdir.apply(fs, args)
18858 }
18859
18860 if (process.version.substr(0, 4) === 'v0.8') {
18861 var legStreams = legacyStreams(fs);
18862 ReadStream = legStreams.ReadStream;
18863 WriteStream = legStreams.WriteStream;
18864 }
18865
18866 var fs$ReadStream = fs.ReadStream;
18867 if (fs$ReadStream) {
18868 ReadStream.prototype = Object.create(fs$ReadStream.prototype);
18869 ReadStream.prototype.open = ReadStream$open;
18870 }
18871
18872 var fs$WriteStream = fs.WriteStream;
18873 if (fs$WriteStream) {
18874 WriteStream.prototype = Object.create(fs$WriteStream.prototype);
18875 WriteStream.prototype.open = WriteStream$open;
18876 }
18877
18878 Object.defineProperty(fs, 'ReadStream', {
18879 get: function () {
18880 return ReadStream
18881 },
18882 set: function (val) {
18883 ReadStream = val;
18884 },
18885 enumerable: true,
18886 configurable: true
18887 });
18888 Object.defineProperty(fs, 'WriteStream', {
18889 get: function () {
18890 return WriteStream
18891 },
18892 set: function (val) {
18893 WriteStream = val;
18894 },
18895 enumerable: true,
18896 configurable: true
18897 });
18898
18899 // legacy names
18900 Object.defineProperty(fs, 'FileReadStream', {
18901 get: function () {
18902 return ReadStream
18903 },
18904 set: function (val) {
18905 ReadStream = val;
18906 },
18907 enumerable: true,
18908 configurable: true
18909 });
18910 Object.defineProperty(fs, 'FileWriteStream', {
18911 get: function () {
18912 return WriteStream
18913 },
18914 set: function (val) {
18915 WriteStream = val;
18916 },
18917 enumerable: true,
18918 configurable: true
18919 });
18920
18921 function ReadStream (path, options) {
18922 if (this instanceof ReadStream)
18923 return fs$ReadStream.apply(this, arguments), this
18924 else
18925 return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
18926 }
18927
18928 function ReadStream$open () {
18929 var that = this;
18930 open(that.path, that.flags, that.mode, function (err, fd) {
18931 if (err) {
18932 if (that.autoClose)
18933 that.destroy();
18934
18935 that.emit('error', err);
18936 } else {
18937 that.fd = fd;
18938 that.emit('open', fd);
18939 that.read();
18940 }
18941 });
18942 }
18943
18944 function WriteStream (path, options) {
18945 if (this instanceof WriteStream)
18946 return fs$WriteStream.apply(this, arguments), this
18947 else
18948 return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
18949 }
18950
18951 function WriteStream$open () {
18952 var that = this;
18953 open(that.path, that.flags, that.mode, function (err, fd) {
18954 if (err) {
18955 that.destroy();
18956 that.emit('error', err);
18957 } else {
18958 that.fd = fd;
18959 that.emit('open', fd);
18960 }
18961 });
18962 }
18963
18964 function createReadStream (path, options) {
18965 return new fs.ReadStream(path, options)
18966 }
18967
18968 function createWriteStream (path, options) {
18969 return new fs.WriteStream(path, options)
18970 }
18971
18972 var fs$open = fs.open;
18973 fs.open = open;
18974 function open (path, flags, mode, cb) {
18975 if (typeof mode === 'function')
18976 cb = mode, mode = null;
18977
18978 return go$open(path, flags, mode, cb)
18979
18980 function go$open (path, flags, mode, cb) {
18981 return fs$open(path, flags, mode, function (err, fd) {
18982 if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
18983 enqueue([go$open, [path, flags, mode, cb]]);
18984 else {
18985 if (typeof cb === 'function')
18986 cb.apply(this, arguments);
18987 retry();
18988 }
18989 })
18990 }
18991 }
18992
18993 return fs
18994 }
18995
18996 function enqueue (elem) {
18997 debug('ENQUEUE', elem[0].name, elem[1]);
18998 global[gracefulQueue].push(elem);
18999 }
19000
19001 function retry () {
19002 var elem = global[gracefulQueue].shift();
19003 if (elem) {
19004 debug('RETRY', elem[0].name, elem[1]);
19005 elem[0].apply(null, elem[1]);
19006 }
19007 }
19008 });
19009
19010 var fs_1 = createCommonjsModule(function (module, exports) {
19011 // This is adapted from https://github.com/normalize/mz
19012 // Copyright (c) 2014-2016 Jonathan Ong me@jongleberry.com and Contributors
19013 const u = universalify.fromCallback;
19014
19015
19016 const api = [
19017 'access',
19018 'appendFile',
19019 'chmod',
19020 'chown',
19021 'close',
19022 'fchmod',
19023 'fchown',
19024 'fdatasync',
19025 'fstat',
19026 'fsync',
19027 'ftruncate',
19028 'futimes',
19029 'lchown',
19030 'link',
19031 'lstat',
19032 'mkdir',
19033 'open',
19034 'readFile',
19035 'readdir',
19036 'readlink',
19037 'realpath',
19038 'rename',
19039 'rmdir',
19040 'stat',
19041 'symlink',
19042 'truncate',
19043 'unlink',
19044 'utimes',
19045 'writeFile'
19046 ];
19047 // Add methods that are only in some Node.js versions
19048 // fs.copyFile was added in Node.js v8.5.0
19049 typeof gracefulFs.copyFile === 'function' && api.push('copyFile');
19050 // fs.mkdtemp() was added in Node.js v5.10.0
19051 typeof gracefulFs.mkdtemp === 'function' && api.push('mkdtemp');
19052
19053 // Export all keys:
19054 Object.keys(gracefulFs).forEach(key => {
19055 exports[key] = gracefulFs[key];
19056 });
19057
19058 // Universalify async methods:
19059 api.forEach(method => {
19060 exports[method] = u(gracefulFs[method]);
19061 });
19062
19063 // We differ from mz/fs in that we still ship the old, broken, fs.exists()
19064 // since we are a drop-in replacement for the native module
19065 exports.exists = function (filename, callback) {
19066 if (typeof callback === 'function') {
19067 return gracefulFs.exists(filename, callback)
19068 }
19069 return new Promise(resolve => {
19070 return gracefulFs.exists(filename, resolve)
19071 })
19072 };
19073
19074 // fs.read() & fs.write need special treatment due to multiple callback args
19075
19076 exports.read = function (fd, buffer, offset, length, position, callback) {
19077 if (typeof callback === 'function') {
19078 return gracefulFs.read(fd, buffer, offset, length, position, callback)
19079 }
19080 return new Promise((resolve, reject) => {
19081 gracefulFs.read(fd, buffer, offset, length, position, (err, bytesRead, buffer) => {
19082 if (err) return reject(err)
19083 resolve({ bytesRead, buffer });
19084 });
19085 })
19086 };
19087
19088 // Function signature can be
19089 // fs.write(fd, buffer[, offset[, length[, position]]], callback)
19090 // OR
19091 // fs.write(fd, string[, position[, encoding]], callback)
19092 // so we need to handle both cases
19093 exports.write = function (fd, buffer, a, b, c, callback) {
19094 if (typeof arguments[arguments.length - 1] === 'function') {
19095 return gracefulFs.write(fd, buffer, a, b, c, callback)
19096 }
19097
19098 // Check for old, depricated fs.write(fd, string[, position[, encoding]], callback)
19099 if (typeof buffer === 'string') {
19100 return new Promise((resolve, reject) => {
19101 gracefulFs.write(fd, buffer, a, b, (err, bytesWritten, buffer) => {
19102 if (err) return reject(err)
19103 resolve({ bytesWritten, buffer });
19104 });
19105 })
19106 }
19107
19108 return new Promise((resolve, reject) => {
19109 gracefulFs.write(fd, buffer, a, b, c, (err, bytesWritten, buffer) => {
19110 if (err) return reject(err)
19111 resolve({ bytesWritten, buffer });
19112 });
19113 })
19114 };
19115 });
19116 var fs_2 = fs_1.exists;
19117 var fs_3 = fs_1.read;
19118 var fs_4 = fs_1.write;
19119
19120 // HFS, ext{2,3}, FAT do not, Node.js v0.10 does not
19121 function hasMillisResSync () {
19122 let tmpfile = path__default.join('millis-test-sync' + Date.now().toString() + Math.random().toString().slice(2));
19123 tmpfile = path__default.join(os__default.tmpdir(), tmpfile);
19124
19125 // 550 millis past UNIX epoch
19126 const d = new Date(1435410243862);
19127 gracefulFs.writeFileSync(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141');
19128 const fd = gracefulFs.openSync(tmpfile, 'r+');
19129 gracefulFs.futimesSync(fd, d, d);
19130 gracefulFs.closeSync(fd);
19131 return gracefulFs.statSync(tmpfile).mtime > 1435410243000
19132 }
19133
19134 function hasMillisRes (callback) {
19135 let tmpfile = path__default.join('millis-test' + Date.now().toString() + Math.random().toString().slice(2));
19136 tmpfile = path__default.join(os__default.tmpdir(), tmpfile);
19137
19138 // 550 millis past UNIX epoch
19139 const d = new Date(1435410243862);
19140 gracefulFs.writeFile(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141', err => {
19141 if (err) return callback(err)
19142 gracefulFs.open(tmpfile, 'r+', (err, fd) => {
19143 if (err) return callback(err)
19144 gracefulFs.futimes(fd, d, d, err => {
19145 if (err) return callback(err)
19146 gracefulFs.close(fd, err => {
19147 if (err) return callback(err)
19148 gracefulFs.stat(tmpfile, (err, stats) => {
19149 if (err) return callback(err)
19150 callback(null, stats.mtime > 1435410243000);
19151 });
19152 });
19153 });
19154 });
19155 });
19156 }
19157
19158 function timeRemoveMillis (timestamp) {
19159 if (typeof timestamp === 'number') {
19160 return Math.floor(timestamp / 1000) * 1000
19161 } else if (timestamp instanceof Date) {
19162 return new Date(Math.floor(timestamp.getTime() / 1000) * 1000)
19163 } else {
19164 throw new Error('fs-extra: timeRemoveMillis() unknown parameter type')
19165 }
19166 }
19167
19168 function utimesMillis (path, atime, mtime, callback) {
19169 // if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback)
19170 gracefulFs.open(path, 'r+', (err, fd) => {
19171 if (err) return callback(err)
19172 gracefulFs.futimes(fd, atime, mtime, futimesErr => {
19173 gracefulFs.close(fd, closeErr => {
19174 if (callback) callback(futimesErr || closeErr);
19175 });
19176 });
19177 });
19178 }
19179
19180 var utimes = {
19181 hasMillisRes,
19182 hasMillisResSync,
19183 timeRemoveMillis,
19184 utimesMillis
19185 };
19186
19187 // imported from ncp (this is temporary, will rewrite)
19188
19189
19190
19191
19192
19193 function ncp (source, dest, options, callback) {
19194 if (!callback) {
19195 callback = options;
19196 options = {};
19197 }
19198
19199 var basePath = process.cwd();
19200 var currentPath = path__default.resolve(basePath, source);
19201 var targetPath = path__default.resolve(basePath, dest);
19202
19203 var filter = options.filter;
19204 var transform = options.transform;
19205 var overwrite = options.overwrite;
19206 // If overwrite is undefined, use clobber, otherwise default to true:
19207 if (overwrite === undefined) overwrite = options.clobber;
19208 if (overwrite === undefined) overwrite = true;
19209 var errorOnExist = options.errorOnExist;
19210 var dereference = options.dereference;
19211 var preserveTimestamps = options.preserveTimestamps === true;
19212
19213 var started = 0;
19214 var finished = 0;
19215 var running = 0;
19216
19217 var errored = false;
19218
19219 startCopy(currentPath);
19220
19221 function startCopy (source) {
19222 started++;
19223 if (filter) {
19224 if (filter instanceof RegExp) {
19225 console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function');
19226 if (!filter.test(source)) {
19227 return doneOne(true)
19228 }
19229 } else if (typeof filter === 'function') {
19230 if (!filter(source, dest)) {
19231 return doneOne(true)
19232 }
19233 }
19234 }
19235 return getStats(source)
19236 }
19237
19238 function getStats (source) {
19239 var stat = dereference ? gracefulFs.stat : gracefulFs.lstat;
19240 running++;
19241 stat(source, function (err, stats) {
19242 if (err) return onError(err)
19243
19244 // We need to get the mode from the stats object and preserve it.
19245 var item = {
19246 name: source,
19247 mode: stats.mode,
19248 mtime: stats.mtime, // modified time
19249 atime: stats.atime, // access time
19250 stats: stats // temporary
19251 };
19252
19253 if (stats.isDirectory()) {
19254 return onDir(item)
19255 } else if (stats.isFile() || stats.isCharacterDevice() || stats.isBlockDevice()) {
19256 return onFile(item)
19257 } else if (stats.isSymbolicLink()) {
19258 // Symlinks don't really need to know about the mode.
19259 return onLink(source)
19260 }
19261 });
19262 }
19263
19264 function onFile (file) {
19265 var target = file.name.replace(currentPath, targetPath.replace('$', '$$$$')); // escapes '$' with '$$'
19266 isWritable(target, function (writable) {
19267 if (writable) {
19268 copyFile(file, target);
19269 } else {
19270 if (overwrite) {
19271 rmFile(target, function () {
19272 copyFile(file, target);
19273 });
19274 } else if (errorOnExist) {
19275 onError(new Error(target + ' already exists'));
19276 } else {
19277 doneOne();
19278 }
19279 }
19280 });
19281 }
19282
19283 function copyFile (file, target) {
19284 var readStream = gracefulFs.createReadStream(file.name);
19285 var writeStream = gracefulFs.createWriteStream(target, { mode: file.mode });
19286
19287 readStream.on('error', onError);
19288 writeStream.on('error', onError);
19289
19290 if (transform) {
19291 transform(readStream, writeStream, file);
19292 } else {
19293 writeStream.on('open', function () {
19294 readStream.pipe(writeStream);
19295 });
19296 }
19297
19298 writeStream.once('close', function () {
19299 gracefulFs.chmod(target, file.mode, function (err) {
19300 if (err) return onError(err)
19301 if (preserveTimestamps) {
19302 utimes.utimesMillis(target, file.atime, file.mtime, function (err) {
19303 if (err) return onError(err)
19304 return doneOne()
19305 });
19306 } else {
19307 doneOne();
19308 }
19309 });
19310 });
19311 }
19312
19313 function rmFile (file, done) {
19314 gracefulFs.unlink(file, function (err) {
19315 if (err) return onError(err)
19316 return done()
19317 });
19318 }
19319
19320 function onDir (dir) {
19321 var target = dir.name.replace(currentPath, targetPath.replace('$', '$$$$')); // escapes '$' with '$$'
19322 isWritable(target, function (writable) {
19323 if (writable) {
19324 return mkDir(dir, target)
19325 }
19326 copyDir(dir.name);
19327 });
19328 }
19329
19330 function mkDir (dir, target) {
19331 gracefulFs.mkdir(target, dir.mode, function (err) {
19332 if (err) return onError(err)
19333 // despite setting mode in fs.mkdir, doesn't seem to work
19334 // so we set it here.
19335 gracefulFs.chmod(target, dir.mode, function (err) {
19336 if (err) return onError(err)
19337 copyDir(dir.name);
19338 });
19339 });
19340 }
19341
19342 function copyDir (dir) {
19343 gracefulFs.readdir(dir, function (err, items) {
19344 if (err) return onError(err)
19345 items.forEach(function (item) {
19346 startCopy(path__default.join(dir, item));
19347 });
19348 return doneOne()
19349 });
19350 }
19351
19352 function onLink (link) {
19353 var target = link.replace(currentPath, targetPath);
19354 gracefulFs.readlink(link, function (err, resolvedPath) {
19355 if (err) return onError(err)
19356 checkLink(resolvedPath, target);
19357 });
19358 }
19359
19360 function checkLink (resolvedPath, target) {
19361 if (dereference) {
19362 resolvedPath = path__default.resolve(basePath, resolvedPath);
19363 }
19364 isWritable(target, function (writable) {
19365 if (writable) {
19366 return makeLink(resolvedPath, target)
19367 }
19368 gracefulFs.readlink(target, function (err, targetDest) {
19369 if (err) return onError(err)
19370
19371 if (dereference) {
19372 targetDest = path__default.resolve(basePath, targetDest);
19373 }
19374 if (targetDest === resolvedPath) {
19375 return doneOne()
19376 }
19377 return rmFile(target, function () {
19378 makeLink(resolvedPath, target);
19379 })
19380 });
19381 });
19382 }
19383
19384 function makeLink (linkPath, target) {
19385 gracefulFs.symlink(linkPath, target, function (err) {
19386 if (err) return onError(err)
19387 return doneOne()
19388 });
19389 }
19390
19391 function isWritable (path, done) {
19392 gracefulFs.lstat(path, function (err) {
19393 if (err) {
19394 if (err.code === 'ENOENT') return done(true)
19395 return done(false)
19396 }
19397 return done(false)
19398 });
19399 }
19400
19401 function onError (err) {
19402 // ensure callback is defined & called only once:
19403 if (!errored && callback !== undefined) {
19404 errored = true;
19405 return callback(err)
19406 }
19407 }
19408
19409 function doneOne (skipped) {
19410 if (!skipped) running--;
19411 finished++;
19412 if ((started === finished) && (running === 0)) {
19413 if (callback !== undefined) {
19414 return callback(null)
19415 }
19416 }
19417 }
19418 }
19419
19420 var ncp_1 = ncp;
19421
19422 // get drive on windows
19423 function getRootPath (p) {
19424 p = path__default.normalize(path__default.resolve(p)).split(path__default.sep);
19425 if (p.length > 0) return p[0]
19426 return null
19427 }
19428
19429 // http://stackoverflow.com/a/62888/10333 contains more accurate
19430 // TODO: expand to include the rest
19431 const INVALID_PATH_CHARS = /[<>:"|?*]/;
19432
19433 function invalidWin32Path (p) {
19434 const rp = getRootPath(p);
19435 p = p.replace(rp, '');
19436 return INVALID_PATH_CHARS.test(p)
19437 }
19438
19439 var win32 = {
19440 getRootPath,
19441 invalidWin32Path
19442 };
19443
19444 const invalidWin32Path$1 = win32.invalidWin32Path;
19445
19446 const o777 = parseInt('0777', 8);
19447
19448 function mkdirs (p, opts, callback, made) {
19449 if (typeof opts === 'function') {
19450 callback = opts;
19451 opts = {};
19452 } else if (!opts || typeof opts !== 'object') {
19453 opts = { mode: opts };
19454 }
19455
19456 if (process.platform === 'win32' && invalidWin32Path$1(p)) {
19457 const errInval = new Error(p + ' contains invalid WIN32 path characters.');
19458 errInval.code = 'EINVAL';
19459 return callback(errInval)
19460 }
19461
19462 let mode = opts.mode;
19463 const xfs = opts.fs || gracefulFs;
19464
19465 if (mode === undefined) {
19466 mode = o777 & (~process.umask());
19467 }
19468 if (!made) made = null;
19469
19470 callback = callback || function () {};
19471 p = path__default.resolve(p);
19472
19473 xfs.mkdir(p, mode, er => {
19474 if (!er) {
19475 made = made || p;
19476 return callback(null, made)
19477 }
19478 switch (er.code) {
19479 case 'ENOENT':
19480 if (path__default.dirname(p) === p) return callback(er)
19481 mkdirs(path__default.dirname(p), opts, (er, made) => {
19482 if (er) callback(er, made);
19483 else mkdirs(p, opts, callback, made);
19484 });
19485 break
19486
19487 // In the case of any other error, just see if there's a dir
19488 // there already. If so, then hooray! If not, then something
19489 // is borked.
19490 default:
19491 xfs.stat(p, (er2, stat) => {
19492 // if the stat fails, then that's super weird.
19493 // let the original error be the failure reason.
19494 if (er2 || !stat.isDirectory()) callback(er, made);
19495 else callback(null, made);
19496 });
19497 break
19498 }
19499 });
19500 }
19501
19502 var mkdirs_1 = mkdirs;
19503
19504 const invalidWin32Path$2 = win32.invalidWin32Path;
19505
19506 const o777$1 = parseInt('0777', 8);
19507
19508 function mkdirsSync (p, opts, made) {
19509 if (!opts || typeof opts !== 'object') {
19510 opts = { mode: opts };
19511 }
19512
19513 let mode = opts.mode;
19514 const xfs = opts.fs || gracefulFs;
19515
19516 if (process.platform === 'win32' && invalidWin32Path$2(p)) {
19517 const errInval = new Error(p + ' contains invalid WIN32 path characters.');
19518 errInval.code = 'EINVAL';
19519 throw errInval
19520 }
19521
19522 if (mode === undefined) {
19523 mode = o777$1 & (~process.umask());
19524 }
19525 if (!made) made = null;
19526
19527 p = path__default.resolve(p);
19528
19529 try {
19530 xfs.mkdirSync(p, mode);
19531 made = made || p;
19532 } catch (err0) {
19533 switch (err0.code) {
19534 case 'ENOENT':
19535 if (path__default.dirname(p) === p) throw err0
19536 made = mkdirsSync(path__default.dirname(p), opts, made);
19537 mkdirsSync(p, opts, made);
19538 break
19539
19540 // In the case of any other error, just see if there's a dir
19541 // there already. If so, then hooray! If not, then something
19542 // is borked.
19543 default:
19544 let stat;
19545 try {
19546 stat = xfs.statSync(p);
19547 } catch (err1) {
19548 throw err0
19549 }
19550 if (!stat.isDirectory()) throw err0
19551 break
19552 }
19553 }
19554
19555 return made
19556 }
19557
19558 var mkdirsSync_1 = mkdirsSync;
19559
19560 const u = universalify.fromCallback;
19561 const mkdirs$1 = u(mkdirs_1);
19562
19563
19564 var mkdirs_1$1 = {
19565 mkdirs: mkdirs$1,
19566 mkdirsSync: mkdirsSync_1,
19567 // alias
19568 mkdirp: mkdirs$1,
19569 mkdirpSync: mkdirsSync_1,
19570 ensureDir: mkdirs$1,
19571 ensureDirSync: mkdirsSync_1
19572 };
19573
19574 const u$1 = universalify.fromPromise;
19575
19576
19577 function pathExists (path) {
19578 return fs_1.access(path).then(() => true).catch(() => false)
19579 }
19580
19581 var pathExists_1 = {
19582 pathExists: u$1(pathExists),
19583 pathExistsSync: fs_1.existsSync
19584 };
19585
19586 const pathExists$1 = pathExists_1.pathExists;
19587
19588 function copy (src, dest, options, callback) {
19589 if (typeof options === 'function' && !callback) {
19590 callback = options;
19591 options = {};
19592 } else if (typeof options === 'function' || options instanceof RegExp) {
19593 options = {filter: options};
19594 }
19595 callback = callback || function () {};
19596 options = options || {};
19597
19598 // Warn about using preserveTimestamps on 32-bit node:
19599 if (options.preserveTimestamps && process.arch === 'ia32') {
19600 console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
19601 see https://github.com/jprichardson/node-fs-extra/issues/269`);
19602 }
19603
19604 // don't allow src and dest to be the same
19605 const basePath = process.cwd();
19606 const currentPath = path__default.resolve(basePath, src);
19607 const targetPath = path__default.resolve(basePath, dest);
19608 if (currentPath === targetPath) return callback(new Error('Source and destination must not be the same.'))
19609
19610 gracefulFs.lstat(src, (err, stats) => {
19611 if (err) return callback(err)
19612
19613 let dir = null;
19614 if (stats.isDirectory()) {
19615 const parts = dest.split(path__default.sep);
19616 parts.pop();
19617 dir = parts.join(path__default.sep);
19618 } else {
19619 dir = path__default.dirname(dest);
19620 }
19621
19622 pathExists$1(dir, (err, dirExists) => {
19623 if (err) return callback(err)
19624 if (dirExists) return ncp_1(src, dest, options, callback)
19625 mkdirs_1$1.mkdirs(dir, err => {
19626 if (err) return callback(err)
19627 ncp_1(src, dest, options, callback);
19628 });
19629 });
19630 });
19631 }
19632
19633 var copy_1 = copy;
19634
19635 const u$2 = universalify.fromCallback;
19636 var copy$1 = {
19637 copy: u$2(copy_1)
19638 };
19639
19640 /* eslint-disable node/no-deprecated-api */
19641 var buffer = function (size) {
19642 if (typeof Buffer.allocUnsafe === 'function') {
19643 try {
19644 return Buffer.allocUnsafe(size)
19645 } catch (e) {
19646 return new Buffer(size)
19647 }
19648 }
19649 return new Buffer(size)
19650 };
19651
19652 const BUF_LENGTH = 64 * 1024;
19653 const _buff = buffer(BUF_LENGTH);
19654
19655 function copyFileSync (srcFile, destFile, options) {
19656 const overwrite = options.overwrite;
19657 const errorOnExist = options.errorOnExist;
19658 const preserveTimestamps = options.preserveTimestamps;
19659
19660 if (gracefulFs.existsSync(destFile)) {
19661 if (overwrite) {
19662 gracefulFs.unlinkSync(destFile);
19663 } else if (errorOnExist) {
19664 throw new Error(`${destFile} already exists`)
19665 } else return
19666 }
19667
19668 const fdr = gracefulFs.openSync(srcFile, 'r');
19669 const stat = gracefulFs.fstatSync(fdr);
19670 const fdw = gracefulFs.openSync(destFile, 'w', stat.mode);
19671 let bytesRead = 1;
19672 let pos = 0;
19673
19674 while (bytesRead > 0) {
19675 bytesRead = gracefulFs.readSync(fdr, _buff, 0, BUF_LENGTH, pos);
19676 gracefulFs.writeSync(fdw, _buff, 0, bytesRead);
19677 pos += bytesRead;
19678 }
19679
19680 if (preserveTimestamps) {
19681 gracefulFs.futimesSync(fdw, stat.atime, stat.mtime);
19682 }
19683
19684 gracefulFs.closeSync(fdr);
19685 gracefulFs.closeSync(fdw);
19686 }
19687
19688 var copyFileSync_1 = copyFileSync;
19689
19690 function copySync (src, dest, options) {
19691 if (typeof options === 'function' || options instanceof RegExp) {
19692 options = {filter: options};
19693 }
19694
19695 options = options || {};
19696 options.recursive = !!options.recursive;
19697
19698 // default to true for now
19699 options.clobber = 'clobber' in options ? !!options.clobber : true;
19700 // overwrite falls back to clobber
19701 options.overwrite = 'overwrite' in options ? !!options.overwrite : options.clobber;
19702 options.dereference = 'dereference' in options ? !!options.dereference : false;
19703 options.preserveTimestamps = 'preserveTimestamps' in options ? !!options.preserveTimestamps : false;
19704
19705 options.filter = options.filter || function () { return true };
19706
19707 // Warn about using preserveTimestamps on 32-bit node:
19708 if (options.preserveTimestamps && process.arch === 'ia32') {
19709 console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
19710 see https://github.com/jprichardson/node-fs-extra/issues/269`);
19711 }
19712
19713 const stats = (options.recursive && !options.dereference) ? gracefulFs.lstatSync(src) : gracefulFs.statSync(src);
19714 const destFolder = path__default.dirname(dest);
19715 const destFolderExists = gracefulFs.existsSync(destFolder);
19716 let performCopy = false;
19717
19718 if (options.filter instanceof RegExp) {
19719 console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function');
19720 performCopy = options.filter.test(src);
19721 } else if (typeof options.filter === 'function') performCopy = options.filter(src, dest);
19722
19723 if (stats.isFile() && performCopy) {
19724 if (!destFolderExists) mkdirs_1$1.mkdirsSync(destFolder);
19725 copyFileSync_1(src, dest, {
19726 overwrite: options.overwrite,
19727 errorOnExist: options.errorOnExist,
19728 preserveTimestamps: options.preserveTimestamps
19729 });
19730 } else if (stats.isDirectory() && performCopy) {
19731 if (!gracefulFs.existsSync(dest)) mkdirs_1$1.mkdirsSync(dest);
19732 const contents = gracefulFs.readdirSync(src);
19733 contents.forEach(content => {
19734 const opts = options;
19735 opts.recursive = true;
19736 copySync(path__default.join(src, content), path__default.join(dest, content), opts);
19737 });
19738 } else if (options.recursive && stats.isSymbolicLink() && performCopy) {
19739 const srcPath = gracefulFs.readlinkSync(src);
19740 gracefulFs.symlinkSync(srcPath, dest);
19741 }
19742 }
19743
19744 var copySync_1 = copySync;
19745
19746 var copySync$1 = {
19747 copySync: copySync_1
19748 };
19749
19750 const isWindows = (process.platform === 'win32');
19751
19752 function defaults (options) {
19753 const methods = [
19754 'unlink',
19755 'chmod',
19756 'stat',
19757 'lstat',
19758 'rmdir',
19759 'readdir'
19760 ];
19761 methods.forEach(m => {
19762 options[m] = options[m] || gracefulFs[m];
19763 m = m + 'Sync';
19764 options[m] = options[m] || gracefulFs[m];
19765 });
19766
19767 options.maxBusyTries = options.maxBusyTries || 3;
19768 }
19769
19770 function rimraf (p, options, cb) {
19771 let busyTries = 0;
19772
19773 if (typeof options === 'function') {
19774 cb = options;
19775 options = {};
19776 }
19777
19778 assert(p, 'rimraf: missing path');
19779 assert.equal(typeof p, 'string', 'rimraf: path should be a string');
19780 assert.equal(typeof cb, 'function', 'rimraf: callback function required');
19781 assert(options, 'rimraf: invalid options argument provided');
19782 assert.equal(typeof options, 'object', 'rimraf: options should be object');
19783
19784 defaults(options);
19785
19786 rimraf_(p, options, function CB (er) {
19787 if (er) {
19788 if ((er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') &&
19789 busyTries < options.maxBusyTries) {
19790 busyTries++;
19791 let time = busyTries * 100;
19792 // try again, with the same exact callback as this one.
19793 return setTimeout(() => rimraf_(p, options, CB), time)
19794 }
19795
19796 // already gone
19797 if (er.code === 'ENOENT') er = null;
19798 }
19799
19800 cb(er);
19801 });
19802 }
19803
19804 // Two possible strategies.
19805 // 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
19806 // 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
19807 //
19808 // Both result in an extra syscall when you guess wrong. However, there
19809 // are likely far more normal files in the world than directories. This
19810 // is based on the assumption that a the average number of files per
19811 // directory is >= 1.
19812 //
19813 // If anyone ever complains about this, then I guess the strategy could
19814 // be made configurable somehow. But until then, YAGNI.
19815 function rimraf_ (p, options, cb) {
19816 assert(p);
19817 assert(options);
19818 assert(typeof cb === 'function');
19819
19820 // sunos lets the root user unlink directories, which is... weird.
19821 // so we have to lstat here and make sure it's not a dir.
19822 options.lstat(p, (er, st) => {
19823 if (er && er.code === 'ENOENT') {
19824 return cb(null)
19825 }
19826
19827 // Windows can EPERM on stat. Life is suffering.
19828 if (er && er.code === 'EPERM' && isWindows) {
19829 return fixWinEPERM(p, options, er, cb)
19830 }
19831
19832 if (st && st.isDirectory()) {
19833 return rmdir(p, options, er, cb)
19834 }
19835
19836 options.unlink(p, er => {
19837 if (er) {
19838 if (er.code === 'ENOENT') {
19839 return cb(null)
19840 }
19841 if (er.code === 'EPERM') {
19842 return (isWindows)
19843 ? fixWinEPERM(p, options, er, cb)
19844 : rmdir(p, options, er, cb)
19845 }
19846 if (er.code === 'EISDIR') {
19847 return rmdir(p, options, er, cb)
19848 }
19849 }
19850 return cb(er)
19851 });
19852 });
19853 }
19854
19855 function fixWinEPERM (p, options, er, cb) {
19856 assert(p);
19857 assert(options);
19858 assert(typeof cb === 'function');
19859 if (er) {
19860 assert(er instanceof Error);
19861 }
19862
19863 options.chmod(p, 666, er2 => {
19864 if (er2) {
19865 cb(er2.code === 'ENOENT' ? null : er);
19866 } else {
19867 options.stat(p, (er3, stats) => {
19868 if (er3) {
19869 cb(er3.code === 'ENOENT' ? null : er);
19870 } else if (stats.isDirectory()) {
19871 rmdir(p, options, er, cb);
19872 } else {
19873 options.unlink(p, cb);
19874 }
19875 });
19876 }
19877 });
19878 }
19879
19880 function fixWinEPERMSync (p, options, er) {
19881 let stats;
19882
19883 assert(p);
19884 assert(options);
19885 if (er) {
19886 assert(er instanceof Error);
19887 }
19888
19889 try {
19890 options.chmodSync(p, 666);
19891 } catch (er2) {
19892 if (er2.code === 'ENOENT') {
19893 return
19894 } else {
19895 throw er
19896 }
19897 }
19898
19899 try {
19900 stats = options.statSync(p);
19901 } catch (er3) {
19902 if (er3.code === 'ENOENT') {
19903 return
19904 } else {
19905 throw er
19906 }
19907 }
19908
19909 if (stats.isDirectory()) {
19910 rmdirSync(p, options, er);
19911 } else {
19912 options.unlinkSync(p);
19913 }
19914 }
19915
19916 function rmdir (p, options, originalEr, cb) {
19917 assert(p);
19918 assert(options);
19919 if (originalEr) {
19920 assert(originalEr instanceof Error);
19921 }
19922 assert(typeof cb === 'function');
19923
19924 // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
19925 // if we guessed wrong, and it's not a directory, then
19926 // raise the original error.
19927 options.rmdir(p, er => {
19928 if (er && (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM')) {
19929 rmkids(p, options, cb);
19930 } else if (er && er.code === 'ENOTDIR') {
19931 cb(originalEr);
19932 } else {
19933 cb(er);
19934 }
19935 });
19936 }
19937
19938 function rmkids (p, options, cb) {
19939 assert(p);
19940 assert(options);
19941 assert(typeof cb === 'function');
19942
19943 options.readdir(p, (er, files) => {
19944 if (er) return cb(er)
19945
19946 let n = files.length;
19947 let errState;
19948
19949 if (n === 0) return options.rmdir(p, cb)
19950
19951 files.forEach(f => {
19952 rimraf(path__default.join(p, f), options, er => {
19953 if (errState) {
19954 return
19955 }
19956 if (er) return cb(errState = er)
19957 if (--n === 0) {
19958 options.rmdir(p, cb);
19959 }
19960 });
19961 });
19962 });
19963 }
19964
19965 // this looks simpler, and is strictly *faster*, but will
19966 // tie up the JavaScript thread and fail on excessively
19967 // deep directory trees.
19968 function rimrafSync (p, options) {
19969 let st;
19970
19971 options = options || {};
19972 defaults(options);
19973
19974 assert(p, 'rimraf: missing path');
19975 assert.equal(typeof p, 'string', 'rimraf: path should be a string');
19976 assert(options, 'rimraf: missing options');
19977 assert.equal(typeof options, 'object', 'rimraf: options should be object');
19978
19979 try {
19980 st = options.lstatSync(p);
19981 } catch (er) {
19982 if (er.code === 'ENOENT') {
19983 return
19984 }
19985
19986 // Windows can EPERM on stat. Life is suffering.
19987 if (er.code === 'EPERM' && isWindows) {
19988 fixWinEPERMSync(p, options, er);
19989 }
19990 }
19991
19992 try {
19993 // sunos lets the root user unlink directories, which is... weird.
19994 if (st && st.isDirectory()) {
19995 rmdirSync(p, options, null);
19996 } else {
19997 options.unlinkSync(p);
19998 }
19999 } catch (er) {
20000 if (er.code === 'ENOENT') {
20001 return
20002 } else if (er.code === 'EPERM') {
20003 return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
20004 } else if (er.code !== 'EISDIR') {
20005 throw er
20006 }
20007 rmdirSync(p, options, er);
20008 }
20009 }
20010
20011 function rmdirSync (p, options, originalEr) {
20012 assert(p);
20013 assert(options);
20014 if (originalEr) {
20015 assert(originalEr instanceof Error);
20016 }
20017
20018 try {
20019 options.rmdirSync(p);
20020 } catch (er) {
20021 if (er.code === 'ENOTDIR') {
20022 throw originalEr
20023 } else if (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM') {
20024 rmkidsSync(p, options);
20025 } else if (er.code !== 'ENOENT') {
20026 throw er
20027 }
20028 }
20029 }
20030
20031 function rmkidsSync (p, options) {
20032 assert(p);
20033 assert(options);
20034 options.readdirSync(p).forEach(f => rimrafSync(path__default.join(p, f), options));
20035
20036 // We only end up here once we got ENOTEMPTY at least once, and
20037 // at this point, we are guaranteed to have removed all the kids.
20038 // So, we know that it won't be ENOENT or ENOTDIR or anything else.
20039 // try really hard to delete stuff on windows, because it has a
20040 // PROFOUNDLY annoying habit of not closing handles promptly when
20041 // files are deleted, resulting in spurious ENOTEMPTY errors.
20042 const retries = isWindows ? 100 : 1;
20043 let i = 0;
20044 do {
20045 let threw = true;
20046 try {
20047 const ret = options.rmdirSync(p, options);
20048 threw = false;
20049 return ret
20050 } finally {
20051 if (++i < retries && threw) continue // eslint-disable-line
20052 }
20053 } while (true)
20054 }
20055
20056 var rimraf_1 = rimraf;
20057 rimraf.sync = rimrafSync;
20058
20059 const u$3 = universalify.fromCallback;
20060
20061
20062 var remove = {
20063 remove: u$3(rimraf_1),
20064 removeSync: rimraf_1.sync
20065 };
20066
20067 var _fs;
20068 try {
20069 _fs = gracefulFs;
20070 } catch (_) {
20071 _fs = fs$2__default;
20072 }
20073
20074 function readFile (file, options, callback) {
20075 if (callback == null) {
20076 callback = options;
20077 options = {};
20078 }
20079
20080 if (typeof options === 'string') {
20081 options = {encoding: options};
20082 }
20083
20084 options = options || {};
20085 var fs = options.fs || _fs;
20086
20087 var shouldThrow = true;
20088 if ('throws' in options) {
20089 shouldThrow = options.throws;
20090 }
20091
20092 fs.readFile(file, options, function (err, data) {
20093 if (err) return callback(err)
20094
20095 data = stripBom(data);
20096
20097 var obj;
20098 try {
20099 obj = JSON.parse(data, options ? options.reviver : null);
20100 } catch (err2) {
20101 if (shouldThrow) {
20102 err2.message = file + ': ' + err2.message;
20103 return callback(err2)
20104 } else {
20105 return callback(null, null)
20106 }
20107 }
20108
20109 callback(null, obj);
20110 });
20111 }
20112
20113 function readFileSync (file, options) {
20114 options = options || {};
20115 if (typeof options === 'string') {
20116 options = {encoding: options};
20117 }
20118
20119 var fs = options.fs || _fs;
20120
20121 var shouldThrow = true;
20122 if ('throws' in options) {
20123 shouldThrow = options.throws;
20124 }
20125
20126 try {
20127 var content = fs.readFileSync(file, options);
20128 content = stripBom(content);
20129 return JSON.parse(content, options.reviver)
20130 } catch (err) {
20131 if (shouldThrow) {
20132 err.message = file + ': ' + err.message;
20133 throw err
20134 } else {
20135 return null
20136 }
20137 }
20138 }
20139
20140 function stringify$1 (obj, options) {
20141 var spaces;
20142 var EOL = '\n';
20143 if (typeof options === 'object' && options !== null) {
20144 if (options.spaces) {
20145 spaces = options.spaces;
20146 }
20147 if (options.EOL) {
20148 EOL = options.EOL;
20149 }
20150 }
20151
20152 var str = JSON.stringify(obj, options ? options.replacer : null, spaces);
20153
20154 return str.replace(/\n/g, EOL) + EOL
20155 }
20156
20157 function writeFile (file, obj, options, callback) {
20158 if (callback == null) {
20159 callback = options;
20160 options = {};
20161 }
20162 options = options || {};
20163 var fs = options.fs || _fs;
20164
20165 var str = '';
20166 try {
20167 str = stringify$1(obj, options);
20168 } catch (err) {
20169 // Need to return whether a callback was passed or not
20170 if (callback) callback(err, null);
20171 return
20172 }
20173
20174 fs.writeFile(file, str, options, callback);
20175 }
20176
20177 function writeFileSync (file, obj, options) {
20178 options = options || {};
20179 var fs = options.fs || _fs;
20180
20181 var str = stringify$1(obj, options);
20182 // not sure if fs.writeFileSync returns anything, but just in case
20183 return fs.writeFileSync(file, str, options)
20184 }
20185
20186 function stripBom (content) {
20187 // we do this because JSON.parse would convert it to a utf8 string if encoding wasn't specified
20188 if (Buffer.isBuffer(content)) content = content.toString('utf8');
20189 content = content.replace(/^\uFEFF/, '');
20190 return content
20191 }
20192
20193 var jsonfile = {
20194 readFile: readFile,
20195 readFileSync: readFileSync,
20196 writeFile: writeFile,
20197 writeFileSync: writeFileSync
20198 };
20199
20200 var jsonfile_1 = jsonfile;
20201
20202 const u$4 = universalify.fromCallback;
20203
20204
20205 var jsonfile$1 = {
20206 // jsonfile exports
20207 readJson: u$4(jsonfile_1.readFile),
20208 readJsonSync: jsonfile_1.readFileSync,
20209 writeJson: u$4(jsonfile_1.writeFile),
20210 writeJsonSync: jsonfile_1.writeFileSync
20211 };
20212
20213 const pathExists$2 = pathExists_1.pathExists;
20214
20215
20216 function outputJson (file, data, options, callback) {
20217 if (typeof options === 'function') {
20218 callback = options;
20219 options = {};
20220 }
20221
20222 const dir = path__default.dirname(file);
20223
20224 pathExists$2(dir, (err, itDoes) => {
20225 if (err) return callback(err)
20226 if (itDoes) return jsonfile$1.writeJson(file, data, options, callback)
20227
20228 mkdirs_1$1.mkdirs(dir, err => {
20229 if (err) return callback(err)
20230 jsonfile$1.writeJson(file, data, options, callback);
20231 });
20232 });
20233 }
20234
20235 var outputJson_1 = outputJson;
20236
20237 function outputJsonSync (file, data, options) {
20238 const dir = path__default.dirname(file);
20239
20240 if (!gracefulFs.existsSync(dir)) {
20241 mkdirs_1$1.mkdirsSync(dir);
20242 }
20243
20244 jsonfile$1.writeJsonSync(file, data, options);
20245 }
20246
20247 var outputJsonSync_1 = outputJsonSync;
20248
20249 const u$5 = universalify.fromCallback;
20250
20251
20252 jsonfile$1.outputJson = u$5(outputJson_1);
20253 jsonfile$1.outputJsonSync = outputJsonSync_1;
20254 // aliases
20255 jsonfile$1.outputJSON = jsonfile$1.outputJson;
20256 jsonfile$1.outputJSONSync = jsonfile$1.outputJsonSync;
20257 jsonfile$1.writeJSON = jsonfile$1.writeJson;
20258 jsonfile$1.writeJSONSync = jsonfile$1.writeJsonSync;
20259 jsonfile$1.readJSON = jsonfile$1.readJson;
20260 jsonfile$1.readJSONSync = jsonfile$1.readJsonSync;
20261
20262 var json = jsonfile$1;
20263
20264 // most of this code was written by Andrew Kelley
20265 // licensed under the BSD license: see
20266 // https://github.com/andrewrk/node-mv/blob/master/package.json
20267
20268 // this needs a cleanup
20269
20270 const u$6 = universalify.fromCallback;
20271
20272
20273
20274 const remove$1 = remove.remove;
20275 const mkdirp = mkdirs_1$1.mkdirs;
20276
20277 function move (src, dest, options, callback) {
20278 if (typeof options === 'function') {
20279 callback = options;
20280 options = {};
20281 }
20282
20283 const overwrite = options.overwrite || options.clobber || false;
20284
20285 isSrcSubdir(src, dest, (err, itIs) => {
20286 if (err) return callback(err)
20287 if (itIs) return callback(new Error(`Cannot move '${src}' to a subdirectory of itself, '${dest}'.`))
20288 mkdirp(path__default.dirname(dest), err => {
20289 if (err) return callback(err)
20290 doRename();
20291 });
20292 });
20293
20294 function doRename () {
20295 if (path__default.resolve(src) === path__default.resolve(dest)) {
20296 gracefulFs.access(src, callback);
20297 } else if (overwrite) {
20298 gracefulFs.rename(src, dest, err => {
20299 if (!err) return callback()
20300
20301 if (err.code === 'ENOTEMPTY' || err.code === 'EEXIST') {
20302 remove$1(dest, err => {
20303 if (err) return callback(err)
20304 options.overwrite = false; // just overwriteed it, no need to do it again
20305 move(src, dest, options, callback);
20306 });
20307 return
20308 }
20309
20310 // weird Windows shit
20311 if (err.code === 'EPERM') {
20312 setTimeout(() => {
20313 remove$1(dest, err => {
20314 if (err) return callback(err)
20315 options.overwrite = false;
20316 move(src, dest, options, callback);
20317 });
20318 }, 200);
20319 return
20320 }
20321
20322 if (err.code !== 'EXDEV') return callback(err)
20323 moveAcrossDevice(src, dest, overwrite, callback);
20324 });
20325 } else {
20326 gracefulFs.link(src, dest, err => {
20327 if (err) {
20328 if (err.code === 'EXDEV' || err.code === 'EISDIR' || err.code === 'EPERM' || err.code === 'ENOTSUP') {
20329 return moveAcrossDevice(src, dest, overwrite, callback)
20330 }
20331 return callback(err)
20332 }
20333 return gracefulFs.unlink(src, callback)
20334 });
20335 }
20336 }
20337 }
20338
20339 function moveAcrossDevice (src, dest, overwrite, callback) {
20340 gracefulFs.stat(src, (err, stat) => {
20341 if (err) return callback(err)
20342
20343 if (stat.isDirectory()) {
20344 moveDirAcrossDevice(src, dest, overwrite, callback);
20345 } else {
20346 moveFileAcrossDevice(src, dest, overwrite, callback);
20347 }
20348 });
20349 }
20350
20351 function moveFileAcrossDevice (src, dest, overwrite, callback) {
20352 const flags = overwrite ? 'w' : 'wx';
20353 const ins = gracefulFs.createReadStream(src);
20354 const outs = gracefulFs.createWriteStream(dest, { flags });
20355
20356 ins.on('error', err => {
20357 ins.destroy();
20358 outs.destroy();
20359 outs.removeListener('close', onClose);
20360
20361 // may want to create a directory but `out` line above
20362 // creates an empty file for us: See #108
20363 // don't care about error here
20364 gracefulFs.unlink(dest, () => {
20365 // note: `err` here is from the input stream errror
20366 if (err.code === 'EISDIR' || err.code === 'EPERM') {
20367 moveDirAcrossDevice(src, dest, overwrite, callback);
20368 } else {
20369 callback(err);
20370 }
20371 });
20372 });
20373
20374 outs.on('error', err => {
20375 ins.destroy();
20376 outs.destroy();
20377 outs.removeListener('close', onClose);
20378 callback(err);
20379 });
20380
20381 outs.once('close', onClose);
20382 ins.pipe(outs);
20383
20384 function onClose () {
20385 gracefulFs.unlink(src, callback);
20386 }
20387 }
20388
20389 function moveDirAcrossDevice (src, dest, overwrite, callback) {
20390 const options = {
20391 overwrite: false
20392 };
20393
20394 if (overwrite) {
20395 remove$1(dest, err => {
20396 if (err) return callback(err)
20397 startNcp();
20398 });
20399 } else {
20400 startNcp();
20401 }
20402
20403 function startNcp () {
20404 ncp_1(src, dest, options, err => {
20405 if (err) return callback(err)
20406 remove$1(src, callback);
20407 });
20408 }
20409 }
20410
20411 // return true if dest is a subdir of src, otherwise false.
20412 // extract dest base dir and check if that is the same as src basename
20413 function isSrcSubdir (src, dest, cb) {
20414 gracefulFs.stat(src, (err, st) => {
20415 if (err) return cb(err)
20416 if (st.isDirectory()) {
20417 const baseDir = dest.split(path__default.dirname(src) + path__default.sep)[1];
20418 if (baseDir) {
20419 const destBasename = baseDir.split(path__default.sep)[0];
20420 if (destBasename) return cb(null, src !== dest && dest.indexOf(src) > -1 && destBasename === path__default.basename(src))
20421 return cb(null, false)
20422 }
20423 return cb(null, false)
20424 }
20425 return cb(null, false)
20426 });
20427 }
20428
20429 var move_1 = {
20430 move: u$6(move)
20431 };
20432
20433 const copySync$2 = copySync$1.copySync;
20434 const removeSync = remove.removeSync;
20435 const mkdirpSync = mkdirs_1$1.mkdirsSync;
20436
20437
20438 function moveSync (src, dest, options) {
20439 options = options || {};
20440 const overwrite = options.overwrite || options.clobber || false;
20441
20442 src = path__default.resolve(src);
20443 dest = path__default.resolve(dest);
20444
20445 if (src === dest) return gracefulFs.accessSync(src)
20446
20447 if (isSrcSubdir$1(src, dest)) throw new Error(`Cannot move '${src}' into itself '${dest}'.`)
20448
20449 mkdirpSync(path__default.dirname(dest));
20450 tryRenameSync();
20451
20452 function tryRenameSync () {
20453 if (overwrite) {
20454 try {
20455 return gracefulFs.renameSync(src, dest)
20456 } catch (err) {
20457 if (err.code === 'ENOTEMPTY' || err.code === 'EEXIST' || err.code === 'EPERM') {
20458 removeSync(dest);
20459 options.overwrite = false; // just overwriteed it, no need to do it again
20460 return moveSync(src, dest, options)
20461 }
20462
20463 if (err.code !== 'EXDEV') throw err
20464 return moveSyncAcrossDevice(src, dest, overwrite)
20465 }
20466 } else {
20467 try {
20468 gracefulFs.linkSync(src, dest);
20469 return gracefulFs.unlinkSync(src)
20470 } catch (err) {
20471 if (err.code === 'EXDEV' || err.code === 'EISDIR' || err.code === 'EPERM' || err.code === 'ENOTSUP') {
20472 return moveSyncAcrossDevice(src, dest, overwrite)
20473 }
20474 throw err
20475 }
20476 }
20477 }
20478 }
20479
20480 function moveSyncAcrossDevice (src, dest, overwrite) {
20481 const stat = gracefulFs.statSync(src);
20482
20483 if (stat.isDirectory()) {
20484 return moveDirSyncAcrossDevice(src, dest, overwrite)
20485 } else {
20486 return moveFileSyncAcrossDevice(src, dest, overwrite)
20487 }
20488 }
20489
20490 function moveFileSyncAcrossDevice (src, dest, overwrite) {
20491 const BUF_LENGTH = 64 * 1024;
20492 const _buff = buffer(BUF_LENGTH);
20493
20494 const flags = overwrite ? 'w' : 'wx';
20495
20496 const fdr = gracefulFs.openSync(src, 'r');
20497 const stat = gracefulFs.fstatSync(fdr);
20498 const fdw = gracefulFs.openSync(dest, flags, stat.mode);
20499 let bytesRead = 1;
20500 let pos = 0;
20501
20502 while (bytesRead > 0) {
20503 bytesRead = gracefulFs.readSync(fdr, _buff, 0, BUF_LENGTH, pos);
20504 gracefulFs.writeSync(fdw, _buff, 0, bytesRead);
20505 pos += bytesRead;
20506 }
20507
20508 gracefulFs.closeSync(fdr);
20509 gracefulFs.closeSync(fdw);
20510 return gracefulFs.unlinkSync(src)
20511 }
20512
20513 function moveDirSyncAcrossDevice (src, dest, overwrite) {
20514 const options = {
20515 overwrite: false
20516 };
20517
20518 if (overwrite) {
20519 removeSync(dest);
20520 tryCopySync();
20521 } else {
20522 tryCopySync();
20523 }
20524
20525 function tryCopySync () {
20526 copySync$2(src, dest, options);
20527 return removeSync(src)
20528 }
20529 }
20530
20531 // return true if dest is a subdir of src, otherwise false.
20532 // extract dest base dir and check if that is the same as src basename
20533 function isSrcSubdir$1 (src, dest) {
20534 try {
20535 return gracefulFs.statSync(src).isDirectory() &&
20536 src !== dest &&
20537 dest.indexOf(src) > -1 &&
20538 dest.split(path__default.dirname(src) + path__default.sep)[1].split(path__default.sep)[0] === path__default.basename(src)
20539 } catch (e) {
20540 return false
20541 }
20542 }
20543
20544 var moveSync_1 = {
20545 moveSync
20546 };
20547
20548 const u$7 = universalify.fromCallback;
20549
20550
20551
20552
20553
20554 const emptyDir = u$7(function emptyDir (dir, callback) {
20555 callback = callback || function () {};
20556 fs$2__default.readdir(dir, (err, items) => {
20557 if (err) return mkdirs_1$1.mkdirs(dir, callback)
20558
20559 items = items.map(item => path__default.join(dir, item));
20560
20561 deleteItem();
20562
20563 function deleteItem () {
20564 const item = items.pop();
20565 if (!item) return callback()
20566 remove.remove(item, err => {
20567 if (err) return callback(err)
20568 deleteItem();
20569 });
20570 }
20571 });
20572 });
20573
20574 function emptyDirSync (dir) {
20575 let items;
20576 try {
20577 items = fs$2__default.readdirSync(dir);
20578 } catch (err) {
20579 return mkdirs_1$1.mkdirsSync(dir)
20580 }
20581
20582 items.forEach(item => {
20583 item = path__default.join(dir, item);
20584 remove.removeSync(item);
20585 });
20586 }
20587
20588 var empty = {
20589 emptyDirSync,
20590 emptydirSync: emptyDirSync,
20591 emptyDir,
20592 emptydir: emptyDir
20593 };
20594
20595 const u$8 = universalify.fromCallback;
20596
20597
20598
20599 const pathExists$3 = pathExists_1.pathExists;
20600
20601 function createFile (file, callback) {
20602 function makeFile () {
20603 gracefulFs.writeFile(file, '', err => {
20604 if (err) return callback(err)
20605 callback();
20606 });
20607 }
20608
20609 gracefulFs.stat(file, (err, stats) => { // eslint-disable-line handle-callback-err
20610 if (!err && stats.isFile()) return callback()
20611 const dir = path__default.dirname(file);
20612 pathExists$3(dir, (err, dirExists) => {
20613 if (err) return callback(err)
20614 if (dirExists) return makeFile()
20615 mkdirs_1$1.mkdirs(dir, err => {
20616 if (err) return callback(err)
20617 makeFile();
20618 });
20619 });
20620 });
20621 }
20622
20623 function createFileSync (file) {
20624 let stats;
20625 try {
20626 stats = gracefulFs.statSync(file);
20627 } catch (e) {}
20628 if (stats && stats.isFile()) return
20629
20630 const dir = path__default.dirname(file);
20631 if (!gracefulFs.existsSync(dir)) {
20632 mkdirs_1$1.mkdirsSync(dir);
20633 }
20634
20635 gracefulFs.writeFileSync(file, '');
20636 }
20637
20638 var file = {
20639 createFile: u$8(createFile),
20640 createFileSync
20641 };
20642
20643 const u$9 = universalify.fromCallback;
20644
20645
20646
20647 const pathExists$4 = pathExists_1.pathExists;
20648
20649 function createLink (srcpath, dstpath, callback) {
20650 function makeLink (srcpath, dstpath) {
20651 gracefulFs.link(srcpath, dstpath, err => {
20652 if (err) return callback(err)
20653 callback(null);
20654 });
20655 }
20656
20657 pathExists$4(dstpath, (err, destinationExists) => {
20658 if (err) return callback(err)
20659 if (destinationExists) return callback(null)
20660 gracefulFs.lstat(srcpath, (err, stat) => {
20661 if (err) {
20662 err.message = err.message.replace('lstat', 'ensureLink');
20663 return callback(err)
20664 }
20665
20666 const dir = path__default.dirname(dstpath);
20667 pathExists$4(dir, (err, dirExists) => {
20668 if (err) return callback(err)
20669 if (dirExists) return makeLink(srcpath, dstpath)
20670 mkdirs_1$1.mkdirs(dir, err => {
20671 if (err) return callback(err)
20672 makeLink(srcpath, dstpath);
20673 });
20674 });
20675 });
20676 });
20677 }
20678
20679 function createLinkSync (srcpath, dstpath, callback) {
20680 const destinationExists = gracefulFs.existsSync(dstpath);
20681 if (destinationExists) return undefined
20682
20683 try {
20684 gracefulFs.lstatSync(srcpath);
20685 } catch (err) {
20686 err.message = err.message.replace('lstat', 'ensureLink');
20687 throw err
20688 }
20689
20690 const dir = path__default.dirname(dstpath);
20691 const dirExists = gracefulFs.existsSync(dir);
20692 if (dirExists) return gracefulFs.linkSync(srcpath, dstpath)
20693 mkdirs_1$1.mkdirsSync(dir);
20694
20695 return gracefulFs.linkSync(srcpath, dstpath)
20696 }
20697
20698 var link = {
20699 createLink: u$9(createLink),
20700 createLinkSync
20701 };
20702
20703 const pathExists$5 = pathExists_1.pathExists;
20704
20705 /**
20706 * Function that returns two types of paths, one relative to symlink, and one
20707 * relative to the current working directory. Checks if path is absolute or
20708 * relative. If the path is relative, this function checks if the path is
20709 * relative to symlink or relative to current working directory. This is an
20710 * initiative to find a smarter `srcpath` to supply when building symlinks.
20711 * This allows you to determine which path to use out of one of three possible
20712 * types of source paths. The first is an absolute path. This is detected by
20713 * `path.isAbsolute()`. When an absolute path is provided, it is checked to
20714 * see if it exists. If it does it's used, if not an error is returned
20715 * (callback)/ thrown (sync). The other two options for `srcpath` are a
20716 * relative url. By default Node's `fs.symlink` works by creating a symlink
20717 * using `dstpath` and expects the `srcpath` to be relative to the newly
20718 * created symlink. If you provide a `srcpath` that does not exist on the file
20719 * system it results in a broken symlink. To minimize this, the function
20720 * checks to see if the 'relative to symlink' source file exists, and if it
20721 * does it will use it. If it does not, it checks if there's a file that
20722 * exists that is relative to the current working directory, if does its used.
20723 * This preserves the expectations of the original fs.symlink spec and adds
20724 * the ability to pass in `relative to current working direcotry` paths.
20725 */
20726
20727 function symlinkPaths (srcpath, dstpath, callback) {
20728 if (path__default.isAbsolute(srcpath)) {
20729 return gracefulFs.lstat(srcpath, (err, stat) => {
20730 if (err) {
20731 err.message = err.message.replace('lstat', 'ensureSymlink');
20732 return callback(err)
20733 }
20734 return callback(null, {
20735 'toCwd': srcpath,
20736 'toDst': srcpath
20737 })
20738 })
20739 } else {
20740 const dstdir = path__default.dirname(dstpath);
20741 const relativeToDst = path__default.join(dstdir, srcpath);
20742 return pathExists$5(relativeToDst, (err, exists) => {
20743 if (err) return callback(err)
20744 if (exists) {
20745 return callback(null, {
20746 'toCwd': relativeToDst,
20747 'toDst': srcpath
20748 })
20749 } else {
20750 return gracefulFs.lstat(srcpath, (err, stat) => {
20751 if (err) {
20752 err.message = err.message.replace('lstat', 'ensureSymlink');
20753 return callback(err)
20754 }
20755 return callback(null, {
20756 'toCwd': srcpath,
20757 'toDst': path__default.relative(dstdir, srcpath)
20758 })
20759 })
20760 }
20761 })
20762 }
20763 }
20764
20765 function symlinkPathsSync (srcpath, dstpath) {
20766 let exists;
20767 if (path__default.isAbsolute(srcpath)) {
20768 exists = gracefulFs.existsSync(srcpath);
20769 if (!exists) throw new Error('absolute srcpath does not exist')
20770 return {
20771 'toCwd': srcpath,
20772 'toDst': srcpath
20773 }
20774 } else {
20775 const dstdir = path__default.dirname(dstpath);
20776 const relativeToDst = path__default.join(dstdir, srcpath);
20777 exists = gracefulFs.existsSync(relativeToDst);
20778 if (exists) {
20779 return {
20780 'toCwd': relativeToDst,
20781 'toDst': srcpath
20782 }
20783 } else {
20784 exists = gracefulFs.existsSync(srcpath);
20785 if (!exists) throw new Error('relative srcpath does not exist')
20786 return {
20787 'toCwd': srcpath,
20788 'toDst': path__default.relative(dstdir, srcpath)
20789 }
20790 }
20791 }
20792 }
20793
20794 var symlinkPaths_1 = {
20795 symlinkPaths,
20796 symlinkPathsSync
20797 };
20798
20799 function symlinkType (srcpath, type, callback) {
20800 callback = (typeof type === 'function') ? type : callback;
20801 type = (typeof type === 'function') ? false : type;
20802 if (type) return callback(null, type)
20803 gracefulFs.lstat(srcpath, (err, stats) => {
20804 if (err) return callback(null, 'file')
20805 type = (stats && stats.isDirectory()) ? 'dir' : 'file';
20806 callback(null, type);
20807 });
20808 }
20809
20810 function symlinkTypeSync (srcpath, type) {
20811 let stats;
20812
20813 if (type) return type
20814 try {
20815 stats = gracefulFs.lstatSync(srcpath);
20816 } catch (e) {
20817 return 'file'
20818 }
20819 return (stats && stats.isDirectory()) ? 'dir' : 'file'
20820 }
20821
20822 var symlinkType_1 = {
20823 symlinkType,
20824 symlinkTypeSync
20825 };
20826
20827 const u$a = universalify.fromCallback;
20828
20829
20830
20831 const mkdirs$2 = mkdirs_1$1.mkdirs;
20832 const mkdirsSync$1 = mkdirs_1$1.mkdirsSync;
20833
20834
20835 const symlinkPaths$1 = symlinkPaths_1.symlinkPaths;
20836 const symlinkPathsSync$1 = symlinkPaths_1.symlinkPathsSync;
20837
20838
20839 const symlinkType$1 = symlinkType_1.symlinkType;
20840 const symlinkTypeSync$1 = symlinkType_1.symlinkTypeSync;
20841
20842 const pathExists$6 = pathExists_1.pathExists;
20843
20844 function createSymlink (srcpath, dstpath, type, callback) {
20845 callback = (typeof type === 'function') ? type : callback;
20846 type = (typeof type === 'function') ? false : type;
20847
20848 pathExists$6(dstpath, (err, destinationExists) => {
20849 if (err) return callback(err)
20850 if (destinationExists) return callback(null)
20851 symlinkPaths$1(srcpath, dstpath, (err, relative) => {
20852 if (err) return callback(err)
20853 srcpath = relative.toDst;
20854 symlinkType$1(relative.toCwd, type, (err, type) => {
20855 if (err) return callback(err)
20856 const dir = path__default.dirname(dstpath);
20857 pathExists$6(dir, (err, dirExists) => {
20858 if (err) return callback(err)
20859 if (dirExists) return gracefulFs.symlink(srcpath, dstpath, type, callback)
20860 mkdirs$2(dir, err => {
20861 if (err) return callback(err)
20862 gracefulFs.symlink(srcpath, dstpath, type, callback);
20863 });
20864 });
20865 });
20866 });
20867 });
20868 }
20869
20870 function createSymlinkSync (srcpath, dstpath, type, callback) {
20871 type = (typeof type === 'function') ? false : type;
20872
20873 const destinationExists = gracefulFs.existsSync(dstpath);
20874 if (destinationExists) return undefined
20875
20876 const relative = symlinkPathsSync$1(srcpath, dstpath);
20877 srcpath = relative.toDst;
20878 type = symlinkTypeSync$1(relative.toCwd, type);
20879 const dir = path__default.dirname(dstpath);
20880 const exists = gracefulFs.existsSync(dir);
20881 if (exists) return gracefulFs.symlinkSync(srcpath, dstpath, type)
20882 mkdirsSync$1(dir);
20883 return gracefulFs.symlinkSync(srcpath, dstpath, type)
20884 }
20885
20886 var symlink = {
20887 createSymlink: u$a(createSymlink),
20888 createSymlinkSync
20889 };
20890
20891 var ensure = {
20892 // file
20893 createFile: file.createFile,
20894 createFileSync: file.createFileSync,
20895 ensureFile: file.createFile,
20896 ensureFileSync: file.createFileSync,
20897 // link
20898 createLink: link.createLink,
20899 createLinkSync: link.createLinkSync,
20900 ensureLink: link.createLink,
20901 ensureLinkSync: link.createLinkSync,
20902 // symlink
20903 createSymlink: symlink.createSymlink,
20904 createSymlinkSync: symlink.createSymlinkSync,
20905 ensureSymlink: symlink.createSymlink,
20906 ensureSymlinkSync: symlink.createSymlinkSync
20907 };
20908
20909 const u$b = universalify.fromCallback;
20910
20911
20912
20913 const pathExists$7 = pathExists_1.pathExists;
20914
20915 function outputFile (file, data, encoding, callback) {
20916 if (typeof encoding === 'function') {
20917 callback = encoding;
20918 encoding = 'utf8';
20919 }
20920
20921 const dir = path__default.dirname(file);
20922 pathExists$7(dir, (err, itDoes) => {
20923 if (err) return callback(err)
20924 if (itDoes) return gracefulFs.writeFile(file, data, encoding, callback)
20925
20926 mkdirs_1$1.mkdirs(dir, err => {
20927 if (err) return callback(err)
20928
20929 gracefulFs.writeFile(file, data, encoding, callback);
20930 });
20931 });
20932 }
20933
20934 function outputFileSync (file, data, encoding) {
20935 const dir = path__default.dirname(file);
20936 if (gracefulFs.existsSync(dir)) {
20937 return gracefulFs.writeFileSync.apply(gracefulFs, arguments)
20938 }
20939 mkdirs_1$1.mkdirsSync(dir);
20940 gracefulFs.writeFileSync.apply(gracefulFs, arguments);
20941 }
20942
20943 var output = {
20944 outputFile: u$b(outputFile),
20945 outputFileSync
20946 };
20947
20948 const fs$1 = {};
20949
20950 // Export graceful-fs:
20951 assign_1(fs$1, fs_1);
20952 // Export extra methods:
20953 assign_1(fs$1, copy$1);
20954 assign_1(fs$1, copySync$1);
20955 assign_1(fs$1, mkdirs_1$1);
20956 assign_1(fs$1, remove);
20957 assign_1(fs$1, json);
20958 assign_1(fs$1, move_1);
20959 assign_1(fs$1, moveSync_1);
20960 assign_1(fs$1, empty);
20961 assign_1(fs$1, ensure);
20962 assign_1(fs$1, output);
20963 assign_1(fs$1, pathExists_1);
20964
20965 var lib = fs$1;
20966
20967 var fsExtra = /*#__PURE__*/Object.freeze({
20968 __proto__: null,
20969 'default': lib,
20970 __moduleExports: lib
20971 });
20972
20973 /**
20974 * @license
20975 * Copyright Google LLC All Rights Reserved.
20976 *
20977 * Use of this source code is governed by an MIT-style license that can be
20978 * found in the LICENSE file at https://angular.io/license
20979 */
20980 /**
20981 * A wrapper around the Node.js file-system that supports path manipulation.
20982 */
20983 class NodeJSPathManipulation {
20984 pwd() {
20985 return this.normalize(process.cwd());
20986 }
20987 chdir(dir) {
20988 process.chdir(dir);
20989 }
20990 resolve(...paths) {
20991 return this.normalize(path.resolve(...paths));
20992 }
20993 dirname(file) {
20994 return this.normalize(path.dirname(file));
20995 }
20996 join(basePath, ...paths) {
20997 return this.normalize(path.join(basePath, ...paths));
20998 }
20999 isRoot(path) {
21000 return this.dirname(path) === this.normalize(path);
21001 }
21002 isRooted(path$1) {
21003 return path.isAbsolute(path$1);
21004 }
21005 relative(from, to) {
21006 return this.normalize(path.relative(from, to));
21007 }
21008 basename(filePath, extension) {
21009 return path.basename(filePath, extension);
21010 }
21011 extname(path$1) {
21012 return path.extname(path$1);
21013 }
21014 normalize(path) {
21015 // Convert backslashes to forward slashes
21016 return path.replace(/\\/g, '/');
21017 }
21018 }
21019 /**
21020 * A wrapper around the Node.js file-system that supports readonly operations and path manipulation.
21021 */
21022 class NodeJSReadonlyFileSystem extends NodeJSPathManipulation {
21023 constructor() {
21024 super(...arguments);
21025 this._caseSensitive = undefined;
21026 }
21027 isCaseSensitive() {
21028 if (this._caseSensitive === undefined) {
21029 // Note the use of the real file-system is intentional:
21030 // `this.exists()` relies upon `isCaseSensitive()` so that would cause an infinite recursion.
21031 this._caseSensitive = !fs$2.existsSync(this.normalize(toggleCase(__filename)));
21032 }
21033 return this._caseSensitive;
21034 }
21035 exists(path) {
21036 return fs$2.existsSync(path);
21037 }
21038 readFile(path) {
21039 return fs$2.readFileSync(path, 'utf8');
21040 }
21041 readFileBuffer(path) {
21042 return fs$2.readFileSync(path);
21043 }
21044 readdir(path) {
21045 return fs$2.readdirSync(path);
21046 }
21047 lstat(path) {
21048 return fs$2.lstatSync(path);
21049 }
21050 stat(path) {
21051 return fs$2.statSync(path);
21052 }
21053 realpath(path) {
21054 return this.resolve(fs$2.realpathSync(path));
21055 }
21056 getDefaultLibLocation() {
21057 return this.resolve(require.resolve('typescript'), '..');
21058 }
21059 }
21060 /**
21061 * A wrapper around the Node.js file-system (i.e. the `fs` package).
21062 */
21063 class NodeJSFileSystem extends NodeJSReadonlyFileSystem {
21064 writeFile(path, data, exclusive = false) {
21065 fs$2.writeFileSync(path, data, exclusive ? { flag: 'wx' } : undefined);
21066 }
21067 removeFile(path) {
21068 fs$2.unlinkSync(path);
21069 }
21070 symlink(target, path) {
21071 fs$2.symlinkSync(target, path);
21072 }
21073 copyFile(from, to) {
21074 fs$2.copyFileSync(from, to);
21075 }
21076 moveFile(from, to) {
21077 fs$2.renameSync(from, to);
21078 }
21079 ensureDir(path) {
21080 const parents = [];
21081 while (!this.isRoot(path) && !this.exists(path)) {
21082 parents.push(path);
21083 path = this.dirname(path);
21084 }
21085 while (parents.length) {
21086 this.safeMkdir(parents.pop());
21087 }
21088 }
21089 removeDeep(path) {
21090 undefined(path);
21091 }
21092 safeMkdir(path) {
21093 try {
21094 fs$2.mkdirSync(path);
21095 }
21096 catch (err) {
21097 // Ignore the error, if the path already exists and points to a directory.
21098 // Re-throw otherwise.
21099 if (!this.exists(path) || !this.stat(path).isDirectory()) {
21100 throw err;
21101 }
21102 }
21103 }
21104 }
21105 /**
21106 * Toggle the case of each character in a string.
21107 */
21108 function toggleCase(str) {
21109 return str.replace(/\w/g, ch => ch.toUpperCase() === ch ? ch.toLowerCase() : ch.toUpperCase());
21110 }
21111
21112 /**
21113 * @license
21114 * Copyright Google LLC All Rights Reserved.
21115 *
21116 * Use of this source code is governed by an MIT-style license that can be
21117 * found in the LICENSE file at https://angular.io/license
21118 */
21119 const VERSION$2 = new Version('11.2.0');
21120
21121 /**
21122 * @license
21123 * Copyright Google LLC All Rights Reserved.
21124 *
21125 * Use of this source code is governed by an MIT-style license that can be
21126 * found in the LICENSE file at https://angular.io/license
21127 */
21128 // In TypeScript 2.1 the spread element kind was renamed.
21129 const spreadElementSyntaxKind = ts$1.SyntaxKind.SpreadElement || ts$1.SyntaxKind.SpreadElementExpression;
21130 const empty$1 = ts$1.createNodeArray();
21131
21132 /**
21133 * @license
21134 * Copyright Google LLC All Rights Reserved.
21135 *
21136 * Use of this source code is governed by an MIT-style license that can be
21137 * found in the LICENSE file at https://angular.io/license
21138 */
21139 const UNKNOWN_ERROR_CODE = 500;
21140 var EmitFlags;
21141 (function (EmitFlags) {
21142 EmitFlags[EmitFlags["DTS"] = 1] = "DTS";
21143 EmitFlags[EmitFlags["JS"] = 2] = "JS";
21144 EmitFlags[EmitFlags["Metadata"] = 4] = "Metadata";
21145 EmitFlags[EmitFlags["I18nBundle"] = 8] = "I18nBundle";
21146 EmitFlags[EmitFlags["Codegen"] = 16] = "Codegen";
21147 EmitFlags[EmitFlags["Default"] = 19] = "Default";
21148 EmitFlags[EmitFlags["All"] = 31] = "All";
21149 })(EmitFlags || (EmitFlags = {}));
21150
21151 /*! *****************************************************************************
21152 Copyright (c) Microsoft Corporation.
21153
21154 Permission to use, copy, modify, and/or distribute this software for any
21155 purpose with or without fee is hereby granted.
21156
21157 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
21158 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
21159 AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
21160 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21161 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21162 OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21163 PERFORMANCE OF THIS SOFTWARE.
21164 ***************************************************************************** */
21165
21166 function __awaiter(thisArg, _arguments, P, generator) {
21167 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
21168 return new (P || (P = Promise))(function (resolve, reject) {
21169 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
21170 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
21171 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
21172 step((generator = generator.apply(thisArg, _arguments || [])).next());
21173 });
21174 }
21175
21176 /**
21177 * @license
21178 * Copyright Google LLC All Rights Reserved.
21179 *
21180 * Use of this source code is governed by an MIT-style license that can be
21181 * found in the LICENSE file at https://angular.io/license
21182 */
21183 /**
21184 * @publicApi
21185 */
21186 var ErrorCode;
21187 (function (ErrorCode) {
21188 ErrorCode[ErrorCode["DECORATOR_ARG_NOT_LITERAL"] = 1001] = "DECORATOR_ARG_NOT_LITERAL";
21189 ErrorCode[ErrorCode["DECORATOR_ARITY_WRONG"] = 1002] = "DECORATOR_ARITY_WRONG";
21190 ErrorCode[ErrorCode["DECORATOR_NOT_CALLED"] = 1003] = "DECORATOR_NOT_CALLED";
21191 ErrorCode[ErrorCode["DECORATOR_ON_ANONYMOUS_CLASS"] = 1004] = "DECORATOR_ON_ANONYMOUS_CLASS";
21192 ErrorCode[ErrorCode["DECORATOR_UNEXPECTED"] = 1005] = "DECORATOR_UNEXPECTED";
21193 /**
21194 * This error code indicates that there are incompatible decorators on a type or a class field.
21195 */
21196 ErrorCode[ErrorCode["DECORATOR_COLLISION"] = 1006] = "DECORATOR_COLLISION";
21197 ErrorCode[ErrorCode["VALUE_HAS_WRONG_TYPE"] = 1010] = "VALUE_HAS_WRONG_TYPE";
21198 ErrorCode[ErrorCode["VALUE_NOT_LITERAL"] = 1011] = "VALUE_NOT_LITERAL";
21199 ErrorCode[ErrorCode["COMPONENT_MISSING_TEMPLATE"] = 2001] = "COMPONENT_MISSING_TEMPLATE";
21200 ErrorCode[ErrorCode["PIPE_MISSING_NAME"] = 2002] = "PIPE_MISSING_NAME";
21201 ErrorCode[ErrorCode["PARAM_MISSING_TOKEN"] = 2003] = "PARAM_MISSING_TOKEN";
21202 ErrorCode[ErrorCode["DIRECTIVE_MISSING_SELECTOR"] = 2004] = "DIRECTIVE_MISSING_SELECTOR";
21203 /** Raised when an undecorated class is passed in as a provider to a module or a directive. */
21204 ErrorCode[ErrorCode["UNDECORATED_PROVIDER"] = 2005] = "UNDECORATED_PROVIDER";
21205 /**
21206 * Raised when a Directive inherits its constructor from a base class without an Angular
21207 * decorator.
21208 */
21209 ErrorCode[ErrorCode["DIRECTIVE_INHERITS_UNDECORATED_CTOR"] = 2006] = "DIRECTIVE_INHERITS_UNDECORATED_CTOR";
21210 /**
21211 * Raised when an undecorated class that is using Angular features
21212 * has been discovered.
21213 */
21214 ErrorCode[ErrorCode["UNDECORATED_CLASS_USING_ANGULAR_FEATURES"] = 2007] = "UNDECORATED_CLASS_USING_ANGULAR_FEATURES";
21215 /**
21216 * Raised when an component cannot resolve an external resource, such as a template or a style
21217 * sheet.
21218 */
21219 ErrorCode[ErrorCode["COMPONENT_RESOURCE_NOT_FOUND"] = 2008] = "COMPONENT_RESOURCE_NOT_FOUND";
21220 ErrorCode[ErrorCode["SYMBOL_NOT_EXPORTED"] = 3001] = "SYMBOL_NOT_EXPORTED";
21221 ErrorCode[ErrorCode["SYMBOL_EXPORTED_UNDER_DIFFERENT_NAME"] = 3002] = "SYMBOL_EXPORTED_UNDER_DIFFERENT_NAME";
21222 ErrorCode[ErrorCode["CONFIG_FLAT_MODULE_NO_INDEX"] = 4001] = "CONFIG_FLAT_MODULE_NO_INDEX";
21223 ErrorCode[ErrorCode["CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK"] = 4002] = "CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK";
21224 /**
21225 * Raised when a host expression has a parse error, such as a host listener or host binding
21226 * expression containing a pipe.
21227 */
21228 ErrorCode[ErrorCode["HOST_BINDING_PARSE_ERROR"] = 5001] = "HOST_BINDING_PARSE_ERROR";
21229 /**
21230 * Raised when the compiler cannot parse a component's template.
21231 */
21232 ErrorCode[ErrorCode["TEMPLATE_PARSE_ERROR"] = 5002] = "TEMPLATE_PARSE_ERROR";
21233 /**
21234 * Raised when an NgModule contains an invalid reference in `declarations`.
21235 */
21236 ErrorCode[ErrorCode["NGMODULE_INVALID_DECLARATION"] = 6001] = "NGMODULE_INVALID_DECLARATION";
21237 /**
21238 * Raised when an NgModule contains an invalid type in `imports`.
21239 */
21240 ErrorCode[ErrorCode["NGMODULE_INVALID_IMPORT"] = 6002] = "NGMODULE_INVALID_IMPORT";
21241 /**
21242 * Raised when an NgModule contains an invalid type in `exports`.
21243 */
21244 ErrorCode[ErrorCode["NGMODULE_INVALID_EXPORT"] = 6003] = "NGMODULE_INVALID_EXPORT";
21245 /**
21246 * Raised when an NgModule contains a type in `exports` which is neither in `declarations` nor
21247 * otherwise imported.
21248 */
21249 ErrorCode[ErrorCode["NGMODULE_INVALID_REEXPORT"] = 6004] = "NGMODULE_INVALID_REEXPORT";
21250 /**
21251 * Raised when a `ModuleWithProviders` with a missing
21252 * generic type argument is passed into an `NgModule`.
21253 */
21254 ErrorCode[ErrorCode["NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC"] = 6005] = "NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC";
21255 /**
21256 * Raised when an NgModule exports multiple directives/pipes of the same name and the compiler
21257 * attempts to generate private re-exports within the NgModule file.
21258 */
21259 ErrorCode[ErrorCode["NGMODULE_REEXPORT_NAME_COLLISION"] = 6006] = "NGMODULE_REEXPORT_NAME_COLLISION";
21260 /**
21261 * Raised when a directive/pipe is part of the declarations of two or more NgModules.
21262 */
21263 ErrorCode[ErrorCode["NGMODULE_DECLARATION_NOT_UNIQUE"] = 6007] = "NGMODULE_DECLARATION_NOT_UNIQUE";
21264 /**
21265 * An element name failed validation against the DOM schema.
21266 */
21267 ErrorCode[ErrorCode["SCHEMA_INVALID_ELEMENT"] = 8001] = "SCHEMA_INVALID_ELEMENT";
21268 /**
21269 * An element's attribute name failed validation against the DOM schema.
21270 */
21271 ErrorCode[ErrorCode["SCHEMA_INVALID_ATTRIBUTE"] = 8002] = "SCHEMA_INVALID_ATTRIBUTE";
21272 /**
21273 * No matching directive was found for a `#ref="target"` expression.
21274 */
21275 ErrorCode[ErrorCode["MISSING_REFERENCE_TARGET"] = 8003] = "MISSING_REFERENCE_TARGET";
21276 /**
21277 * No matching pipe was found for a
21278 */
21279 ErrorCode[ErrorCode["MISSING_PIPE"] = 8004] = "MISSING_PIPE";
21280 /**
21281 * The left-hand side of an assignment expression was a template variable. Effectively, the
21282 * template looked like:
21283 *
21284 * ```
21285 * <ng-template let-something>
21286 * <button (click)="something = ...">...</button>
21287 * </ng-template>
21288 * ```
21289 *
21290 * Template variables are read-only.
21291 */
21292 ErrorCode[ErrorCode["WRITE_TO_READ_ONLY_VARIABLE"] = 8005] = "WRITE_TO_READ_ONLY_VARIABLE";
21293 /**
21294 * A template variable was declared twice. For example:
21295 *
21296 * ```html
21297 * <div *ngFor="let i of items; let i = index">
21298 * </div>
21299 * ```
21300 */
21301 ErrorCode[ErrorCode["DUPLICATE_VARIABLE_DECLARATION"] = 8006] = "DUPLICATE_VARIABLE_DECLARATION";
21302 /**
21303 * The template type-checking engine would need to generate an inline type check block for a
21304 * component, but the current type-checking environment doesn't support it.
21305 */
21306 ErrorCode[ErrorCode["INLINE_TCB_REQUIRED"] = 8900] = "INLINE_TCB_REQUIRED";
21307 /**
21308 * The template type-checking engine would need to generate an inline type constructor for a
21309 * directive or component, but the current type-checking environment doesn't support it.
21310 */
21311 ErrorCode[ErrorCode["INLINE_TYPE_CTOR_REQUIRED"] = 8901] = "INLINE_TYPE_CTOR_REQUIRED";
21312 /**
21313 * An injectable already has a `ɵprov` property.
21314 */
21315 ErrorCode[ErrorCode["INJECTABLE_DUPLICATE_PROV"] = 9001] = "INJECTABLE_DUPLICATE_PROV";
21316 // 10XXX error codes are reserved for diagnostics with category
21317 // `ts.DiagnosticCategory.Suggestion`. These diagnostics are generated by
21318 // language service.
21319 /**
21320 * Suggest users to enable `strictTemplates` to make use of full capabilities
21321 * provided by Angular language service.
21322 */
21323 ErrorCode[ErrorCode["SUGGEST_STRICT_TEMPLATES"] = 10001] = "SUGGEST_STRICT_TEMPLATES";
21324 })(ErrorCode || (ErrorCode = {}));
21325 /**
21326 * @internal
21327 * Base URL for the error details page.
21328 * Keep this value in sync with a similar const in
21329 * `packages/core/src/render3/error_code.ts`.
21330 */
21331 const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
21332 /**
21333 * @internal
21334 * Contains a set of error messages that have detailed guides at angular.io.
21335 * Full list of available error guides can be found at https://angular.io/errors
21336 */
21337 const COMPILER_ERRORS_WITH_GUIDES = new Set([
21338 ErrorCode.DECORATOR_ARG_NOT_LITERAL,
21339 ErrorCode.PARAM_MISSING_TOKEN,
21340 ErrorCode.SCHEMA_INVALID_ELEMENT,
21341 ErrorCode.SCHEMA_INVALID_ATTRIBUTE,
21342 ErrorCode.MISSING_REFERENCE_TARGET,
21343 ]);
21344 /**
21345 * @internal
21346 */
21347 function ngErrorCode(code) {
21348 return parseInt('-99' + code);
21349 }
21350
21351 /**
21352 * @license
21353 * Copyright Google LLC All Rights Reserved.
21354 *
21355 * Use of this source code is governed by an MIT-style license that can be
21356 * found in the LICENSE file at https://angular.io/license
21357 */
21358 class FatalDiagnosticError {
21359 constructor(code, node, message, relatedInformation) {
21360 this.code = code;
21361 this.node = node;
21362 this.message = message;
21363 this.relatedInformation = relatedInformation;
21364 /**
21365 * @internal
21366 */
21367 this._isFatalDiagnosticError = true;
21368 }
21369 toDiagnostic() {
21370 return makeDiagnostic(this.code, this.node, this.message, this.relatedInformation);
21371 }
21372 }
21373 function makeDiagnostic(code, node, messageText, relatedInformation) {
21374 node = ts$1.getOriginalNode(node);
21375 return {
21376 category: ts$1.DiagnosticCategory.Error,
21377 code: ngErrorCode(code),
21378 file: ts$1.getOriginalNode(node).getSourceFile(),
21379 start: node.getStart(undefined, false),
21380 length: node.getWidth(),
21381 messageText,
21382 relatedInformation,
21383 };
21384 }
21385 function makeRelatedInformation(node, messageText) {
21386 node = ts$1.getOriginalNode(node);
21387 return {
21388 category: ts$1.DiagnosticCategory.Message,
21389 code: 0,
21390 file: node.getSourceFile(),
21391 start: node.getStart(),
21392 length: node.getWidth(),
21393 messageText,
21394 };
21395 }
21396
21397 /**
21398 * @license
21399 * Copyright Google LLC All Rights Reserved.
21400 *
21401 * Use of this source code is governed by an MIT-style license that can be
21402 * found in the LICENSE file at https://angular.io/license
21403 */
21404 const D_TS = /\.d\.ts$/i;
21405 function isDtsPath(filePath) {
21406 return D_TS.test(filePath);
21407 }
21408 function nodeNameForError(node) {
21409 if (node.name !== undefined && ts$1.isIdentifier(node.name)) {
21410 return node.name.text;
21411 }
21412 else {
21413 const kind = ts$1.SyntaxKind[node.kind];
21414 const { line, character } = ts$1.getLineAndCharacterOfPosition(node.getSourceFile(), node.getStart());
21415 return `${kind}@${line}:${character}`;
21416 }
21417 }
21418 function getSourceFile(node) {
21419 // In certain transformation contexts, `ts.Node.getSourceFile()` can actually return `undefined`,
21420 // despite the type signature not allowing it. In that event, get the `ts.SourceFile` via the
21421 // original node instead (which works).
21422 const directSf = node.getSourceFile();
21423 return directSf !== undefined ? directSf : ts$1.getOriginalNode(node).getSourceFile();
21424 }
21425 function getSourceFileOrNull(program, fileName) {
21426 return program.getSourceFile(fileName) || null;
21427 }
21428 function getTokenAtPosition(sf, pos) {
21429 // getTokenAtPosition is part of TypeScript's private API.
21430 return ts$1.getTokenAtPosition(sf, pos);
21431 }
21432 function identifierOfNode(decl) {
21433 if (decl.name !== undefined && ts$1.isIdentifier(decl.name)) {
21434 return decl.name;
21435 }
21436 else {
21437 return null;
21438 }
21439 }
21440 function isDeclaration(node) {
21441 return isValueDeclaration(node) || isTypeDeclaration(node);
21442 }
21443 function isValueDeclaration(node) {
21444 return ts$1.isClassDeclaration(node) || ts$1.isFunctionDeclaration(node) ||
21445 ts$1.isVariableDeclaration(node);
21446 }
21447 function isTypeDeclaration(node) {
21448 return ts$1.isEnumDeclaration(node) || ts$1.isTypeAliasDeclaration(node) ||
21449 ts$1.isInterfaceDeclaration(node);
21450 }
21451 function isExported(node) {
21452 let topLevel = node;
21453 if (ts$1.isVariableDeclaration(node) && ts$1.isVariableDeclarationList(node.parent)) {
21454 topLevel = node.parent.parent;
21455 }
21456 return topLevel.modifiers !== undefined &&
21457 topLevel.modifiers.some(modifier => modifier.kind === ts$1.SyntaxKind.ExportKeyword);
21458 }
21459 function getRootDirs(host, options) {
21460 const rootDirs = [];
21461 if (options.rootDirs !== undefined) {
21462 rootDirs.push(...options.rootDirs);
21463 }
21464 else if (options.rootDir !== undefined) {
21465 rootDirs.push(options.rootDir);
21466 }
21467 else {
21468 rootDirs.push(host.getCurrentDirectory());
21469 }
21470 // In Windows the above might not always return posix separated paths
21471 // See:
21472 // https://github.com/Microsoft/TypeScript/blob/3f7357d37f66c842d70d835bc925ec2a873ecfec/src/compiler/sys.ts#L650
21473 // Also compiler options might be set via an API which doesn't normalize paths
21474 return rootDirs.map(rootDir => absoluteFrom(host.getCanonicalFileName(rootDir)));
21475 }
21476 function nodeDebugInfo(node) {
21477 const sf = getSourceFile(node);
21478 const { line, character } = ts$1.getLineAndCharacterOfPosition(sf, node.pos);
21479 return `[${sf.fileName}: ${ts$1.SyntaxKind[node.kind]} @ ${line}:${character}]`;
21480 }
21481 /**
21482 * Resolve the specified `moduleName` using the given `compilerOptions` and `compilerHost`.
21483 *
21484 * This helper will attempt to use the `CompilerHost.resolveModuleNames()` method if available.
21485 * Otherwise it will fallback on the `ts.ResolveModuleName()` function.
21486 */
21487 function resolveModuleName(moduleName, containingFile, compilerOptions, compilerHost, moduleResolutionCache) {
21488 if (compilerHost.resolveModuleNames) {
21489 return compilerHost.resolveModuleNames([moduleName], containingFile, undefined, // reusedNames
21490 undefined, // redirectedReference
21491 compilerOptions)[0];
21492 }
21493 else {
21494 return ts$1.resolveModuleName(moduleName, containingFile, compilerOptions, compilerHost, moduleResolutionCache !== null ? moduleResolutionCache : undefined)
21495 .resolvedModule;
21496 }
21497 }
21498 /** Returns true if the node is an assignment expression. */
21499 function isAssignment(node) {
21500 return ts$1.isBinaryExpression(node) && node.operatorToken.kind === ts$1.SyntaxKind.EqualsToken;
21501 }
21502
21503 /**
21504 * @license
21505 * Copyright Google LLC All Rights Reserved.
21506 *
21507 * Use of this source code is governed by an MIT-style license that can be
21508 * found in the LICENSE file at https://angular.io/license
21509 */
21510 /**
21511 * Find the name, if any, by which a node is exported from a given file.
21512 */
21513 function findExportedNameOfNode(target, file, reflector) {
21514 const exports = reflector.getExportsOfModule(file);
21515 if (exports === null) {
21516 return null;
21517 }
21518 // Look for the export which declares the node.
21519 const keys = Array.from(exports.keys());
21520 const name = keys.find(key => {
21521 const decl = exports.get(key);
21522 return decl !== undefined && decl.node === target;
21523 });
21524 if (name === undefined) {
21525 throw new Error(`Failed to find exported name of node (${target.getText()}) in '${file.fileName}'.`);
21526 }
21527 return name;
21528 }
21529
21530 /**
21531 * @license
21532 * Copyright Google LLC All Rights Reserved.
21533 *
21534 * Use of this source code is governed by an MIT-style license that can be
21535 * found in the LICENSE file at https://angular.io/license
21536 */
21537 /**
21538 * Flags which alter the imports generated by the `ReferenceEmitter`.
21539 */
21540 var ImportFlags;
21541 (function (ImportFlags) {
21542 ImportFlags[ImportFlags["None"] = 0] = "None";
21543 /**
21544 * Force the generation of a new import when generating a reference, even if an identifier already
21545 * exists in the target file which could be used instead.
21546 *
21547 * This is sometimes required if there's a risk TypeScript might remove imports during emit.
21548 */
21549 ImportFlags[ImportFlags["ForceNewImport"] = 1] = "ForceNewImport";
21550 /**
21551 * Don't make use of any aliasing information when emitting a reference.
21552 *
21553 * This is sometimes required if emitting into a context where generated references will be fed
21554 * into TypeScript and type-checked (such as in template type-checking).
21555 */
21556 ImportFlags[ImportFlags["NoAliasing"] = 2] = "NoAliasing";
21557 /**
21558 * Indicates that an import to a type-only declaration is allowed.
21559 *
21560 * For references that occur in type-positions, the referred declaration may be a type-only
21561 * declaration that is not retained during emit. Including this flag allows to emit references to
21562 * type-only declarations as used in e.g. template type-checking.
21563 */
21564 ImportFlags[ImportFlags["AllowTypeImports"] = 4] = "AllowTypeImports";
21565 })(ImportFlags || (ImportFlags = {}));
21566 /**
21567 * Generates `Expression`s which refer to `Reference`s in a given context.
21568 *
21569 * A `ReferenceEmitter` uses one or more `ReferenceEmitStrategy` implementations to produce an
21570 * `Expression` which refers to a `Reference` in the context of a particular file.
21571 */
21572 class ReferenceEmitter {
21573 constructor(strategies) {
21574 this.strategies = strategies;
21575 }
21576 emit(ref, context, importFlags = ImportFlags.None) {
21577 for (const strategy of this.strategies) {
21578 const emitted = strategy.emit(ref, context, importFlags);
21579 if (emitted !== null) {
21580 return emitted;
21581 }
21582 }
21583 throw new Error(`Unable to write a reference to ${nodeNameForError(ref.node)} in ${ref.node.getSourceFile().fileName} from ${context.fileName}`);
21584 }
21585 }
21586 /**
21587 * A `ReferenceEmitStrategy` which will refer to declarations by any local `ts.Identifier`s, if
21588 * such identifiers are available.
21589 */
21590 class LocalIdentifierStrategy {
21591 emit(ref, context, importFlags) {
21592 const refSf = getSourceFile(ref.node);
21593 // If the emitter has specified ForceNewImport, then LocalIdentifierStrategy should not use a
21594 // local identifier at all, *except* in the source file where the node is actually declared.
21595 if (importFlags & ImportFlags.ForceNewImport && refSf !== context) {
21596 return null;
21597 }
21598 // If referenced node is not an actual TS declaration (e.g. `class Foo` or `function foo() {}`,
21599 // etc) and it is in the current file then just use it directly.
21600 // This is important because the reference could be a property access (e.g. `exports.foo`). In
21601 // such a case, the reference's `identities` property would be `[foo]`, which would result in an
21602 // invalid emission of a free-standing `foo` identifier, rather than `exports.foo`.
21603 if (!isDeclaration(ref.node) && refSf === context) {
21604 return new WrappedNodeExpr(ref.node);
21605 }
21606 // A Reference can have multiple identities in different files, so it may already have an
21607 // Identifier in the requested context file.
21608 const identifier = ref.getIdentityIn(context);
21609 if (identifier !== null) {
21610 return new WrappedNodeExpr(identifier);
21611 }
21612 else {
21613 return null;
21614 }
21615 }
21616 }
21617 /**
21618 * A `ReferenceEmitStrategy` which will refer to declarations that come from `node_modules` using
21619 * an absolute import.
21620 *
21621 * Part of this strategy involves looking at the target entry point and identifying the exported
21622 * name of the targeted declaration, as it might be different from the declared name (e.g. a
21623 * directive might be declared as FooDirImpl, but exported as FooDir). If no export can be found
21624 * which maps back to the original directive, an error is thrown.
21625 */
21626 class AbsoluteModuleStrategy {
21627 constructor(program, checker, moduleResolver, reflectionHost) {
21628 this.program = program;
21629 this.checker = checker;
21630 this.moduleResolver = moduleResolver;
21631 this.reflectionHost = reflectionHost;
21632 /**
21633 * A cache of the exports of specific modules, because resolving a module to its exports is a
21634 * costly operation.
21635 */
21636 this.moduleExportsCache = new Map();
21637 }
21638 emit(ref, context, importFlags) {
21639 if (ref.bestGuessOwningModule === null) {
21640 // There is no module name available for this Reference, meaning it was arrived at via a
21641 // relative path.
21642 return null;
21643 }
21644 else if (!isDeclaration(ref.node)) {
21645 // It's not possible to import something which isn't a declaration.
21646 throw new Error(`Debug assert: unable to import a Reference to non-declaration of type ${ts$1.SyntaxKind[ref.node.kind]}.`);
21647 }
21648 else if ((importFlags & ImportFlags.AllowTypeImports) === 0 && isTypeDeclaration(ref.node)) {
21649 throw new Error(`Importing a type-only declaration of type ${ts$1.SyntaxKind[ref.node.kind]} in a value position is not allowed.`);
21650 }
21651 // Try to find the exported name of the declaration, if one is available.
21652 const { specifier, resolutionContext } = ref.bestGuessOwningModule;
21653 const symbolName = this.resolveImportName(specifier, ref.node, resolutionContext);
21654 if (symbolName === null) {
21655 // TODO(alxhub): make this error a ts.Diagnostic pointing at whatever caused this import to be
21656 // triggered.
21657 throw new Error(`Symbol ${ref.debugName} declared in ${getSourceFile(ref.node).fileName} is not exported from ${specifier} (import into ${context.fileName})`);
21658 }
21659 return new ExternalExpr(new ExternalReference(specifier, symbolName));
21660 }
21661 resolveImportName(moduleName, target, fromFile) {
21662 const exports = this.getExportsOfModule(moduleName, fromFile);
21663 if (exports !== null && exports.has(target)) {
21664 return exports.get(target);
21665 }
21666 else {
21667 return null;
21668 }
21669 }
21670 getExportsOfModule(moduleName, fromFile) {
21671 if (!this.moduleExportsCache.has(moduleName)) {
21672 this.moduleExportsCache.set(moduleName, this.enumerateExportsOfModule(moduleName, fromFile));
21673 }
21674 return this.moduleExportsCache.get(moduleName);
21675 }
21676 enumerateExportsOfModule(specifier, fromFile) {
21677 // First, resolve the module specifier to its entry point, and get the ts.Symbol for it.
21678 const entryPointFile = this.moduleResolver.resolveModule(specifier, fromFile);
21679 if (entryPointFile === null) {
21680 return null;
21681 }
21682 const exports = this.reflectionHost.getExportsOfModule(entryPointFile);
21683 if (exports === null) {
21684 return null;
21685 }
21686 const exportMap = new Map();
21687 exports.forEach((declaration, name) => {
21688 exportMap.set(declaration.node, name);
21689 });
21690 return exportMap;
21691 }
21692 }
21693 /**
21694 * A `ReferenceEmitStrategy` which will refer to declarations via relative paths, provided they're
21695 * both in the logical project "space" of paths.
21696 *
21697 * This is trickier than it sounds, as the two files may be in different root directories in the
21698 * project. Simply calculating a file system relative path between the two is not sufficient.
21699 * Instead, `LogicalProjectPath`s are used.
21700 */
21701 class LogicalProjectStrategy {
21702 constructor(reflector, logicalFs) {
21703 this.reflector = reflector;
21704 this.logicalFs = logicalFs;
21705 }
21706 emit(ref, context) {
21707 const destSf = getSourceFile(ref.node);
21708 // Compute the relative path from the importing file to the file being imported. This is done
21709 // as a logical path computation, because the two files might be in different rootDirs.
21710 const destPath = this.logicalFs.logicalPathOfSf(destSf);
21711 if (destPath === null) {
21712 // The imported file is not within the logical project filesystem.
21713 return null;
21714 }
21715 const originPath = this.logicalFs.logicalPathOfSf(context);
21716 if (originPath === null) {
21717 throw new Error(`Debug assert: attempt to import from ${context.fileName} but it's outside the program?`);
21718 }
21719 // There's no way to emit a relative reference from a file to itself.
21720 if (destPath === originPath) {
21721 return null;
21722 }
21723 const name = findExportedNameOfNode(ref.node, destSf, this.reflector);
21724 if (name === null) {
21725 // The target declaration isn't exported from the file it's declared in. This is an issue!
21726 return null;
21727 }
21728 // With both files expressed as LogicalProjectPaths, getting the module specifier as a relative
21729 // path is now straightforward.
21730 const moduleName = LogicalProjectPath.relativePathBetween(originPath, destPath);
21731 return new ExternalExpr({ moduleName, name });
21732 }
21733 }
21734 /**
21735 * A `ReferenceEmitStrategy` which constructs relatives paths between `ts.SourceFile`s.
21736 *
21737 * This strategy can be used if there is no `rootDir`/`rootDirs` structure for the project which
21738 * necessitates the stronger logic of `LogicalProjectStrategy`.
21739 */
21740 class RelativePathStrategy {
21741 constructor(reflector) {
21742 this.reflector = reflector;
21743 }
21744 emit(ref, context) {
21745 const destSf = getSourceFile(ref.node);
21746 const relativePath = relative(dirname(absoluteFromSourceFile(context)), absoluteFromSourceFile(destSf));
21747 const moduleName = toRelativeImport(stripExtension(relativePath));
21748 const name = findExportedNameOfNode(ref.node, destSf, this.reflector);
21749 return new ExternalExpr({ moduleName, name });
21750 }
21751 }
21752 /**
21753 * A `ReferenceEmitStrategy` which uses a `UnifiedModulesHost` to generate absolute import
21754 * references.
21755 */
21756 class UnifiedModulesStrategy {
21757 constructor(reflector, unifiedModulesHost) {
21758 this.reflector = reflector;
21759 this.unifiedModulesHost = unifiedModulesHost;
21760 }
21761 emit(ref, context) {
21762 const destSf = getSourceFile(ref.node);
21763 const name = findExportedNameOfNode(ref.node, destSf, this.reflector);
21764 if (name === null) {
21765 return null;
21766 }
21767 const moduleName = this.unifiedModulesHost.fileNameToModuleName(destSf.fileName, context.fileName);
21768 return new ExternalExpr({ moduleName, name });
21769 }
21770 }
21771
21772 /**
21773 * @license
21774 * Copyright Google LLC All Rights Reserved.
21775 *
21776 * Use of this source code is governed by an MIT-style license that can be
21777 * found in the LICENSE file at https://angular.io/license
21778 */
21779 // Escape anything that isn't alphanumeric, '/' or '_'.
21780 const CHARS_TO_ESCAPE = /[^a-zA-Z0-9/_]/g;
21781 /**
21782 * An `AliasingHost` which generates and consumes alias re-exports when module names for each file
21783 * are determined by a `UnifiedModulesHost`.
21784 *
21785 * When using a `UnifiedModulesHost`, aliasing prevents issues with transitive dependencies. See the
21786 * README.md for more details.
21787 */
21788 class UnifiedModulesAliasingHost {
21789 constructor(unifiedModulesHost) {
21790 this.unifiedModulesHost = unifiedModulesHost;
21791 /**
21792 * With a `UnifiedModulesHost`, aliases are chosen automatically without the need to look through
21793 * the exports present in a .d.ts file, so we can avoid cluttering the .d.ts files.
21794 */
21795 this.aliasExportsInDts = false;
21796 }
21797 maybeAliasSymbolAs(ref, context, ngModuleName, isReExport) {
21798 if (!isReExport) {
21799 // Aliasing is used with a UnifiedModulesHost to prevent transitive dependencies. Thus,
21800 // aliases
21801 // only need to be created for directives/pipes which are not direct declarations of an
21802 // NgModule which exports them.
21803 return null;
21804 }
21805 return this.aliasName(ref.node, context);
21806 }
21807 /**
21808 * Generates an `Expression` to import `decl` from `via`, assuming an export was added when `via`
21809 * was compiled per `maybeAliasSymbolAs` above.
21810 */
21811 getAliasIn(decl, via, isReExport) {
21812 if (!isReExport) {
21813 // Directly exported directives/pipes don't require an alias, per the logic in
21814 // `maybeAliasSymbolAs`.
21815 return null;
21816 }
21817 // viaModule is the module it'll actually be imported from.
21818 const moduleName = this.unifiedModulesHost.fileNameToModuleName(via.fileName, via.fileName);
21819 return new ExternalExpr({ moduleName, name: this.aliasName(decl, via) });
21820 }
21821 /**
21822 * Generates an alias name based on the full module name of the file which declares the aliased
21823 * directive/pipe.
21824 */
21825 aliasName(decl, context) {
21826 // The declared module is used to get the name of the alias.
21827 const declModule = this.unifiedModulesHost.fileNameToModuleName(decl.getSourceFile().fileName, context.fileName);
21828 const replaced = declModule.replace(CHARS_TO_ESCAPE, '_').replace(/\//g, '$');
21829 return 'ɵng$' + replaced + '$$' + decl.name.text;
21830 }
21831 }
21832 /**
21833 * An `AliasingHost` which exports directives from any file containing an NgModule in which they're
21834 * declared/exported, under a private symbol name.
21835 *
21836 * These exports support cases where an NgModule is imported deeply from an absolute module path
21837 * (that is, it's not part of an Angular Package Format entrypoint), and the compiler needs to
21838 * import any matched directives/pipes from the same path (to the NgModule file). See README.md for
21839 * more details.
21840 */
21841 class PrivateExportAliasingHost {
21842 constructor(host) {
21843 this.host = host;
21844 /**
21845 * Under private export aliasing, the `AbsoluteModuleStrategy` used for emitting references will
21846 * will select aliased exports that it finds in the .d.ts file for an NgModule's file. Thus,
21847 * emitting these exports in .d.ts is a requirement for the `PrivateExportAliasingHost` to
21848 * function correctly.
21849 */
21850 this.aliasExportsInDts = true;
21851 }
21852 maybeAliasSymbolAs(ref, context, ngModuleName) {
21853 if (ref.hasOwningModuleGuess) {
21854 // Skip nodes that already have an associated absolute module specifier, since they can be
21855 // safely imported from that specifier.
21856 return null;
21857 }
21858 // Look for a user-provided export of `decl` in `context`. If one exists, then an alias export
21859 // is not needed.
21860 // TODO(alxhub): maybe add a host method to check for the existence of an export without going
21861 // through the entire list of exports.
21862 const exports = this.host.getExportsOfModule(context);
21863 if (exports === null) {
21864 // Something went wrong, and no exports were available at all. Bail rather than risk creating
21865 // re-exports when they're not needed.
21866 throw new Error(`Could not determine the exports of: ${context.fileName}`);
21867 }
21868 let found = false;
21869 exports.forEach(value => {
21870 if (value.node === ref.node) {
21871 found = true;
21872 }
21873 });
21874 if (found) {
21875 // The module exports the declared class directly, no alias is necessary.
21876 return null;
21877 }
21878 return `ɵngExportɵ${ngModuleName}ɵ${ref.node.name.text}`;
21879 }
21880 /**
21881 * A `PrivateExportAliasingHost` only generates re-exports and does not direct the compiler to
21882 * directly consume the aliases it creates.
21883 *
21884 * Instead, they're consumed indirectly: `AbsoluteModuleStrategy` `ReferenceEmitterStrategy` will
21885 * select these alias exports automatically when looking for an export of the directive/pipe from
21886 * the same path as the NgModule was imported.
21887 *
21888 * Thus, `getAliasIn` always returns `null`.
21889 */
21890 getAliasIn() {
21891 return null;
21892 }
21893 }
21894 /**
21895 * A `ReferenceEmitStrategy` which will consume the alias attached to a particular `Reference` to a
21896 * directive or pipe, if it exists.
21897 */
21898 class AliasStrategy {
21899 emit(ref, context, importMode) {
21900 if (importMode & ImportFlags.NoAliasing) {
21901 return null;
21902 }
21903 return ref.alias;
21904 }
21905 }
21906
21907 /**
21908 * @license
21909 * Copyright Google LLC All Rights Reserved.
21910 *
21911 * Use of this source code is governed by an MIT-style license that can be
21912 * found in the LICENSE file at https://angular.io/license
21913 */
21914 function relativePathBetween(from, to) {
21915 const relativePath = stripExtension(relative(dirname(resolve(from)), resolve(to)));
21916 return relativePath !== '' ? toRelativeImport(relativePath) : null;
21917 }
21918
21919 /**
21920 * @license
21921 * Copyright Google LLC All Rights Reserved.
21922 *
21923 * Use of this source code is governed by an MIT-style license that can be
21924 * found in the LICENSE file at https://angular.io/license
21925 */
21926 /**
21927 * `ImportRewriter` that does no rewriting.
21928 */
21929 class NoopImportRewriter {
21930 shouldImportSymbol(symbol, specifier) {
21931 return true;
21932 }
21933 rewriteSymbol(symbol, specifier) {
21934 return symbol;
21935 }
21936 rewriteSpecifier(specifier, inContextOfFile) {
21937 return specifier;
21938 }
21939 }
21940 /**
21941 * A mapping of supported symbols that can be imported from within @angular/core, and the names by
21942 * which they're exported from r3_symbols.
21943 */
21944 const CORE_SUPPORTED_SYMBOLS = new Map([
21945 ['ɵɵdefineInjectable', 'ɵɵdefineInjectable'],
21946 ['ɵɵdefineInjector', 'ɵɵdefineInjector'],
21947 ['ɵɵdefineNgModule', 'ɵɵdefineNgModule'],
21948 ['ɵɵsetNgModuleScope', 'ɵɵsetNgModuleScope'],
21949 ['ɵɵinject', 'ɵɵinject'],
21950 ['ɵɵFactoryDef', 'ɵɵFactoryDef'],
21951 ['ɵsetClassMetadata', 'setClassMetadata'],
21952 ['ɵɵInjectableDef', 'ɵɵInjectableDef'],
21953 ['ɵɵInjectorDef', 'ɵɵInjectorDef'],
21954 ['ɵɵNgModuleDefWithMeta', 'ɵɵNgModuleDefWithMeta'],
21955 ['ɵNgModuleFactory', 'NgModuleFactory'],
21956 ['ɵnoSideEffects', 'ɵnoSideEffects'],
21957 ]);
21958 const CORE_MODULE = '@angular/core';
21959 /**
21960 * `ImportRewriter` that rewrites imports from '@angular/core' to be imported from the r3_symbols.ts
21961 * file instead.
21962 */
21963 class R3SymbolsImportRewriter {
21964 constructor(r3SymbolsPath) {
21965 this.r3SymbolsPath = r3SymbolsPath;
21966 }
21967 shouldImportSymbol(symbol, specifier) {
21968 return true;
21969 }
21970 rewriteSymbol(symbol, specifier) {
21971 if (specifier !== CORE_MODULE) {
21972 // This import isn't from core, so ignore it.
21973 return symbol;
21974 }
21975 return validateAndRewriteCoreSymbol(symbol);
21976 }
21977 rewriteSpecifier(specifier, inContextOfFile) {
21978 if (specifier !== CORE_MODULE) {
21979 // This module isn't core, so ignore it.
21980 return specifier;
21981 }
21982 const relativePathToR3Symbols = relativePathBetween(inContextOfFile, this.r3SymbolsPath);
21983 if (relativePathToR3Symbols === null) {
21984 throw new Error(`Failed to rewrite import inside ${CORE_MODULE}: ${inContextOfFile} -> ${this.r3SymbolsPath}`);
21985 }
21986 return relativePathToR3Symbols;
21987 }
21988 }
21989 function validateAndRewriteCoreSymbol(name) {
21990 if (!CORE_SUPPORTED_SYMBOLS.has(name)) {
21991 throw new Error(`Importing unexpected symbol ${name} while compiling ${CORE_MODULE}`);
21992 }
21993 return CORE_SUPPORTED_SYMBOLS.get(name);
21994 }
21995
21996 /**
21997 * @license
21998 * Copyright Google LLC All Rights Reserved.
21999 *
22000 * Use of this source code is governed by an MIT-style license that can be
22001 * found in the LICENSE file at https://angular.io/license
22002 */
22003 /**
22004 * TypeScript has trouble with generating default imports inside of transformers for some module
22005 * formats. The issue is that for the statement:
22006 *
22007 * import X from 'some/module';
22008 * console.log(X);
22009 *
22010 * TypeScript will not use the "X" name in generated code. For normal user code, this is fine
22011 * because references to X will also be renamed. However, if both the import and any references are
22012 * added in a transformer, TypeScript does not associate the two, and will leave the "X" references
22013 * dangling while renaming the import variable. The generated code looks something like:
22014 *
22015 * const module_1 = require('some/module');
22016 * console.log(X); // now X is a dangling reference.
22017 *
22018 * Therefore, we cannot synthetically add default imports, and must reuse the imports that users
22019 * include. Doing this poses a challenge for imports that are only consumed in the type position in
22020 * the user's code. If Angular reuses the imported symbol in a value position (for example, we
22021 * see a constructor parameter of type Foo and try to write "inject(Foo)") we will also end up with
22022 * a dangling reference, as TS will elide the import because it was only used in the type position
22023 * originally.
22024 *
22025 * To avoid this, the compiler must "touch" the imports with `ts.getMutableClone`, and should
22026 * only do this for imports which are actually consumed. The `DefaultImportTracker` keeps track of
22027 * these imports as they're encountered and emitted, and implements a transform which can correctly
22028 * flag the imports as required.
22029 *
22030 * This problem does not exist for non-default imports as the compiler can easily insert
22031 * "import * as X" style imports for those, and the "X" identifier survives transformation.
22032 */
22033 class DefaultImportTracker {
22034 constructor() {
22035 /**
22036 * A `Map` which tracks the `Map` of default import `ts.Identifier`s to their
22037 * `ts.ImportDeclaration`s. These declarations are not guaranteed to be used.
22038 */
22039 this.sourceFileToImportMap = new Map();
22040 /**
22041 * A `Map` which tracks the `Set` of `ts.ImportDeclaration`s for default imports that were used in
22042 * a given `ts.SourceFile` and need to be preserved.
22043 */
22044 this.sourceFileToUsedImports = new Map();
22045 }
22046 recordImportedIdentifier(id, decl) {
22047 const sf = getSourceFile(id);
22048 if (!this.sourceFileToImportMap.has(sf)) {
22049 this.sourceFileToImportMap.set(sf, new Map());
22050 }
22051 this.sourceFileToImportMap.get(sf).set(id, decl);
22052 }
22053 recordUsedIdentifier(id) {
22054 const sf = getSourceFile(id);
22055 if (!this.sourceFileToImportMap.has(sf)) {
22056 // The identifier's source file has no registered default imports at all.
22057 return;
22058 }
22059 const identiferToDeclaration = this.sourceFileToImportMap.get(sf);
22060 if (!identiferToDeclaration.has(id)) {
22061 // The identifier isn't from a registered default import.
22062 return;
22063 }
22064 const decl = identiferToDeclaration.get(id);
22065 // Add the default import declaration to the set of used import declarations for the file.
22066 if (!this.sourceFileToUsedImports.has(sf)) {
22067 this.sourceFileToUsedImports.set(sf, new Set());
22068 }
22069 this.sourceFileToUsedImports.get(sf).add(decl);
22070 }
22071 /**
22072 * Get a `ts.TransformerFactory` which will preserve default imports that were previously marked
22073 * as used.
22074 *
22075 * This transformer must run after any other transformers which call `recordUsedIdentifier`.
22076 */
22077 importPreservingTransformer() {
22078 return (context) => {
22079 return (sf) => {
22080 return this.transformSourceFile(sf);
22081 };
22082 };
22083 }
22084 /**
22085 * Process a `ts.SourceFile` and replace any `ts.ImportDeclaration`s.
22086 */
22087 transformSourceFile(sf) {
22088 const originalSf = ts$1.getOriginalNode(sf);
22089 // Take a fast path if no import declarations need to be preserved in the file.
22090 if (!this.sourceFileToUsedImports.has(originalSf)) {
22091 return sf;
22092 }
22093 // There are declarations that need to be preserved.
22094 const importsToPreserve = this.sourceFileToUsedImports.get(originalSf);
22095 // Generate a new statement list which preserves any imports present in `importsToPreserve`.
22096 const statements = sf.statements.map(stmt => {
22097 if (ts$1.isImportDeclaration(stmt) && importsToPreserve.has(stmt)) {
22098 // Preserving an import that's marked as unreferenced (type-only) is tricky in TypeScript.
22099 //
22100 // Various approaches have been tried, with mixed success:
22101 //
22102 // 1. Using `ts.updateImportDeclaration` does not cause the import to be retained.
22103 //
22104 // 2. Using `ts.createImportDeclaration` with the same `ts.ImportClause` causes the import
22105 // to correctly be retained, but when emitting CommonJS module format code, references
22106 // to the imported value will not match the import variable.
22107 //
22108 // 3. Emitting "import * as" imports instead generates the correct import variable, but
22109 // references are missing the ".default" access. This happens to work for tsickle code
22110 // with goog.module transformations as tsickle strips the ".default" anyway.
22111 //
22112 // 4. It's possible to trick TypeScript by setting `ts.NodeFlag.Synthesized` on the import
22113 // declaration. This causes the import to be correctly retained and generated, but can
22114 // violate invariants elsewhere in the compiler and cause crashes.
22115 //
22116 // 5. Using `ts.getMutableClone` seems to correctly preserve the import and correctly
22117 // generate references to the import variable across all module types.
22118 //
22119 // Therefore, option 5 is the one used here. It seems to be implemented as the correct way
22120 // to perform option 4, which preserves all the compiler's invariants.
22121 //
22122 // TODO(alxhub): discuss with the TypeScript team and determine if there's a better way to
22123 // deal with this issue.
22124 stmt = ts$1.getMutableClone(stmt);
22125 }
22126 return stmt;
22127 });
22128 // Save memory - there's no need to keep these around once the transform has run for the given
22129 // file.
22130 this.sourceFileToImportMap.delete(originalSf);
22131 this.sourceFileToUsedImports.delete(originalSf);
22132 return ts$1.updateSourceFileNode(sf, statements);
22133 }
22134 }
22135
22136 /**
22137 * @license
22138 * Copyright Google LLC All Rights Reserved.
22139 *
22140 * Use of this source code is governed by an MIT-style license that can be
22141 * found in the LICENSE file at https://angular.io/license
22142 */
22143 /**
22144 * A `ts.Node` plus the context in which it was discovered.
22145 *
22146 * A `Reference` is a pointer to a `ts.Node` that was extracted from the program somehow. It
22147 * contains not only the node itself, but the information regarding how the node was located. In
22148 * particular, it might track different identifiers by which the node is exposed, as well as
22149 * potentially a module specifier which might expose the node.
22150 *
22151 * The Angular compiler uses `Reference`s instead of `ts.Node`s when tracking classes or generating
22152 * imports.
22153 */
22154 class Reference$1 {
22155 constructor(node, bestGuessOwningModule = null) {
22156 this.node = node;
22157 this.identifiers = [];
22158 /**
22159 * Indicates that the Reference was created synthetically, not as a result of natural value
22160 * resolution.
22161 *
22162 * This is used to avoid misinterpreting the Reference in certain contexts.
22163 */
22164 this.synthetic = false;
22165 this._alias = null;
22166 this.bestGuessOwningModule = bestGuessOwningModule;
22167 const id = identifierOfNode(node);
22168 if (id !== null) {
22169 this.identifiers.push(id);
22170 }
22171 }
22172 /**
22173 * The best guess at which module specifier owns this particular reference, or `null` if there
22174 * isn't one.
22175 */
22176 get ownedByModuleGuess() {
22177 if (this.bestGuessOwningModule !== null) {
22178 return this.bestGuessOwningModule.specifier;
22179 }
22180 else {
22181 return null;
22182 }
22183 }
22184 /**
22185 * Whether this reference has a potential owning module or not.
22186 *
22187 * See `bestGuessOwningModule`.
22188 */
22189 get hasOwningModuleGuess() {
22190 return this.bestGuessOwningModule !== null;
22191 }
22192 /**
22193 * A name for the node, if one is available.
22194 *
22195 * This is only suited for debugging. Any actual references to this node should be made with
22196 * `ts.Identifier`s (see `getIdentityIn`).
22197 */
22198 get debugName() {
22199 const id = identifierOfNode(this.node);
22200 return id !== null ? id.text : null;
22201 }
22202 get alias() {
22203 return this._alias;
22204 }
22205 /**
22206 * Record a `ts.Identifier` by which it's valid to refer to this node, within the context of this
22207 * `Reference`.
22208 */
22209 addIdentifier(identifier) {
22210 this.identifiers.push(identifier);
22211 }
22212 /**
22213 * Get a `ts.Identifier` within this `Reference` that can be used to refer within the context of a
22214 * given `ts.SourceFile`, if any.
22215 */
22216 getIdentityIn(context) {
22217 return this.identifiers.find(id => id.getSourceFile() === context) || null;
22218 }
22219 /**
22220 * Get a `ts.Identifier` for this `Reference` that exists within the given expression.
22221 *
22222 * This is very useful for producing `ts.Diagnostic`s that reference `Reference`s that were
22223 * extracted from some larger expression, as it can be used to pinpoint the `ts.Identifier` within
22224 * the expression from which the `Reference` originated.
22225 */
22226 getIdentityInExpression(expr) {
22227 const sf = expr.getSourceFile();
22228 return this.identifiers.find(id => {
22229 if (id.getSourceFile() !== sf) {
22230 return false;
22231 }
22232 // This identifier is a match if its position lies within the given expression.
22233 return id.pos >= expr.pos && id.end <= expr.end;
22234 }) ||
22235 null;
22236 }
22237 /**
22238 * Given the 'container' expression from which this `Reference` was extracted, produce a
22239 * `ts.Expression` to use in a diagnostic which best indicates the position within the container
22240 * expression that generated the `Reference`.
22241 *
22242 * For example, given a `Reference` to the class 'Bar' and the containing expression:
22243 * `[Foo, Bar, Baz]`, this function would attempt to return the `ts.Identifier` for `Bar` within
22244 * the array. This could be used to produce a nice diagnostic context:
22245 *
22246 * ```text
22247 * [Foo, Bar, Baz]
22248 * ~~~
22249 * ```
22250 *
22251 * If no specific node can be found, then the `fallback` expression is used, which defaults to the
22252 * entire containing expression.
22253 */
22254 getOriginForDiagnostics(container, fallback = container) {
22255 const id = this.getIdentityInExpression(container);
22256 return id !== null ? id : fallback;
22257 }
22258 cloneWithAlias(alias) {
22259 const ref = new Reference$1(this.node, this.bestGuessOwningModule);
22260 ref.identifiers = [...this.identifiers];
22261 ref._alias = alias;
22262 return ref;
22263 }
22264 cloneWithNoIdentifiers() {
22265 const ref = new Reference$1(this.node, this.bestGuessOwningModule);
22266 ref._alias = this._alias;
22267 ref.identifiers = [];
22268 return ref;
22269 }
22270 }
22271
22272 /**
22273 * Used by `RouterEntryPointManager` and `NgModuleRouteAnalyzer` (which is in turn is used by
22274 * `NgModuleDecoratorHandler`) for resolving the module source-files references in lazy-loaded
22275 * routes (relative to the source-file containing the `NgModule` that provides the route
22276 * definitions).
22277 */
22278 class ModuleResolver {
22279 constructor(program, compilerOptions, host, moduleResolutionCache) {
22280 this.program = program;
22281 this.compilerOptions = compilerOptions;
22282 this.host = host;
22283 this.moduleResolutionCache = moduleResolutionCache;
22284 }
22285 resolveModule(moduleName, containingFile) {
22286 const resolved = resolveModuleName(moduleName, containingFile, this.compilerOptions, this.host, this.moduleResolutionCache);
22287 if (resolved === undefined) {
22288 return null;
22289 }
22290 return getSourceFileOrNull(this.program, absoluteFrom(resolved.resolvedFileName));
22291 }
22292 }
22293
22294 /**
22295 * @license
22296 * Copyright Google LLC All Rights Reserved.
22297 *
22298 * Use of this source code is governed by an MIT-style license that can be
22299 * found in the LICENSE file at https://angular.io/license
22300 */
22301 const Decorator = {
22302 nodeForError: (decorator) => {
22303 if (decorator.node !== null) {
22304 return decorator.node;
22305 }
22306 else {
22307 // TODO(alxhub): we can't rely on narrowing until TS 3.6 is in g3.
22308 return decorator.synthesizedFor;
22309 }
22310 },
22311 };
22312 function isDecoratorIdentifier(exp) {
22313 return ts$1.isIdentifier(exp) ||
22314 ts$1.isPropertyAccessExpression(exp) && ts$1.isIdentifier(exp.expression) &&
22315 ts$1.isIdentifier(exp.name);
22316 }
22317 /**
22318 * An enumeration of possible kinds of class members.
22319 */
22320 var ClassMemberKind;
22321 (function (ClassMemberKind) {
22322 ClassMemberKind[ClassMemberKind["Constructor"] = 0] = "Constructor";
22323 ClassMemberKind[ClassMemberKind["Getter"] = 1] = "Getter";
22324 ClassMemberKind[ClassMemberKind["Setter"] = 2] = "Setter";
22325 ClassMemberKind[ClassMemberKind["Property"] = 3] = "Property";
22326 ClassMemberKind[ClassMemberKind["Method"] = 4] = "Method";
22327 })(ClassMemberKind || (ClassMemberKind = {}));
22328 /**
22329 * Possible declarations of known values, such as built-in objects/functions or TypeScript helpers.
22330 */
22331 var KnownDeclaration;
22332 (function (KnownDeclaration) {
22333 /**
22334 * Indicates the JavaScript global `Object` class.
22335 */
22336 KnownDeclaration[KnownDeclaration["JsGlobalObject"] = 0] = "JsGlobalObject";
22337 /**
22338 * Indicates the `__assign` TypeScript helper function.
22339 */
22340 KnownDeclaration[KnownDeclaration["TsHelperAssign"] = 1] = "TsHelperAssign";
22341 /**
22342 * Indicates the `__spread` TypeScript helper function.
22343 */
22344 KnownDeclaration[KnownDeclaration["TsHelperSpread"] = 2] = "TsHelperSpread";
22345 /**
22346 * Indicates the `__spreadArrays` TypeScript helper function.
22347 */
22348 KnownDeclaration[KnownDeclaration["TsHelperSpreadArrays"] = 3] = "TsHelperSpreadArrays";
22349 })(KnownDeclaration || (KnownDeclaration = {}));
22350 /**
22351 * Returns true if the `decl` is a `ConcreteDeclaration` (ie. that its `node` property is a
22352 * `ts.Declaration`).
22353 */
22354 function isConcreteDeclaration(decl) {
22355 return decl.kind === 0 /* Concrete */;
22356 }
22357
22358 /**
22359 * @license
22360 * Copyright Google LLC All Rights Reserved.
22361 *
22362 * Use of this source code is governed by an MIT-style license that can be
22363 * found in the LICENSE file at https://angular.io/license
22364 */
22365 /**
22366 * Potentially convert a `ts.TypeNode` to a `TypeValueReference`, which indicates how to use the
22367 * type given in the `ts.TypeNode` in a value position.
22368 *
22369 * This can return `null` if the `typeNode` is `null`, if it does not refer to a symbol with a value
22370 * declaration, or if it is not possible to statically understand.
22371 */
22372 function typeToValue(typeNode, checker) {
22373 // It's not possible to get a value expression if the parameter doesn't even have a type.
22374 if (typeNode === null) {
22375 return missingType();
22376 }
22377 if (!ts$1.isTypeReferenceNode(typeNode)) {
22378 return unsupportedType(typeNode);
22379 }
22380 const symbols = resolveTypeSymbols(typeNode, checker);
22381 if (symbols === null) {
22382 return unknownReference(typeNode);
22383 }
22384 const { local, decl } = symbols;
22385 // It's only valid to convert a type reference to a value reference if the type actually
22386 // has a value declaration associated with it. Note that const enums are an exception,
22387 // because while they do have a value declaration, they don't exist at runtime.
22388 if (decl.valueDeclaration === undefined || decl.flags & ts$1.SymbolFlags.ConstEnum) {
22389 let typeOnlyDecl = null;
22390 if (decl.declarations !== undefined && decl.declarations.length > 0) {
22391 typeOnlyDecl = decl.declarations[0];
22392 }
22393 return noValueDeclaration(typeNode, typeOnlyDecl);
22394 }
22395 // The type points to a valid value declaration. Rewrite the TypeReference into an
22396 // Expression which references the value pointed to by the TypeReference, if possible.
22397 // Look at the local `ts.Symbol`'s declarations and see if it comes from an import
22398 // statement. If so, extract the module specifier and the name of the imported type.
22399 const firstDecl = local.declarations && local.declarations[0];
22400 if (firstDecl !== undefined) {
22401 if (ts$1.isImportClause(firstDecl) && firstDecl.name !== undefined) {
22402 // This is a default import.
22403 // import Foo from 'foo';
22404 if (firstDecl.isTypeOnly) {
22405 // Type-only imports cannot be represented as value.
22406 return typeOnlyImport(typeNode, firstDecl);
22407 }
22408 return {
22409 kind: 0 /* LOCAL */,
22410 expression: firstDecl.name,
22411 defaultImportStatement: firstDecl.parent,
22412 };
22413 }
22414 else if (ts$1.isImportSpecifier(firstDecl)) {
22415 // The symbol was imported by name
22416 // import {Foo} from 'foo';
22417 // or
22418 // import {Foo as Bar} from 'foo';
22419 if (firstDecl.parent.parent.isTypeOnly) {
22420 // Type-only imports cannot be represented as value.
22421 return typeOnlyImport(typeNode, firstDecl.parent.parent);
22422 }
22423 // Determine the name to import (`Foo`) from the import specifier, as the symbol names of
22424 // the imported type could refer to a local alias (like `Bar` in the example above).
22425 const importedName = (firstDecl.propertyName || firstDecl.name).text;
22426 // The first symbol name refers to the local name, which is replaced by `importedName` above.
22427 // Any remaining symbol names make up the complete path to the value.
22428 const [_localName, ...nestedPath] = symbols.symbolNames;
22429 const moduleName = extractModuleName(firstDecl.parent.parent.parent);
22430 return {
22431 kind: 1 /* IMPORTED */,
22432 valueDeclaration: decl.valueDeclaration,
22433 moduleName,
22434 importedName,
22435 nestedPath
22436 };
22437 }
22438 else if (ts$1.isNamespaceImport(firstDecl)) {
22439 // The import is a namespace import
22440 // import * as Foo from 'foo';
22441 if (firstDecl.parent.isTypeOnly) {
22442 // Type-only imports cannot be represented as value.
22443 return typeOnlyImport(typeNode, firstDecl.parent);
22444 }
22445 if (symbols.symbolNames.length === 1) {
22446 // The type refers to the namespace itself, which cannot be represented as a value.
22447 return namespaceImport(typeNode, firstDecl.parent);
22448 }
22449 // The first symbol name refers to the local name of the namespace, which is is discarded
22450 // as a new namespace import will be generated. This is followed by the symbol name that needs
22451 // to be imported and any remaining names that constitute the complete path to the value.
22452 const [_ns, importedName, ...nestedPath] = symbols.symbolNames;
22453 const moduleName = extractModuleName(firstDecl.parent.parent);
22454 return {
22455 kind: 1 /* IMPORTED */,
22456 valueDeclaration: decl.valueDeclaration,
22457 moduleName,
22458 importedName,
22459 nestedPath
22460 };
22461 }
22462 }
22463 // If the type is not imported, the type reference can be converted into an expression as is.
22464 const expression = typeNodeToValueExpr(typeNode);
22465 if (expression !== null) {
22466 return {
22467 kind: 0 /* LOCAL */,
22468 expression,
22469 defaultImportStatement: null,
22470 };
22471 }
22472 else {
22473 return unsupportedType(typeNode);
22474 }
22475 }
22476 function unsupportedType(typeNode) {
22477 return {
22478 kind: 2 /* UNAVAILABLE */,
22479 reason: { kind: 5 /* UNSUPPORTED */, typeNode },
22480 };
22481 }
22482 function noValueDeclaration(typeNode, decl) {
22483 return {
22484 kind: 2 /* UNAVAILABLE */,
22485 reason: { kind: 1 /* NO_VALUE_DECLARATION */, typeNode, decl },
22486 };
22487 }
22488 function typeOnlyImport(typeNode, importClause) {
22489 return {
22490 kind: 2 /* UNAVAILABLE */,
22491 reason: { kind: 2 /* TYPE_ONLY_IMPORT */, typeNode, importClause },
22492 };
22493 }
22494 function unknownReference(typeNode) {
22495 return {
22496 kind: 2 /* UNAVAILABLE */,
22497 reason: { kind: 3 /* UNKNOWN_REFERENCE */, typeNode },
22498 };
22499 }
22500 function namespaceImport(typeNode, importClause) {
22501 return {
22502 kind: 2 /* UNAVAILABLE */,
22503 reason: { kind: 4 /* NAMESPACE */, typeNode, importClause },
22504 };
22505 }
22506 function missingType() {
22507 return {
22508 kind: 2 /* UNAVAILABLE */,
22509 reason: { kind: 0 /* MISSING_TYPE */ },
22510 };
22511 }
22512 /**
22513 * Attempt to extract a `ts.Expression` that's equivalent to a `ts.TypeNode`, as the two have
22514 * different AST shapes but can reference the same symbols.
22515 *
22516 * This will return `null` if an equivalent expression cannot be constructed.
22517 */
22518 function typeNodeToValueExpr(node) {
22519 if (ts$1.isTypeReferenceNode(node)) {
22520 return entityNameToValue(node.typeName);
22521 }
22522 else {
22523 return null;
22524 }
22525 }
22526 /**
22527 * Resolve a `TypeReference` node to the `ts.Symbol`s for both its declaration and its local source.
22528 *
22529 * In the event that the `TypeReference` refers to a locally declared symbol, these will be the
22530 * same. If the `TypeReference` refers to an imported symbol, then `decl` will be the fully resolved
22531 * `ts.Symbol` of the referenced symbol. `local` will be the `ts.Symbol` of the `ts.Identifier`
22532 * which points to the import statement by which the symbol was imported.
22533 *
22534 * All symbol names that make up the type reference are returned left-to-right into the
22535 * `symbolNames` array, which is guaranteed to include at least one entry.
22536 */
22537 function resolveTypeSymbols(typeRef, checker) {
22538 const typeName = typeRef.typeName;
22539 // typeRefSymbol is the ts.Symbol of the entire type reference.
22540 const typeRefSymbol = checker.getSymbolAtLocation(typeName);
22541 if (typeRefSymbol === undefined) {
22542 return null;
22543 }
22544 // `local` is the `ts.Symbol` for the local `ts.Identifier` for the type.
22545 // If the type is actually locally declared or is imported by name, for example:
22546 // import {Foo} from './foo';
22547 // then it'll be the same as `typeRefSymbol`.
22548 //
22549 // If the type is imported via a namespace import, for example:
22550 // import * as foo from './foo';
22551 // and then referenced as:
22552 // constructor(f: foo.Foo)
22553 // then `local` will be the `ts.Symbol` of `foo`, whereas `typeRefSymbol` will be the `ts.Symbol`
22554 // of `foo.Foo`. This allows tracking of the import behind whatever type reference exists.
22555 let local = typeRefSymbol;
22556 // Destructure a name like `foo.X.Y.Z` as follows:
22557 // - in `leftMost`, the `ts.Identifier` of the left-most name (`foo`) in the qualified name.
22558 // This identifier is used to resolve the `ts.Symbol` for `local`.
22559 // - in `symbolNames`, all names involved in the qualified path, or a single symbol name if the
22560 // type is not qualified.
22561 let leftMost = typeName;
22562 const symbolNames = [];
22563 while (ts$1.isQualifiedName(leftMost)) {
22564 symbolNames.unshift(leftMost.right.text);
22565 leftMost = leftMost.left;
22566 }
22567 symbolNames.unshift(leftMost.text);
22568 if (leftMost !== typeName) {
22569 const localTmp = checker.getSymbolAtLocation(leftMost);
22570 if (localTmp !== undefined) {
22571 local = localTmp;
22572 }
22573 }
22574 // De-alias the top-level type reference symbol to get the symbol of the actual declaration.
22575 let decl = typeRefSymbol;
22576 if (typeRefSymbol.flags & ts$1.SymbolFlags.Alias) {
22577 decl = checker.getAliasedSymbol(typeRefSymbol);
22578 }
22579 return { local, decl, symbolNames };
22580 }
22581 function entityNameToValue(node) {
22582 if (ts$1.isQualifiedName(node)) {
22583 const left = entityNameToValue(node.left);
22584 return left !== null ? ts$1.createPropertyAccess(left, node.right) : null;
22585 }
22586 else if (ts$1.isIdentifier(node)) {
22587 return ts$1.getMutableClone(node);
22588 }
22589 else {
22590 return null;
22591 }
22592 }
22593 function extractModuleName(node) {
22594 if (!ts$1.isStringLiteral(node.moduleSpecifier)) {
22595 throw new Error('not a module specifier');
22596 }
22597 return node.moduleSpecifier.text;
22598 }
22599
22600 /**
22601 * @license
22602 * Copyright Google LLC All Rights Reserved.
22603 *
22604 * Use of this source code is governed by an MIT-style license that can be
22605 * found in the LICENSE file at https://angular.io/license
22606 */
22607 function isNamedClassDeclaration(node) {
22608 return ts$1.isClassDeclaration(node) && isIdentifier$1(node.name);
22609 }
22610 function isIdentifier$1(node) {
22611 return node !== undefined && ts$1.isIdentifier(node);
22612 }
22613
22614 /**
22615 * @license
22616 * Copyright Google LLC All Rights Reserved.
22617 *
22618 * Use of this source code is governed by an MIT-style license that can be
22619 * found in the LICENSE file at https://angular.io/license
22620 */
22621 /**
22622 * reflector.ts implements static reflection of declarations using the TypeScript `ts.TypeChecker`.
22623 */
22624 class TypeScriptReflectionHost {
22625 constructor(checker) {
22626 this.checker = checker;
22627 }
22628 getDecoratorsOfDeclaration(declaration) {
22629 if (declaration.decorators === undefined || declaration.decorators.length === 0) {
22630 return null;
22631 }
22632 return declaration.decorators.map(decorator => this._reflectDecorator(decorator))
22633 .filter((dec) => dec !== null);
22634 }
22635 getMembersOfClass(clazz) {
22636 const tsClazz = castDeclarationToClassOrDie(clazz);
22637 return tsClazz.members.map(member => this._reflectMember(member))
22638 .filter((member) => member !== null);
22639 }
22640 getConstructorParameters(clazz) {
22641 const tsClazz = castDeclarationToClassOrDie(clazz);
22642 const isDeclaration = tsClazz.getSourceFile().isDeclarationFile;
22643 // For non-declaration files, we want to find the constructor with a `body`. The constructors
22644 // without a `body` are overloads whereas we want the implementation since it's the one that'll
22645 // be executed and which can have decorators. For declaration files, we take the first one that
22646 // we get.
22647 const ctor = tsClazz.members.find((member) => ts$1.isConstructorDeclaration(member) && (isDeclaration || member.body !== undefined));
22648 if (ctor === undefined) {
22649 return null;
22650 }
22651 return ctor.parameters.map(node => {
22652 // The name of the parameter is easy.
22653 const name = parameterName(node.name);
22654 const decorators = this.getDecoratorsOfDeclaration(node);
22655 // It may or may not be possible to write an expression that refers to the value side of the
22656 // type named for the parameter.
22657 let originalTypeNode = node.type || null;
22658 let typeNode = originalTypeNode;
22659 // Check if we are dealing with a simple nullable union type e.g. `foo: Foo|null`
22660 // and extract the type. More complex union types e.g. `foo: Foo|Bar` are not supported.
22661 // We also don't need to support `foo: Foo|undefined` because Angular's DI injects `null` for
22662 // optional tokes that don't have providers.
22663 if (typeNode && ts$1.isUnionTypeNode(typeNode)) {
22664 let childTypeNodes = typeNode.types.filter(childTypeNode => !(ts$1.isLiteralTypeNode(childTypeNode) &&
22665 childTypeNode.literal.kind === ts$1.SyntaxKind.NullKeyword));
22666 if (childTypeNodes.length === 1) {
22667 typeNode = childTypeNodes[0];
22668 }
22669 }
22670 const typeValueReference = typeToValue(typeNode, this.checker);
22671 return {
22672 name,
22673 nameNode: node.name,
22674 typeValueReference,
22675 typeNode: originalTypeNode,
22676 decorators,
22677 };
22678 });
22679 }
22680 getImportOfIdentifier(id) {
22681 const directImport = this.getDirectImportOfIdentifier(id);
22682 if (directImport !== null) {
22683 return directImport;
22684 }
22685 else if (ts$1.isQualifiedName(id.parent) && id.parent.right === id) {
22686 return this.getImportOfNamespacedIdentifier(id, getQualifiedNameRoot(id.parent));
22687 }
22688 else if (ts$1.isPropertyAccessExpression(id.parent) && id.parent.name === id) {
22689 return this.getImportOfNamespacedIdentifier(id, getFarLeftIdentifier(id.parent));
22690 }
22691 else {
22692 return null;
22693 }
22694 }
22695 getExportsOfModule(node) {
22696 // In TypeScript code, modules are only ts.SourceFiles. Throw if the node isn't a module.
22697 if (!ts$1.isSourceFile(node)) {
22698 throw new Error(`getExportsOfModule() called on non-SourceFile in TS code`);
22699 }
22700 // Reflect the module to a Symbol, and use getExportsOfModule() to get a list of exported
22701 // Symbols.
22702 const symbol = this.checker.getSymbolAtLocation(node);
22703 if (symbol === undefined) {
22704 return null;
22705 }
22706 const map = new Map();
22707 this.checker.getExportsOfModule(symbol).forEach(exportSymbol => {
22708 // Map each exported Symbol to a Declaration and add it to the map.
22709 const decl = this.getDeclarationOfSymbol(exportSymbol, null);
22710 if (decl !== null) {
22711 map.set(exportSymbol.name, decl);
22712 }
22713 });
22714 return map;
22715 }
22716 isClass(node) {
22717 // For our purposes, classes are "named" ts.ClassDeclarations;
22718 // (`node.name` can be undefined in unnamed default exports: `default export class { ... }`).
22719 return isNamedClassDeclaration(node);
22720 }
22721 hasBaseClass(clazz) {
22722 return this.getBaseClassExpression(clazz) !== null;
22723 }
22724 getBaseClassExpression(clazz) {
22725 if (!(ts$1.isClassDeclaration(clazz) || ts$1.isClassExpression(clazz)) ||
22726 clazz.heritageClauses === undefined) {
22727 return null;
22728 }
22729 const extendsClause = clazz.heritageClauses.find(clause => clause.token === ts$1.SyntaxKind.ExtendsKeyword);
22730 if (extendsClause === undefined) {
22731 return null;
22732 }
22733 const extendsType = extendsClause.types[0];
22734 if (extendsType === undefined) {
22735 return null;
22736 }
22737 return extendsType.expression;
22738 }
22739 getDeclarationOfIdentifier(id) {
22740 // Resolve the identifier to a Symbol, and return the declaration of that.
22741 let symbol = this.checker.getSymbolAtLocation(id);
22742 if (symbol === undefined) {
22743 return null;
22744 }
22745 return this.getDeclarationOfSymbol(symbol, id);
22746 }
22747 getDefinitionOfFunction(node) {
22748 if (!ts$1.isFunctionDeclaration(node) && !ts$1.isMethodDeclaration(node) &&
22749 !ts$1.isFunctionExpression(node)) {
22750 return null;
22751 }
22752 return {
22753 node,
22754 body: node.body !== undefined ? Array.from(node.body.statements) : null,
22755 parameters: node.parameters.map(param => {
22756 const name = parameterName(param.name);
22757 const initializer = param.initializer || null;
22758 return { name, node: param, initializer };
22759 }),
22760 };
22761 }
22762 getGenericArityOfClass(clazz) {
22763 if (!ts$1.isClassDeclaration(clazz)) {
22764 return null;
22765 }
22766 return clazz.typeParameters !== undefined ? clazz.typeParameters.length : 0;
22767 }
22768 getVariableValue(declaration) {
22769 return declaration.initializer || null;
22770 }
22771 getDtsDeclaration(_) {
22772 return null;
22773 }
22774 getInternalNameOfClass(clazz) {
22775 return clazz.name;
22776 }
22777 getAdjacentNameOfClass(clazz) {
22778 return clazz.name;
22779 }
22780 getDirectImportOfIdentifier(id) {
22781 const symbol = this.checker.getSymbolAtLocation(id);
22782 if (symbol === undefined || symbol.declarations === undefined ||
22783 symbol.declarations.length !== 1) {
22784 return null;
22785 }
22786 const decl = symbol.declarations[0];
22787 const importDecl = getContainingImportDeclaration(decl);
22788 // Ignore declarations that are defined locally (not imported).
22789 if (importDecl === null) {
22790 return null;
22791 }
22792 // The module specifier is guaranteed to be a string literal, so this should always pass.
22793 if (!ts$1.isStringLiteral(importDecl.moduleSpecifier)) {
22794 // Not allowed to happen in TypeScript ASTs.
22795 return null;
22796 }
22797 return { from: importDecl.moduleSpecifier.text, name: getExportedName(decl, id) };
22798 }
22799 /**
22800 * Try to get the import info for this identifier as though it is a namespaced import.
22801 *
22802 * For example, if the identifier is the `Directive` part of a qualified type chain like:
22803 *
22804 * ```
22805 * core.Directive
22806 * ```
22807 *
22808 * then it might be that `core` is a namespace import such as:
22809 *
22810 * ```
22811 * import * as core from 'tslib';
22812 * ```
22813 *
22814 * @param id the TypeScript identifier to find the import info for.
22815 * @returns The import info if this is a namespaced import or `null`.
22816 */
22817 getImportOfNamespacedIdentifier(id, namespaceIdentifier) {
22818 if (namespaceIdentifier === null) {
22819 return null;
22820 }
22821 const namespaceSymbol = this.checker.getSymbolAtLocation(namespaceIdentifier);
22822 if (!namespaceSymbol) {
22823 return null;
22824 }
22825 const declaration = namespaceSymbol.declarations.length === 1 ? namespaceSymbol.declarations[0] : null;
22826 if (!declaration) {
22827 return null;
22828 }
22829 const namespaceDeclaration = ts$1.isNamespaceImport(declaration) ? declaration : null;
22830 if (!namespaceDeclaration) {
22831 return null;
22832 }
22833 const importDeclaration = namespaceDeclaration.parent.parent;
22834 if (!ts$1.isStringLiteral(importDeclaration.moduleSpecifier)) {
22835 // Should not happen as this would be invalid TypesScript
22836 return null;
22837 }
22838 return {
22839 from: importDeclaration.moduleSpecifier.text,
22840 name: id.text,
22841 };
22842 }
22843 /**
22844 * Resolve a `ts.Symbol` to its declaration, keeping track of the `viaModule` along the way.
22845 */
22846 getDeclarationOfSymbol(symbol, originalId) {
22847 // If the symbol points to a ShorthandPropertyAssignment, resolve it.
22848 let valueDeclaration = undefined;
22849 if (symbol.valueDeclaration !== undefined) {
22850 valueDeclaration = symbol.valueDeclaration;
22851 }
22852 else if (symbol.declarations !== undefined && symbol.declarations.length > 0) {
22853 valueDeclaration = symbol.declarations[0];
22854 }
22855 if (valueDeclaration !== undefined && ts$1.isShorthandPropertyAssignment(valueDeclaration)) {
22856 const shorthandSymbol = this.checker.getShorthandAssignmentValueSymbol(valueDeclaration);
22857 if (shorthandSymbol === undefined) {
22858 return null;
22859 }
22860 return this.getDeclarationOfSymbol(shorthandSymbol, originalId);
22861 }
22862 else if (valueDeclaration !== undefined && ts$1.isExportSpecifier(valueDeclaration)) {
22863 const targetSymbol = this.checker.getExportSpecifierLocalTargetSymbol(valueDeclaration);
22864 if (targetSymbol === undefined) {
22865 return null;
22866 }
22867 return this.getDeclarationOfSymbol(targetSymbol, originalId);
22868 }
22869 const importInfo = originalId && this.getImportOfIdentifier(originalId);
22870 const viaModule = importInfo !== null && importInfo.from !== null && !importInfo.from.startsWith('.') ?
22871 importInfo.from :
22872 null;
22873 // Now, resolve the Symbol to its declaration by following any and all aliases.
22874 while (symbol.flags & ts$1.SymbolFlags.Alias) {
22875 symbol = this.checker.getAliasedSymbol(symbol);
22876 }
22877 // Look at the resolved Symbol's declarations and pick one of them to return. Value declarations
22878 // are given precedence over type declarations.
22879 if (symbol.valueDeclaration !== undefined) {
22880 return {
22881 node: symbol.valueDeclaration,
22882 known: null,
22883 viaModule,
22884 identity: null,
22885 kind: 0 /* Concrete */,
22886 };
22887 }
22888 else if (symbol.declarations !== undefined && symbol.declarations.length > 0) {
22889 return {
22890 node: symbol.declarations[0],
22891 known: null,
22892 viaModule,
22893 identity: null,
22894 kind: 0 /* Concrete */,
22895 };
22896 }
22897 else {
22898 return null;
22899 }
22900 }
22901 _reflectDecorator(node) {
22902 // Attempt to resolve the decorator expression into a reference to a concrete Identifier. The
22903 // expression may contain a call to a function which returns the decorator function, in which
22904 // case we want to return the arguments.
22905 let decoratorExpr = node.expression;
22906 let args = null;
22907 // Check for call expressions.
22908 if (ts$1.isCallExpression(decoratorExpr)) {
22909 args = Array.from(decoratorExpr.arguments);
22910 decoratorExpr = decoratorExpr.expression;
22911 }
22912 // The final resolved decorator should be a `ts.Identifier` - if it's not, then something is
22913 // wrong and the decorator can't be resolved statically.
22914 if (!isDecoratorIdentifier(decoratorExpr)) {
22915 return null;
22916 }
22917 const decoratorIdentifier = ts$1.isIdentifier(decoratorExpr) ? decoratorExpr : decoratorExpr.name;
22918 const importDecl = this.getImportOfIdentifier(decoratorIdentifier);
22919 return {
22920 name: decoratorIdentifier.text,
22921 identifier: decoratorExpr,
22922 import: importDecl,
22923 node,
22924 args,
22925 };
22926 }
22927 _reflectMember(node) {
22928 let kind = null;
22929 let value = null;
22930 let name = null;
22931 let nameNode = null;
22932 if (ts$1.isPropertyDeclaration(node)) {
22933 kind = ClassMemberKind.Property;
22934 value = node.initializer || null;
22935 }
22936 else if (ts$1.isGetAccessorDeclaration(node)) {
22937 kind = ClassMemberKind.Getter;
22938 }
22939 else if (ts$1.isSetAccessorDeclaration(node)) {
22940 kind = ClassMemberKind.Setter;
22941 }
22942 else if (ts$1.isMethodDeclaration(node)) {
22943 kind = ClassMemberKind.Method;
22944 }
22945 else if (ts$1.isConstructorDeclaration(node)) {
22946 kind = ClassMemberKind.Constructor;
22947 }
22948 else {
22949 return null;
22950 }
22951 if (ts$1.isConstructorDeclaration(node)) {
22952 name = 'constructor';
22953 }
22954 else if (ts$1.isIdentifier(node.name)) {
22955 name = node.name.text;
22956 nameNode = node.name;
22957 }
22958 else if (ts$1.isStringLiteral(node.name)) {
22959 name = node.name.text;
22960 nameNode = node.name;
22961 }
22962 else {
22963 return null;
22964 }
22965 const decorators = this.getDecoratorsOfDeclaration(node);
22966 const isStatic = node.modifiers !== undefined &&
22967 node.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.StaticKeyword);
22968 return {
22969 node,
22970 implementation: node,
22971 kind,
22972 type: node.type || null,
22973 name,
22974 nameNode,
22975 decorators,
22976 value,
22977 isStatic,
22978 };
22979 }
22980 }
22981 function reflectTypeEntityToDeclaration(type, checker) {
22982 let realSymbol = checker.getSymbolAtLocation(type);
22983 if (realSymbol === undefined) {
22984 throw new Error(`Cannot resolve type entity ${type.getText()} to symbol`);
22985 }
22986 while (realSymbol.flags & ts$1.SymbolFlags.Alias) {
22987 realSymbol = checker.getAliasedSymbol(realSymbol);
22988 }
22989 let node = null;
22990 if (realSymbol.valueDeclaration !== undefined) {
22991 node = realSymbol.valueDeclaration;
22992 }
22993 else if (realSymbol.declarations !== undefined && realSymbol.declarations.length === 1) {
22994 node = realSymbol.declarations[0];
22995 }
22996 else {
22997 throw new Error(`Cannot resolve type entity symbol to declaration`);
22998 }
22999 if (ts$1.isQualifiedName(type)) {
23000 if (!ts$1.isIdentifier(type.left)) {
23001 throw new Error(`Cannot handle qualified name with non-identifier lhs`);
23002 }
23003 const symbol = checker.getSymbolAtLocation(type.left);
23004 if (symbol === undefined || symbol.declarations === undefined ||
23005 symbol.declarations.length !== 1) {
23006 throw new Error(`Cannot resolve qualified type entity lhs to symbol`);
23007 }
23008 const decl = symbol.declarations[0];
23009 if (ts$1.isNamespaceImport(decl)) {
23010 const clause = decl.parent;
23011 const importDecl = clause.parent;
23012 if (!ts$1.isStringLiteral(importDecl.moduleSpecifier)) {
23013 throw new Error(`Module specifier is not a string`);
23014 }
23015 return { node, from: importDecl.moduleSpecifier.text };
23016 }
23017 else {
23018 throw new Error(`Unknown import type?`);
23019 }
23020 }
23021 else {
23022 return { node, from: null };
23023 }
23024 }
23025 function filterToMembersWithDecorator(members, name, module) {
23026 return members.filter(member => !member.isStatic)
23027 .map(member => {
23028 if (member.decorators === null) {
23029 return null;
23030 }
23031 const decorators = member.decorators.filter(dec => {
23032 if (dec.import !== null) {
23033 return dec.import.name === name && (module === undefined || dec.import.from === module);
23034 }
23035 else {
23036 return dec.name === name && module === undefined;
23037 }
23038 });
23039 if (decorators.length === 0) {
23040 return null;
23041 }
23042 return { member, decorators };
23043 })
23044 .filter((value) => value !== null);
23045 }
23046 function reflectObjectLiteral(node) {
23047 const map = new Map();
23048 node.properties.forEach(prop => {
23049 if (ts$1.isPropertyAssignment(prop)) {
23050 const name = propertyNameToString(prop.name);
23051 if (name === null) {
23052 return;
23053 }
23054 map.set(name, prop.initializer);
23055 }
23056 else if (ts$1.isShorthandPropertyAssignment(prop)) {
23057 map.set(prop.name.text, prop.name);
23058 }
23059 else {
23060 return;
23061 }
23062 });
23063 return map;
23064 }
23065 function castDeclarationToClassOrDie(declaration) {
23066 if (!ts$1.isClassDeclaration(declaration)) {
23067 throw new Error(`Reflecting on a ${ts$1.SyntaxKind[declaration.kind]} instead of a ClassDeclaration.`);
23068 }
23069 return declaration;
23070 }
23071 function parameterName(name) {
23072 if (ts$1.isIdentifier(name)) {
23073 return name.text;
23074 }
23075 else {
23076 return null;
23077 }
23078 }
23079 function propertyNameToString(node) {
23080 if (ts$1.isIdentifier(node) || ts$1.isStringLiteral(node) || ts$1.isNumericLiteral(node)) {
23081 return node.text;
23082 }
23083 else {
23084 return null;
23085 }
23086 }
23087 /**
23088 * Compute the left most identifier in a qualified type chain. E.g. the `a` of `a.b.c.SomeType`.
23089 * @param qualifiedName The starting property access expression from which we want to compute
23090 * the left most identifier.
23091 * @returns the left most identifier in the chain or `null` if it is not an identifier.
23092 */
23093 function getQualifiedNameRoot(qualifiedName) {
23094 while (ts$1.isQualifiedName(qualifiedName.left)) {
23095 qualifiedName = qualifiedName.left;
23096 }
23097 return ts$1.isIdentifier(qualifiedName.left) ? qualifiedName.left : null;
23098 }
23099 /**
23100 * Compute the left most identifier in a property access chain. E.g. the `a` of `a.b.c.d`.
23101 * @param propertyAccess The starting property access expression from which we want to compute
23102 * the left most identifier.
23103 * @returns the left most identifier in the chain or `null` if it is not an identifier.
23104 */
23105 function getFarLeftIdentifier(propertyAccess) {
23106 while (ts$1.isPropertyAccessExpression(propertyAccess.expression)) {
23107 propertyAccess = propertyAccess.expression;
23108 }
23109 return ts$1.isIdentifier(propertyAccess.expression) ? propertyAccess.expression : null;
23110 }
23111 /**
23112 * Return the ImportDeclaration for the given `node` if it is either an `ImportSpecifier` or a
23113 * `NamespaceImport`. If not return `null`.
23114 */
23115 function getContainingImportDeclaration(node) {
23116 return ts$1.isImportSpecifier(node) ? node.parent.parent.parent :
23117 ts$1.isNamespaceImport(node) ? node.parent.parent : null;
23118 }
23119 /**
23120 * Compute the name by which the `decl` was exported, not imported.
23121 * If no such declaration can be found (e.g. it is a namespace import)
23122 * then fallback to the `originalId`.
23123 */
23124 function getExportedName(decl, originalId) {
23125 return ts$1.isImportSpecifier(decl) ?
23126 (decl.propertyName !== undefined ? decl.propertyName : decl.name).text :
23127 originalId.text;
23128 }
23129
23130 /**
23131 * @license
23132 * Copyright Google LLC All Rights Reserved.
23133 *
23134 * Use of this source code is governed by an MIT-style license that can be
23135 * found in the LICENSE file at https://angular.io/license
23136 */
23137 /**
23138 * A mapping of component property and template binding property names, for example containing the
23139 * inputs of a particular directive or component.
23140 *
23141 * A single component property has exactly one input/output annotation (and therefore one binding
23142 * property name) associated with it, but the same binding property name may be shared across many
23143 * component property names.
23144 *
23145 * Allows bidirectional querying of the mapping - looking up all inputs/outputs with a given
23146 * property name, or mapping from a specific class property to its binding property name.
23147 */
23148 class ClassPropertyMapping {
23149 constructor(forwardMap) {
23150 this.forwardMap = forwardMap;
23151 this.reverseMap = reverseMapFromForwardMap(forwardMap);
23152 }
23153 /**
23154 * Construct a `ClassPropertyMapping` with no entries.
23155 */
23156 static empty() {
23157 return new ClassPropertyMapping(new Map());
23158 }
23159 /**
23160 * Construct a `ClassPropertyMapping` from a primitive JS object which maps class property names
23161 * to either binding property names or an array that contains both names, which is used in on-disk
23162 * metadata formats (e.g. in .d.ts files).
23163 */
23164 static fromMappedObject(obj) {
23165 const forwardMap = new Map();
23166 for (const classPropertyName of Object.keys(obj)) {
23167 const value = obj[classPropertyName];
23168 const bindingPropertyName = Array.isArray(value) ? value[0] : value;
23169 const inputOrOutput = { classPropertyName, bindingPropertyName };
23170 forwardMap.set(classPropertyName, inputOrOutput);
23171 }
23172 return new ClassPropertyMapping(forwardMap);
23173 }
23174 /**
23175 * Merge two mappings into one, with class properties from `b` taking precedence over class
23176 * properties from `a`.
23177 */
23178 static merge(a, b) {
23179 const forwardMap = new Map(a.forwardMap.entries());
23180 for (const [classPropertyName, inputOrOutput] of b.forwardMap) {
23181 forwardMap.set(classPropertyName, inputOrOutput);
23182 }
23183 return new ClassPropertyMapping(forwardMap);
23184 }
23185 /**
23186 * All class property names mapped in this mapping.
23187 */
23188 get classPropertyNames() {
23189 return Array.from(this.forwardMap.keys());
23190 }
23191 /**
23192 * All binding property names mapped in this mapping.
23193 */
23194 get propertyNames() {
23195 return Array.from(this.reverseMap.keys());
23196 }
23197 /**
23198 * Check whether a mapping for the given property name exists.
23199 */
23200 hasBindingPropertyName(propertyName) {
23201 return this.reverseMap.has(propertyName);
23202 }
23203 /**
23204 * Lookup all `InputOrOutput`s that use this `propertyName`.
23205 */
23206 getByBindingPropertyName(propertyName) {
23207 return this.reverseMap.has(propertyName) ? this.reverseMap.get(propertyName) : null;
23208 }
23209 /**
23210 * Lookup the `InputOrOutput` associated with a `classPropertyName`.
23211 */
23212 getByClassPropertyName(classPropertyName) {
23213 return this.forwardMap.has(classPropertyName) ? this.forwardMap.get(classPropertyName) : null;
23214 }
23215 /**
23216 * Convert this mapping to a primitive JS object which maps each class property directly to the
23217 * binding property name associated with it.
23218 */
23219 toDirectMappedObject() {
23220 const obj = {};
23221 for (const [classPropertyName, inputOrOutput] of this.forwardMap) {
23222 obj[classPropertyName] = inputOrOutput.bindingPropertyName;
23223 }
23224 return obj;
23225 }
23226 /**
23227 * Convert this mapping to a primitive JS object which maps each class property either to itself
23228 * (for cases where the binding property name is the same) or to an array which contains both
23229 * names if they differ.
23230 *
23231 * This object format is used when mappings are serialized (for example into .d.ts files).
23232 */
23233 toJointMappedObject() {
23234 const obj = {};
23235 for (const [classPropertyName, inputOrOutput] of this.forwardMap) {
23236 if (inputOrOutput.bindingPropertyName === classPropertyName) {
23237 obj[classPropertyName] = inputOrOutput.bindingPropertyName;
23238 }
23239 else {
23240 obj[classPropertyName] = [inputOrOutput.bindingPropertyName, classPropertyName];
23241 }
23242 }
23243 return obj;
23244 }
23245 /**
23246 * Implement the iterator protocol and return entry objects which contain the class and binding
23247 * property names (and are useful for destructuring).
23248 */
23249 *[Symbol.iterator]() {
23250 for (const [classPropertyName, inputOrOutput] of this.forwardMap.entries()) {
23251 yield [classPropertyName, inputOrOutput.bindingPropertyName];
23252 }
23253 }
23254 }
23255 function reverseMapFromForwardMap(forwardMap) {
23256 const reverseMap = new Map();
23257 for (const [_, inputOrOutput] of forwardMap) {
23258 if (!reverseMap.has(inputOrOutput.bindingPropertyName)) {
23259 reverseMap.set(inputOrOutput.bindingPropertyName, []);
23260 }
23261 reverseMap.get(inputOrOutput.bindingPropertyName).push(inputOrOutput);
23262 }
23263 return reverseMap;
23264 }
23265
23266 /**
23267 * @license
23268 * Copyright Google LLC All Rights Reserved.
23269 *
23270 * Use of this source code is governed by an MIT-style license that can be
23271 * found in the LICENSE file at https://angular.io/license
23272 */
23273 function extractReferencesFromType(checker, def, ngModuleImportedFrom, resolutionContext) {
23274 if (!ts$1.isTupleTypeNode(def)) {
23275 return [];
23276 }
23277 return def.elements.map(element => {
23278 if (!ts$1.isTypeQueryNode(element)) {
23279 throw new Error(`Expected TypeQueryNode: ${nodeDebugInfo(element)}`);
23280 }
23281 const type = element.exprName;
23282 const { node, from } = reflectTypeEntityToDeclaration(type, checker);
23283 if (!isNamedClassDeclaration(node)) {
23284 throw new Error(`Expected named ClassDeclaration: ${nodeDebugInfo(node)}`);
23285 }
23286 const specifier = (from !== null && !from.startsWith('.') ? from : ngModuleImportedFrom);
23287 if (specifier !== null) {
23288 return new Reference$1(node, { specifier, resolutionContext });
23289 }
23290 else {
23291 return new Reference$1(node);
23292 }
23293 });
23294 }
23295 function readStringType(type) {
23296 if (!ts$1.isLiteralTypeNode(type) || !ts$1.isStringLiteral(type.literal)) {
23297 return null;
23298 }
23299 return type.literal.text;
23300 }
23301 function readStringMapType(type) {
23302 if (!ts$1.isTypeLiteralNode(type)) {
23303 return {};
23304 }
23305 const obj = {};
23306 type.members.forEach(member => {
23307 if (!ts$1.isPropertySignature(member) || member.type === undefined || member.name === undefined ||
23308 !ts$1.isStringLiteral(member.name)) {
23309 return;
23310 }
23311 const value = readStringType(member.type);
23312 if (value === null) {
23313 return null;
23314 }
23315 obj[member.name.text] = value;
23316 });
23317 return obj;
23318 }
23319 function readStringArrayType(type) {
23320 if (!ts$1.isTupleTypeNode(type)) {
23321 return [];
23322 }
23323 const res = [];
23324 type.elements.forEach(el => {
23325 if (!ts$1.isLiteralTypeNode(el) || !ts$1.isStringLiteral(el.literal)) {
23326 return;
23327 }
23328 res.push(el.literal.text);
23329 });
23330 return res;
23331 }
23332 /**
23333 * Inspects the class' members and extracts the metadata that is used when type-checking templates
23334 * that use the directive. This metadata does not contain information from a base class, if any,
23335 * making this metadata invariant to changes of inherited classes.
23336 */
23337 function extractDirectiveTypeCheckMeta(node, inputs, reflector) {
23338 const members = reflector.getMembersOfClass(node);
23339 const staticMembers = members.filter(member => member.isStatic);
23340 const ngTemplateGuards = staticMembers.map(extractTemplateGuard)
23341 .filter((guard) => guard !== null);
23342 const hasNgTemplateContextGuard = staticMembers.some(member => member.kind === ClassMemberKind.Method && member.name === 'ngTemplateContextGuard');
23343 const coercedInputFields = new Set(staticMembers.map(extractCoercedInput)
23344 .filter((inputName) => inputName !== null));
23345 const restrictedInputFields = new Set();
23346 const stringLiteralInputFields = new Set();
23347 const undeclaredInputFields = new Set();
23348 for (const classPropertyName of inputs.classPropertyNames) {
23349 const field = members.find(member => member.name === classPropertyName);
23350 if (field === undefined || field.node === null) {
23351 undeclaredInputFields.add(classPropertyName);
23352 continue;
23353 }
23354 if (isRestricted(field.node)) {
23355 restrictedInputFields.add(classPropertyName);
23356 }
23357 if (field.nameNode !== null && ts$1.isStringLiteral(field.nameNode)) {
23358 stringLiteralInputFields.add(classPropertyName);
23359 }
23360 }
23361 const arity = reflector.getGenericArityOfClass(node);
23362 return {
23363 hasNgTemplateContextGuard,
23364 ngTemplateGuards,
23365 coercedInputFields,
23366 restrictedInputFields,
23367 stringLiteralInputFields,
23368 undeclaredInputFields,
23369 isGeneric: arity !== null && arity > 0,
23370 };
23371 }
23372 function isRestricted(node) {
23373 if (node.modifiers === undefined) {
23374 return false;
23375 }
23376 return node.modifiers.some(modifier => modifier.kind === ts$1.SyntaxKind.PrivateKeyword ||
23377 modifier.kind === ts$1.SyntaxKind.ProtectedKeyword ||
23378 modifier.kind === ts$1.SyntaxKind.ReadonlyKeyword);
23379 }
23380 function extractTemplateGuard(member) {
23381 if (!member.name.startsWith('ngTemplateGuard_')) {
23382 return null;
23383 }
23384 const inputName = afterUnderscore(member.name);
23385 if (member.kind === ClassMemberKind.Property) {
23386 let type = null;
23387 if (member.type !== null && ts$1.isLiteralTypeNode(member.type) &&
23388 ts$1.isStringLiteral(member.type.literal)) {
23389 type = member.type.literal.text;
23390 }
23391 // Only property members with string literal type 'binding' are considered as template guard.
23392 if (type !== 'binding') {
23393 return null;
23394 }
23395 return { inputName, type };
23396 }
23397 else if (member.kind === ClassMemberKind.Method) {
23398 return { inputName, type: 'invocation' };
23399 }
23400 else {
23401 return null;
23402 }
23403 }
23404 function extractCoercedInput(member) {
23405 if (member.kind !== ClassMemberKind.Property || !member.name.startsWith('ngAcceptInputType_')) {
23406 return null;
23407 }
23408 return afterUnderscore(member.name);
23409 }
23410 /**
23411 * A `MetadataReader` that reads from an ordered set of child readers until it obtains the requested
23412 * metadata.
23413 *
23414 * This is used to combine `MetadataReader`s that read from different sources (e.g. from a registry
23415 * and from .d.ts files).
23416 */
23417 class CompoundMetadataReader {
23418 constructor(readers) {
23419 this.readers = readers;
23420 }
23421 getDirectiveMetadata(node) {
23422 for (const reader of this.readers) {
23423 const meta = reader.getDirectiveMetadata(node);
23424 if (meta !== null) {
23425 return meta;
23426 }
23427 }
23428 return null;
23429 }
23430 getNgModuleMetadata(node) {
23431 for (const reader of this.readers) {
23432 const meta = reader.getNgModuleMetadata(node);
23433 if (meta !== null) {
23434 return meta;
23435 }
23436 }
23437 return null;
23438 }
23439 getPipeMetadata(node) {
23440 for (const reader of this.readers) {
23441 const meta = reader.getPipeMetadata(node);
23442 if (meta !== null) {
23443 return meta;
23444 }
23445 }
23446 return null;
23447 }
23448 }
23449 function afterUnderscore(str) {
23450 const pos = str.indexOf('_');
23451 if (pos === -1) {
23452 throw new Error(`Expected '${str}' to contain '_'`);
23453 }
23454 return str.substr(pos + 1);
23455 }
23456 /** Returns whether a class declaration has the necessary class fields to make it injectable. */
23457 function hasInjectableFields(clazz, host) {
23458 const members = host.getMembersOfClass(clazz);
23459 return members.some(({ isStatic, name }) => isStatic && (name === 'ɵprov' || name === 'ɵfac' || name === 'ɵinj'));
23460 }
23461
23462 /**
23463 * @license
23464 * Copyright Google LLC All Rights Reserved.
23465 *
23466 * Use of this source code is governed by an MIT-style license that can be
23467 * found in the LICENSE file at https://angular.io/license
23468 */
23469 /**
23470 * A `MetadataReader` that can read metadata from `.d.ts` files, which have static Ivy properties
23471 * from an upstream compilation already.
23472 */
23473 class DtsMetadataReader {
23474 constructor(checker, reflector) {
23475 this.checker = checker;
23476 this.reflector = reflector;
23477 }
23478 /**
23479 * Read the metadata from a class that has already been compiled somehow (either it's in a .d.ts
23480 * file, or in a .ts file with a handwritten definition).
23481 *
23482 * @param ref `Reference` to the class of interest, with the context of how it was obtained.
23483 */
23484 getNgModuleMetadata(ref) {
23485 const clazz = ref.node;
23486 const resolutionContext = clazz.getSourceFile().fileName;
23487 // This operation is explicitly not memoized, as it depends on `ref.ownedByModuleGuess`.
23488 // TODO(alxhub): investigate caching of .d.ts module metadata.
23489 const ngModuleDef = this.reflector.getMembersOfClass(clazz).find(member => member.name === 'ɵmod' && member.isStatic);
23490 if (ngModuleDef === undefined) {
23491 return null;
23492 }
23493 else if (
23494 // Validate that the shape of the ngModuleDef type is correct.
23495 ngModuleDef.type === null || !ts$1.isTypeReferenceNode(ngModuleDef.type) ||
23496 ngModuleDef.type.typeArguments === undefined ||
23497 ngModuleDef.type.typeArguments.length !== 4) {
23498 return null;
23499 }
23500 // Read the ModuleData out of the type arguments.
23501 const [_, declarationMetadata, importMetadata, exportMetadata] = ngModuleDef.type.typeArguments;
23502 return {
23503 ref,
23504 declarations: extractReferencesFromType(this.checker, declarationMetadata, ref.ownedByModuleGuess, resolutionContext),
23505 exports: extractReferencesFromType(this.checker, exportMetadata, ref.ownedByModuleGuess, resolutionContext),
23506 imports: extractReferencesFromType(this.checker, importMetadata, ref.ownedByModuleGuess, resolutionContext),
23507 schemas: [],
23508 rawDeclarations: null,
23509 };
23510 }
23511 /**
23512 * Read directive (or component) metadata from a referenced class in a .d.ts file.
23513 */
23514 getDirectiveMetadata(ref) {
23515 const clazz = ref.node;
23516 const def = this.reflector.getMembersOfClass(clazz).find(field => field.isStatic && (field.name === 'ɵcmp' || field.name === 'ɵdir'));
23517 if (def === undefined) {
23518 // No definition could be found.
23519 return null;
23520 }
23521 else if (def.type === null || !ts$1.isTypeReferenceNode(def.type) ||
23522 def.type.typeArguments === undefined || def.type.typeArguments.length < 2) {
23523 // The type metadata was the wrong shape.
23524 return null;
23525 }
23526 const isComponent = def.name === 'ɵcmp';
23527 const ctorParams = this.reflector.getConstructorParameters(clazz);
23528 // A directive is considered to be structural if:
23529 // 1) it's a directive, not a component, and
23530 // 2) it injects `TemplateRef`
23531 const isStructural = !isComponent && ctorParams !== null && ctorParams.some(param => {
23532 return param.typeValueReference.kind === 1 /* IMPORTED */ &&
23533 param.typeValueReference.moduleName === '@angular/core' &&
23534 param.typeValueReference.importedName === 'TemplateRef';
23535 });
23536 const inputs = ClassPropertyMapping.fromMappedObject(readStringMapType(def.type.typeArguments[3]));
23537 const outputs = ClassPropertyMapping.fromMappedObject(readStringMapType(def.type.typeArguments[4]));
23538 return Object.assign(Object.assign({ ref, name: clazz.name.text, isComponent, selector: readStringType(def.type.typeArguments[1]), exportAs: readStringArrayType(def.type.typeArguments[2]), inputs,
23539 outputs, queries: readStringArrayType(def.type.typeArguments[5]) }, extractDirectiveTypeCheckMeta(clazz, inputs, this.reflector)), { baseClass: readBaseClass(clazz, this.checker, this.reflector), isPoisoned: false, isStructural });
23540 }
23541 /**
23542 * Read pipe metadata from a referenced class in a .d.ts file.
23543 */
23544 getPipeMetadata(ref) {
23545 const def = this.reflector.getMembersOfClass(ref.node).find(field => field.isStatic && field.name === 'ɵpipe');
23546 if (def === undefined) {
23547 // No definition could be found.
23548 return null;
23549 }
23550 else if (def.type === null || !ts$1.isTypeReferenceNode(def.type) ||
23551 def.type.typeArguments === undefined || def.type.typeArguments.length < 2) {
23552 // The type metadata was the wrong shape.
23553 return null;
23554 }
23555 const type = def.type.typeArguments[1];
23556 if (!ts$1.isLiteralTypeNode(type) || !ts$1.isStringLiteral(type.literal)) {
23557 // The type metadata was the wrong type.
23558 return null;
23559 }
23560 const name = type.literal.text;
23561 return { ref, name };
23562 }
23563 }
23564 function readBaseClass(clazz, checker, reflector) {
23565 if (!isNamedClassDeclaration(clazz)) {
23566 // Technically this is an error in a .d.ts file, but for the purposes of finding the base class
23567 // it's ignored.
23568 return reflector.hasBaseClass(clazz) ? 'dynamic' : null;
23569 }
23570 if (clazz.heritageClauses !== undefined) {
23571 for (const clause of clazz.heritageClauses) {
23572 if (clause.token === ts$1.SyntaxKind.ExtendsKeyword) {
23573 const baseExpr = clause.types[0].expression;
23574 let symbol = checker.getSymbolAtLocation(baseExpr);
23575 if (symbol === undefined) {
23576 return 'dynamic';
23577 }
23578 else if (symbol.flags & ts$1.SymbolFlags.Alias) {
23579 symbol = checker.getAliasedSymbol(symbol);
23580 }
23581 if (symbol.valueDeclaration !== undefined &&
23582 isNamedClassDeclaration(symbol.valueDeclaration)) {
23583 return new Reference$1(symbol.valueDeclaration);
23584 }
23585 else {
23586 return 'dynamic';
23587 }
23588 }
23589 }
23590 }
23591 return null;
23592 }
23593
23594 /**
23595 * @license
23596 * Copyright Google LLC All Rights Reserved.
23597 *
23598 * Use of this source code is governed by an MIT-style license that can be
23599 * found in the LICENSE file at https://angular.io/license
23600 */
23601 /**
23602 * Given a reference to a directive, return a flattened version of its `DirectiveMeta` metadata
23603 * which includes metadata from its entire inheritance chain.
23604 *
23605 * The returned `DirectiveMeta` will either have `baseClass: null` if the inheritance chain could be
23606 * fully resolved, or `baseClass: 'dynamic'` if the inheritance chain could not be completely
23607 * followed.
23608 */
23609 function flattenInheritedDirectiveMetadata(reader, dir) {
23610 const topMeta = reader.getDirectiveMetadata(dir);
23611 if (topMeta === null) {
23612 throw new Error(`Metadata not found for directive: ${dir.debugName}`);
23613 }
23614 if (topMeta.baseClass === null) {
23615 return topMeta;
23616 }
23617 const coercedInputFields = new Set();
23618 const undeclaredInputFields = new Set();
23619 const restrictedInputFields = new Set();
23620 const stringLiteralInputFields = new Set();
23621 let isDynamic = false;
23622 let inputs = ClassPropertyMapping.empty();
23623 let outputs = ClassPropertyMapping.empty();
23624 let isStructural = false;
23625 const addMetadata = (meta) => {
23626 if (meta.baseClass === 'dynamic') {
23627 isDynamic = true;
23628 }
23629 else if (meta.baseClass !== null) {
23630 const baseMeta = reader.getDirectiveMetadata(meta.baseClass);
23631 if (baseMeta !== null) {
23632 addMetadata(baseMeta);
23633 }
23634 else {
23635 // Missing metadata for the base class means it's effectively dynamic.
23636 isDynamic = true;
23637 }
23638 }
23639 isStructural = isStructural || meta.isStructural;
23640 inputs = ClassPropertyMapping.merge(inputs, meta.inputs);
23641 outputs = ClassPropertyMapping.merge(outputs, meta.outputs);
23642 for (const coercedInputField of meta.coercedInputFields) {
23643 coercedInputFields.add(coercedInputField);
23644 }
23645 for (const undeclaredInputField of meta.undeclaredInputFields) {
23646 undeclaredInputFields.add(undeclaredInputField);
23647 }
23648 for (const restrictedInputField of meta.restrictedInputFields) {
23649 restrictedInputFields.add(restrictedInputField);
23650 }
23651 for (const field of meta.stringLiteralInputFields) {
23652 stringLiteralInputFields.add(field);
23653 }
23654 };
23655 addMetadata(topMeta);
23656 return Object.assign(Object.assign({}, topMeta), { inputs,
23657 outputs,
23658 coercedInputFields,
23659 undeclaredInputFields,
23660 restrictedInputFields,
23661 stringLiteralInputFields, baseClass: isDynamic ? 'dynamic' : null, isStructural });
23662 }
23663
23664 /**
23665 * @license
23666 * Copyright Google LLC All Rights Reserved.
23667 *
23668 * Use of this source code is governed by an MIT-style license that can be
23669 * found in the LICENSE file at https://angular.io/license
23670 */
23671 /**
23672 * A registry of directive, pipe, and module metadata for types defined in the current compilation
23673 * unit, which supports both reading and registering.
23674 */
23675 class LocalMetadataRegistry {
23676 constructor() {
23677 this.directives = new Map();
23678 this.ngModules = new Map();
23679 this.pipes = new Map();
23680 }
23681 getDirectiveMetadata(ref) {
23682 return this.directives.has(ref.node) ? this.directives.get(ref.node) : null;
23683 }
23684 getNgModuleMetadata(ref) {
23685 return this.ngModules.has(ref.node) ? this.ngModules.get(ref.node) : null;
23686 }
23687 getPipeMetadata(ref) {
23688 return this.pipes.has(ref.node) ? this.pipes.get(ref.node) : null;
23689 }
23690 registerDirectiveMetadata(meta) {
23691 this.directives.set(meta.ref.node, meta);
23692 }
23693 registerNgModuleMetadata(meta) {
23694 this.ngModules.set(meta.ref.node, meta);
23695 }
23696 registerPipeMetadata(meta) {
23697 this.pipes.set(meta.ref.node, meta);
23698 }
23699 }
23700 /**
23701 * A `MetadataRegistry` which registers metdata with multiple delegate `MetadataRegistry` instances.
23702 */
23703 class CompoundMetadataRegistry {
23704 constructor(registries) {
23705 this.registries = registries;
23706 }
23707 registerDirectiveMetadata(meta) {
23708 for (const registry of this.registries) {
23709 registry.registerDirectiveMetadata(meta);
23710 }
23711 }
23712 registerNgModuleMetadata(meta) {
23713 for (const registry of this.registries) {
23714 registry.registerNgModuleMetadata(meta);
23715 }
23716 }
23717 registerPipeMetadata(meta) {
23718 for (const registry of this.registries) {
23719 registry.registerPipeMetadata(meta);
23720 }
23721 }
23722 }
23723 /**
23724 * Registry that keeps track of classes that can be constructed via dependency injection (e.g.
23725 * injectables, directives, pipes).
23726 */
23727 class InjectableClassRegistry {
23728 constructor(host) {
23729 this.host = host;
23730 this.classes = new Set();
23731 }
23732 registerInjectable(declaration) {
23733 this.classes.add(declaration);
23734 }
23735 isInjectable(declaration) {
23736 // Figure out whether the class is injectable based on the registered classes, otherwise
23737 // fall back to looking at its members since we might not have been able register the class
23738 // if it was compiled already.
23739 return this.classes.has(declaration) || hasInjectableFields(declaration, this.host);
23740 }
23741 }
23742
23743 /**
23744 * @license
23745 * Copyright Google LLC All Rights Reserved.
23746 *
23747 * Use of this source code is governed by an MIT-style license that can be
23748 * found in the LICENSE file at https://angular.io/license
23749 */
23750 function isExternalResource(resource) {
23751 return resource.path !== null;
23752 }
23753 /**
23754 * Tracks the mapping between external template/style files and the component(s) which use them.
23755 *
23756 * This information is produced during analysis of the program and is used mainly to support
23757 * external tooling, for which such a mapping is challenging to determine without compiler
23758 * assistance.
23759 */
23760 class ResourceRegistry {
23761 constructor() {
23762 this.externalTemplateToComponentsMap = new Map();
23763 this.componentToTemplateMap = new Map();
23764 this.componentToStylesMap = new Map();
23765 this.externalStyleToComponentsMap = new Map();
23766 }
23767 getComponentsWithTemplate(template) {
23768 if (!this.externalTemplateToComponentsMap.has(template)) {
23769 return new Set();
23770 }
23771 return this.externalTemplateToComponentsMap.get(template);
23772 }
23773 registerResources(resources, component) {
23774 if (resources.template !== null) {
23775 this.registerTemplate(resources.template, component);
23776 }
23777 for (const style of resources.styles) {
23778 this.registerStyle(style, component);
23779 }
23780 }
23781 registerTemplate(templateResource, component) {
23782 const { path } = templateResource;
23783 if (path !== null) {
23784 if (!this.externalTemplateToComponentsMap.has(path)) {
23785 this.externalTemplateToComponentsMap.set(path, new Set());
23786 }
23787 this.externalTemplateToComponentsMap.get(path).add(component);
23788 }
23789 this.componentToTemplateMap.set(component, templateResource);
23790 }
23791 getTemplate(component) {
23792 if (!this.componentToTemplateMap.has(component)) {
23793 return null;
23794 }
23795 return this.componentToTemplateMap.get(component);
23796 }
23797 registerStyle(styleResource, component) {
23798 const { path } = styleResource;
23799 if (!this.componentToStylesMap.has(component)) {
23800 this.componentToStylesMap.set(component, new Set());
23801 }
23802 if (path !== null) {
23803 if (!this.externalStyleToComponentsMap.has(path)) {
23804 this.externalStyleToComponentsMap.set(path, new Set());
23805 }
23806 this.externalStyleToComponentsMap.get(path).add(component);
23807 }
23808 this.componentToStylesMap.get(component).add(styleResource);
23809 }
23810 getStyles(component) {
23811 if (!this.componentToStylesMap.has(component)) {
23812 return new Set();
23813 }
23814 return this.componentToStylesMap.get(component);
23815 }
23816 getComponentsWithStyle(styleUrl) {
23817 if (!this.externalStyleToComponentsMap.has(styleUrl)) {
23818 return new Set();
23819 }
23820 return this.externalStyleToComponentsMap.get(styleUrl);
23821 }
23822 }
23823
23824 /**
23825 * @license
23826 * Copyright Google LLC All Rights Reserved.
23827 *
23828 * Use of this source code is governed by an MIT-style license that can be
23829 * found in the LICENSE file at https://angular.io/license
23830 */
23831 /**
23832 * Represents a value which cannot be determined statically.
23833 */
23834 class DynamicValue {
23835 constructor(node, reason, code) {
23836 this.node = node;
23837 this.reason = reason;
23838 this.code = code;
23839 }
23840 static fromDynamicInput(node, input) {
23841 return new DynamicValue(node, input, 0 /* DYNAMIC_INPUT */);
23842 }
23843 static fromDynamicString(node) {
23844 return new DynamicValue(node, undefined, 1 /* DYNAMIC_STRING */);
23845 }
23846 static fromExternalReference(node, ref) {
23847 return new DynamicValue(node, ref, 2 /* EXTERNAL_REFERENCE */);
23848 }
23849 static fromUnsupportedSyntax(node) {
23850 return new DynamicValue(node, undefined, 3 /* UNSUPPORTED_SYNTAX */);
23851 }
23852 static fromUnknownIdentifier(node) {
23853 return new DynamicValue(node, undefined, 4 /* UNKNOWN_IDENTIFIER */);
23854 }
23855 static fromInvalidExpressionType(node, value) {
23856 return new DynamicValue(node, value, 5 /* INVALID_EXPRESSION_TYPE */);
23857 }
23858 static fromComplexFunctionCall(node, fn) {
23859 return new DynamicValue(node, fn, 6 /* COMPLEX_FUNCTION_CALL */);
23860 }
23861 static fromUnknown(node) {
23862 return new DynamicValue(node, undefined, 7 /* UNKNOWN */);
23863 }
23864 isFromDynamicInput() {
23865 return this.code === 0 /* DYNAMIC_INPUT */;
23866 }
23867 isFromDynamicString() {
23868 return this.code === 1 /* DYNAMIC_STRING */;
23869 }
23870 isFromExternalReference() {
23871 return this.code === 2 /* EXTERNAL_REFERENCE */;
23872 }
23873 isFromUnsupportedSyntax() {
23874 return this.code === 3 /* UNSUPPORTED_SYNTAX */;
23875 }
23876 isFromUnknownIdentifier() {
23877 return this.code === 4 /* UNKNOWN_IDENTIFIER */;
23878 }
23879 isFromInvalidExpressionType() {
23880 return this.code === 5 /* INVALID_EXPRESSION_TYPE */;
23881 }
23882 isFromComplexFunctionCall() {
23883 return this.code === 6 /* COMPLEX_FUNCTION_CALL */;
23884 }
23885 isFromUnknown() {
23886 return this.code === 7 /* UNKNOWN */;
23887 }
23888 accept(visitor) {
23889 switch (this.code) {
23890 case 0 /* DYNAMIC_INPUT */:
23891 return visitor.visitDynamicInput(this);
23892 case 1 /* DYNAMIC_STRING */:
23893 return visitor.visitDynamicString(this);
23894 case 2 /* EXTERNAL_REFERENCE */:
23895 return visitor.visitExternalReference(this);
23896 case 3 /* UNSUPPORTED_SYNTAX */:
23897 return visitor.visitUnsupportedSyntax(this);
23898 case 4 /* UNKNOWN_IDENTIFIER */:
23899 return visitor.visitUnknownIdentifier(this);
23900 case 5 /* INVALID_EXPRESSION_TYPE */:
23901 return visitor.visitInvalidExpressionType(this);
23902 case 6 /* COMPLEX_FUNCTION_CALL */:
23903 return visitor.visitComplexFunctionCall(this);
23904 case 7 /* UNKNOWN */:
23905 return visitor.visitUnknown(this);
23906 }
23907 }
23908 }
23909
23910 /**
23911 * @license
23912 * Copyright Google LLC All Rights Reserved.
23913 *
23914 * Use of this source code is governed by an MIT-style license that can be
23915 * found in the LICENSE file at https://angular.io/license
23916 */
23917 /**
23918 * A collection of publicly exported declarations from a module. Each declaration is evaluated
23919 * lazily upon request.
23920 */
23921 class ResolvedModule {
23922 constructor(exports, evaluate) {
23923 this.exports = exports;
23924 this.evaluate = evaluate;
23925 }
23926 getExport(name) {
23927 if (!this.exports.has(name)) {
23928 return undefined;
23929 }
23930 return this.evaluate(this.exports.get(name));
23931 }
23932 getExports() {
23933 const map = new Map();
23934 this.exports.forEach((decl, name) => {
23935 map.set(name, this.evaluate(decl));
23936 });
23937 return map;
23938 }
23939 }
23940 /**
23941 * A value member of an enumeration.
23942 *
23943 * Contains a `Reference` to the enumeration itself, and the name of the referenced member.
23944 */
23945 class EnumValue {
23946 constructor(enumRef, name, resolved) {
23947 this.enumRef = enumRef;
23948 this.name = name;
23949 this.resolved = resolved;
23950 }
23951 }
23952 /**
23953 * An implementation of a known function that can be statically evaluated.
23954 * It could be a built-in function or method (such as `Array.prototype.slice`) or a TypeScript
23955 * helper (such as `__spread`).
23956 */
23957 class KnownFn {
23958 }
23959
23960 /**
23961 * @license
23962 * Copyright Google LLC All Rights Reserved.
23963 *
23964 * Use of this source code is governed by an MIT-style license that can be
23965 * found in the LICENSE file at https://angular.io/license
23966 */
23967 /**
23968 * Derives a type representation from a resolved value to be reported in a diagnostic.
23969 *
23970 * @param value The resolved value for which a type representation should be derived.
23971 * @param maxDepth The maximum nesting depth of objects and arrays, defaults to 1 level.
23972 */
23973 function describeResolvedType(value, maxDepth = 1) {
23974 var _a, _b;
23975 if (value === null) {
23976 return 'null';
23977 }
23978 else if (value === undefined) {
23979 return 'undefined';
23980 }
23981 else if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'string') {
23982 return typeof value;
23983 }
23984 else if (value instanceof Map) {
23985 if (maxDepth === 0) {
23986 return 'object';
23987 }
23988 const entries = Array.from(value.entries()).map(([key, v]) => {
23989 return `${quoteKey(key)}: ${describeResolvedType(v, maxDepth - 1)}`;
23990 });
23991 return entries.length > 0 ? `{ ${entries.join('; ')} }` : '{}';
23992 }
23993 else if (value instanceof ResolvedModule) {
23994 return '(module)';
23995 }
23996 else if (value instanceof EnumValue) {
23997 return (_a = value.enumRef.debugName) !== null && _a !== void 0 ? _a : '(anonymous)';
23998 }
23999 else if (value instanceof Reference$1) {
24000 return (_b = value.debugName) !== null && _b !== void 0 ? _b : '(anonymous)';
24001 }
24002 else if (Array.isArray(value)) {
24003 if (maxDepth === 0) {
24004 return 'Array';
24005 }
24006 return `[${value.map(v => describeResolvedType(v, maxDepth - 1)).join(', ')}]`;
24007 }
24008 else if (value instanceof DynamicValue) {
24009 return '(not statically analyzable)';
24010 }
24011 else if (value instanceof KnownFn) {
24012 return 'Function';
24013 }
24014 else {
24015 return 'unknown';
24016 }
24017 }
24018 function quoteKey(key) {
24019 if (/^[a-z0-9_]+$/i.test(key)) {
24020 return key;
24021 }
24022 else {
24023 return `'${key.replace(/'/g, '\\\'')}'`;
24024 }
24025 }
24026 /**
24027 * Creates an array of related information diagnostics for a `DynamicValue` that describe the trace
24028 * of why an expression was evaluated as dynamic.
24029 *
24030 * @param node The node for which a `ts.Diagnostic` is to be created with the trace.
24031 * @param value The dynamic value for which a trace should be created.
24032 */
24033 function traceDynamicValue(node, value) {
24034 return value.accept(new TraceDynamicValueVisitor(node));
24035 }
24036 class TraceDynamicValueVisitor {
24037 constructor(node) {
24038 this.node = node;
24039 this.currentContainerNode = null;
24040 }
24041 visitDynamicInput(value) {
24042 const trace = value.reason.accept(this);
24043 if (this.shouldTrace(value.node)) {
24044 const info = makeRelatedInformation(value.node, 'Unable to evaluate this expression statically.');
24045 trace.unshift(info);
24046 }
24047 return trace;
24048 }
24049 visitDynamicString(value) {
24050 return [makeRelatedInformation(value.node, 'A string value could not be determined statically.')];
24051 }
24052 visitExternalReference(value) {
24053 const name = value.reason.debugName;
24054 const description = name !== null ? `'${name}'` : 'an anonymous declaration';
24055 return [makeRelatedInformation(value.node, `A value for ${description} cannot be determined statically, as it is an external declaration.`)];
24056 }
24057 visitComplexFunctionCall(value) {
24058 return [
24059 makeRelatedInformation(value.node, 'Unable to evaluate function call of complex function. A function must have exactly one return statement.'),
24060 makeRelatedInformation(value.reason.node, 'Function is declared here.')
24061 ];
24062 }
24063 visitInvalidExpressionType(value) {
24064 return [makeRelatedInformation(value.node, 'Unable to evaluate an invalid expression.')];
24065 }
24066 visitUnknown(value) {
24067 return [makeRelatedInformation(value.node, 'Unable to evaluate statically.')];
24068 }
24069 visitUnknownIdentifier(value) {
24070 return [makeRelatedInformation(value.node, 'Unknown reference.')];
24071 }
24072 visitUnsupportedSyntax(value) {
24073 return [makeRelatedInformation(value.node, 'This syntax is not supported.')];
24074 }
24075 /**
24076 * Determines whether the dynamic value reported for the node should be traced, i.e. if it is not
24077 * part of the container for which the most recent trace was created.
24078 */
24079 shouldTrace(node) {
24080 if (node === this.node) {
24081 // Do not include a dynamic value for the origin node, as the main diagnostic is already
24082 // reported on that node.
24083 return false;
24084 }
24085 const container = getContainerNode(node);
24086 if (container === this.currentContainerNode) {
24087 // The node is part of the same container as the previous trace entry, so this dynamic value
24088 // should not become part of the trace.
24089 return false;
24090 }
24091 this.currentContainerNode = container;
24092 return true;
24093 }
24094 }
24095 /**
24096 * Determines the closest parent node that is to be considered as container, which is used to reduce
24097 * the granularity of tracing the dynamic values to a single entry per container. Currently, full
24098 * statements and destructuring patterns are considered as container.
24099 */
24100 function getContainerNode(node) {
24101 let currentNode = node;
24102 while (currentNode !== undefined) {
24103 switch (currentNode.kind) {
24104 case ts$1.SyntaxKind.ExpressionStatement:
24105 case ts$1.SyntaxKind.VariableStatement:
24106 case ts$1.SyntaxKind.ReturnStatement:
24107 case ts$1.SyntaxKind.IfStatement:
24108 case ts$1.SyntaxKind.SwitchStatement:
24109 case ts$1.SyntaxKind.DoStatement:
24110 case ts$1.SyntaxKind.WhileStatement:
24111 case ts$1.SyntaxKind.ForStatement:
24112 case ts$1.SyntaxKind.ForInStatement:
24113 case ts$1.SyntaxKind.ForOfStatement:
24114 case ts$1.SyntaxKind.ContinueStatement:
24115 case ts$1.SyntaxKind.BreakStatement:
24116 case ts$1.SyntaxKind.ThrowStatement:
24117 case ts$1.SyntaxKind.ObjectBindingPattern:
24118 case ts$1.SyntaxKind.ArrayBindingPattern:
24119 return currentNode;
24120 }
24121 currentNode = currentNode.parent;
24122 }
24123 return node.getSourceFile();
24124 }
24125
24126 /**
24127 * @license
24128 * Copyright Google LLC All Rights Reserved.
24129 *
24130 * Use of this source code is governed by an MIT-style license that can be
24131 * found in the LICENSE file at https://angular.io/license
24132 */
24133 class ArraySliceBuiltinFn extends KnownFn {
24134 constructor(lhs) {
24135 super();
24136 this.lhs = lhs;
24137 }
24138 evaluate(node, args) {
24139 if (args.length === 0) {
24140 return this.lhs;
24141 }
24142 else {
24143 return DynamicValue.fromUnknown(node);
24144 }
24145 }
24146 }
24147 class ArrayConcatBuiltinFn extends KnownFn {
24148 constructor(lhs) {
24149 super();
24150 this.lhs = lhs;
24151 }
24152 evaluate(node, args) {
24153 const result = [...this.lhs];
24154 for (const arg of args) {
24155 if (arg instanceof DynamicValue) {
24156 result.push(DynamicValue.fromDynamicInput(node, arg));
24157 }
24158 else if (Array.isArray(arg)) {
24159 result.push(...arg);
24160 }
24161 else {
24162 result.push(arg);
24163 }
24164 }
24165 return result;
24166 }
24167 }
24168 class ObjectAssignBuiltinFn extends KnownFn {
24169 evaluate(node, args) {
24170 if (args.length === 0) {
24171 return DynamicValue.fromUnsupportedSyntax(node);
24172 }
24173 for (const arg of args) {
24174 if (arg instanceof DynamicValue) {
24175 return DynamicValue.fromDynamicInput(node, arg);
24176 }
24177 else if (!(arg instanceof Map)) {
24178 return DynamicValue.fromUnsupportedSyntax(node);
24179 }
24180 }
24181 const [target, ...sources] = args;
24182 for (const source of sources) {
24183 source.forEach((value, key) => target.set(key, value));
24184 }
24185 return target;
24186 }
24187 }
24188
24189 /**
24190 * @license
24191 * Copyright Google LLC All Rights Reserved.
24192 *
24193 * Use of this source code is governed by an MIT-style license that can be
24194 * found in the LICENSE file at https://angular.io/license
24195 */
24196 // Use the same implementation we use for `Object.assign()`. Semantically these functions are the
24197 // same, so they can also share the same evaluation code.
24198 class AssignHelperFn extends ObjectAssignBuiltinFn {
24199 }
24200 // Used for both `__spread()` and `__spreadArrays()` TypeScript helper functions.
24201 class SpreadHelperFn extends KnownFn {
24202 evaluate(node, args) {
24203 const result = [];
24204 for (const arg of args) {
24205 if (arg instanceof DynamicValue) {
24206 result.push(DynamicValue.fromDynamicInput(node, arg));
24207 }
24208 else if (Array.isArray(arg)) {
24209 result.push(...arg);
24210 }
24211 else {
24212 result.push(arg);
24213 }
24214 }
24215 return result;
24216 }
24217 }
24218
24219 /**
24220 * @license
24221 * Copyright Google LLC All Rights Reserved.
24222 *
24223 * Use of this source code is governed by an MIT-style license that can be
24224 * found in the LICENSE file at https://angular.io/license
24225 */
24226 /** Resolved value for the JavaScript global `Object` declaration. */
24227 const jsGlobalObjectValue = new Map([['assign', new ObjectAssignBuiltinFn()]]);
24228 /** Resolved value for the `__assign()` TypeScript helper declaration. */
24229 const assignTsHelperFn = new AssignHelperFn();
24230 /** Resolved value for the `__spread()` and `__spreadArrays()` TypeScript helper declarations. */
24231 const spreadTsHelperFn = new SpreadHelperFn();
24232 /**
24233 * Resolves the specified known declaration to a resolved value. For example,
24234 * the known JavaScript global `Object` will resolve to a `Map` that provides the
24235 * `assign` method with a built-in function. This enables evaluation of `Object.assign`.
24236 */
24237 function resolveKnownDeclaration(decl) {
24238 switch (decl) {
24239 case KnownDeclaration.JsGlobalObject:
24240 return jsGlobalObjectValue;
24241 case KnownDeclaration.TsHelperAssign:
24242 return assignTsHelperFn;
24243 case KnownDeclaration.TsHelperSpread:
24244 case KnownDeclaration.TsHelperSpreadArrays:
24245 return spreadTsHelperFn;
24246 default:
24247 throw new Error(`Cannot resolve known declaration. Received: ${KnownDeclaration[decl]}.`);
24248 }
24249 }
24250
24251 /**
24252 * @license
24253 * Copyright Google LLC All Rights Reserved.
24254 *
24255 * Use of this source code is governed by an MIT-style license that can be
24256 * found in the LICENSE file at https://angular.io/license
24257 */
24258 function literalBinaryOp(op) {
24259 return { op, literal: true };
24260 }
24261 function referenceBinaryOp(op) {
24262 return { op, literal: false };
24263 }
24264 const BINARY_OPERATORS = new Map([
24265 [ts$1.SyntaxKind.PlusToken, literalBinaryOp((a, b) => a + b)],
24266 [ts$1.SyntaxKind.MinusToken, literalBinaryOp((a, b) => a - b)],
24267 [ts$1.SyntaxKind.AsteriskToken, literalBinaryOp((a, b) => a * b)],
24268 [ts$1.SyntaxKind.SlashToken, literalBinaryOp((a, b) => a / b)],
24269 [ts$1.SyntaxKind.PercentToken, literalBinaryOp((a, b) => a % b)],
24270 [ts$1.SyntaxKind.AmpersandToken, literalBinaryOp((a, b) => a & b)],
24271 [ts$1.SyntaxKind.BarToken, literalBinaryOp((a, b) => a | b)],
24272 [ts$1.SyntaxKind.CaretToken, literalBinaryOp((a, b) => a ^ b)],
24273 [ts$1.SyntaxKind.LessThanToken, literalBinaryOp((a, b) => a < b)],
24274 [ts$1.SyntaxKind.LessThanEqualsToken, literalBinaryOp((a, b) => a <= b)],
24275 [ts$1.SyntaxKind.GreaterThanToken, literalBinaryOp((a, b) => a > b)],
24276 [ts$1.SyntaxKind.GreaterThanEqualsToken, literalBinaryOp((a, b) => a >= b)],
24277 [ts$1.SyntaxKind.EqualsEqualsToken, literalBinaryOp((a, b) => a == b)],
24278 [ts$1.SyntaxKind.EqualsEqualsEqualsToken, literalBinaryOp((a, b) => a === b)],
24279 [ts$1.SyntaxKind.ExclamationEqualsToken, literalBinaryOp((a, b) => a != b)],
24280 [ts$1.SyntaxKind.ExclamationEqualsEqualsToken, literalBinaryOp((a, b) => a !== b)],
24281 [ts$1.SyntaxKind.LessThanLessThanToken, literalBinaryOp((a, b) => a << b)],
24282 [ts$1.SyntaxKind.GreaterThanGreaterThanToken, literalBinaryOp((a, b) => a >> b)],
24283 [ts$1.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, literalBinaryOp((a, b) => a >>> b)],
24284 [ts$1.SyntaxKind.AsteriskAsteriskToken, literalBinaryOp((a, b) => Math.pow(a, b))],
24285 [ts$1.SyntaxKind.AmpersandAmpersandToken, referenceBinaryOp((a, b) => a && b)],
24286 [ts$1.SyntaxKind.BarBarToken, referenceBinaryOp((a, b) => a || b)]
24287 ]);
24288 const UNARY_OPERATORS = new Map([
24289 [ts$1.SyntaxKind.TildeToken, a => ~a], [ts$1.SyntaxKind.MinusToken, a => -a],
24290 [ts$1.SyntaxKind.PlusToken, a => +a], [ts$1.SyntaxKind.ExclamationToken, a => !a]
24291 ]);
24292 class StaticInterpreter {
24293 constructor(host, checker, dependencyTracker) {
24294 this.host = host;
24295 this.checker = checker;
24296 this.dependencyTracker = dependencyTracker;
24297 }
24298 visit(node, context) {
24299 return this.visitExpression(node, context);
24300 }
24301 visitExpression(node, context) {
24302 let result;
24303 if (node.kind === ts$1.SyntaxKind.TrueKeyword) {
24304 return true;
24305 }
24306 else if (node.kind === ts$1.SyntaxKind.FalseKeyword) {
24307 return false;
24308 }
24309 else if (node.kind === ts$1.SyntaxKind.NullKeyword) {
24310 return null;
24311 }
24312 else if (ts$1.isStringLiteral(node)) {
24313 return node.text;
24314 }
24315 else if (ts$1.isNoSubstitutionTemplateLiteral(node)) {
24316 return node.text;
24317 }
24318 else if (ts$1.isTemplateExpression(node)) {
24319 result = this.visitTemplateExpression(node, context);
24320 }
24321 else if (ts$1.isNumericLiteral(node)) {
24322 return parseFloat(node.text);
24323 }
24324 else if (ts$1.isObjectLiteralExpression(node)) {
24325 result = this.visitObjectLiteralExpression(node, context);
24326 }
24327 else if (ts$1.isIdentifier(node)) {
24328 result = this.visitIdentifier(node, context);
24329 }
24330 else if (ts$1.isPropertyAccessExpression(node)) {
24331 result = this.visitPropertyAccessExpression(node, context);
24332 }
24333 else if (ts$1.isCallExpression(node)) {
24334 result = this.visitCallExpression(node, context);
24335 }
24336 else if (ts$1.isConditionalExpression(node)) {
24337 result = this.visitConditionalExpression(node, context);
24338 }
24339 else if (ts$1.isPrefixUnaryExpression(node)) {
24340 result = this.visitPrefixUnaryExpression(node, context);
24341 }
24342 else if (ts$1.isBinaryExpression(node)) {
24343 result = this.visitBinaryExpression(node, context);
24344 }
24345 else if (ts$1.isArrayLiteralExpression(node)) {
24346 result = this.visitArrayLiteralExpression(node, context);
24347 }
24348 else if (ts$1.isParenthesizedExpression(node)) {
24349 result = this.visitParenthesizedExpression(node, context);
24350 }
24351 else if (ts$1.isElementAccessExpression(node)) {
24352 result = this.visitElementAccessExpression(node, context);
24353 }
24354 else if (ts$1.isAsExpression(node)) {
24355 result = this.visitExpression(node.expression, context);
24356 }
24357 else if (ts$1.isNonNullExpression(node)) {
24358 result = this.visitExpression(node.expression, context);
24359 }
24360 else if (this.host.isClass(node)) {
24361 result = this.visitDeclaration(node, context);
24362 }
24363 else {
24364 return DynamicValue.fromUnsupportedSyntax(node);
24365 }
24366 if (result instanceof DynamicValue && result.node !== node) {
24367 return DynamicValue.fromDynamicInput(node, result);
24368 }
24369 return result;
24370 }
24371 visitArrayLiteralExpression(node, context) {
24372 const array = [];
24373 for (let i = 0; i < node.elements.length; i++) {
24374 const element = node.elements[i];
24375 if (ts$1.isSpreadElement(element)) {
24376 array.push(...this.visitSpreadElement(element, context));
24377 }
24378 else {
24379 array.push(this.visitExpression(element, context));
24380 }
24381 }
24382 return array;
24383 }
24384 visitObjectLiteralExpression(node, context) {
24385 const map = new Map();
24386 for (let i = 0; i < node.properties.length; i++) {
24387 const property = node.properties[i];
24388 if (ts$1.isPropertyAssignment(property)) {
24389 const name = this.stringNameFromPropertyName(property.name, context);
24390 // Check whether the name can be determined statically.
24391 if (name === undefined) {
24392 return DynamicValue.fromDynamicInput(node, DynamicValue.fromDynamicString(property.name));
24393 }
24394 map.set(name, this.visitExpression(property.initializer, context));
24395 }
24396 else if (ts$1.isShorthandPropertyAssignment(property)) {
24397 const symbol = this.checker.getShorthandAssignmentValueSymbol(property);
24398 if (symbol === undefined || symbol.valueDeclaration === undefined) {
24399 map.set(property.name.text, DynamicValue.fromUnknown(property));
24400 }
24401 else {
24402 map.set(property.name.text, this.visitDeclaration(symbol.valueDeclaration, context));
24403 }
24404 }
24405 else if (ts$1.isSpreadAssignment(property)) {
24406 const spread = this.visitExpression(property.expression, context);
24407 if (spread instanceof DynamicValue) {
24408 return DynamicValue.fromDynamicInput(node, spread);
24409 }
24410 else if (spread instanceof Map) {
24411 spread.forEach((value, key) => map.set(key, value));
24412 }
24413 else if (spread instanceof ResolvedModule) {
24414 spread.getExports().forEach((value, key) => map.set(key, value));
24415 }
24416 else {
24417 return DynamicValue.fromDynamicInput(node, DynamicValue.fromInvalidExpressionType(property, spread));
24418 }
24419 }
24420 else {
24421 return DynamicValue.fromUnknown(node);
24422 }
24423 }
24424 return map;
24425 }
24426 visitTemplateExpression(node, context) {
24427 const pieces = [node.head.text];
24428 for (let i = 0; i < node.templateSpans.length; i++) {
24429 const span = node.templateSpans[i];
24430 const value = literal$1(this.visit(span.expression, context), () => DynamicValue.fromDynamicString(span.expression));
24431 if (value instanceof DynamicValue) {
24432 return DynamicValue.fromDynamicInput(node, value);
24433 }
24434 pieces.push(`${value}`, span.literal.text);
24435 }
24436 return pieces.join('');
24437 }
24438 visitIdentifier(node, context) {
24439 const decl = this.host.getDeclarationOfIdentifier(node);
24440 if (decl === null) {
24441 if (node.originalKeywordKind === ts$1.SyntaxKind.UndefinedKeyword) {
24442 return undefined;
24443 }
24444 else {
24445 // Check if the symbol here is imported.
24446 if (this.dependencyTracker !== null && this.host.getImportOfIdentifier(node) !== null) {
24447 // It was, but no declaration for the node could be found. This means that the dependency
24448 // graph for the current file cannot be properly updated to account for this (broken)
24449 // import. Instead, the originating file is reported as failing dependency analysis,
24450 // ensuring that future compilations will always attempt to re-resolve the previously
24451 // broken identifier.
24452 this.dependencyTracker.recordDependencyAnalysisFailure(context.originatingFile);
24453 }
24454 return DynamicValue.fromUnknownIdentifier(node);
24455 }
24456 }
24457 if (decl.known !== null) {
24458 return resolveKnownDeclaration(decl.known);
24459 }
24460 else if (isConcreteDeclaration(decl) && decl.identity !== null &&
24461 decl.identity.kind === 0 /* DownleveledEnum */) {
24462 return this.getResolvedEnum(decl.node, decl.identity.enumMembers, context);
24463 }
24464 const declContext = Object.assign(Object.assign({}, context), joinModuleContext(context, node, decl));
24465 const result = this.visitAmbiguousDeclaration(decl, declContext);
24466 if (result instanceof Reference$1) {
24467 // Only record identifiers to non-synthetic references. Synthetic references may not have the
24468 // same value at runtime as they do at compile time, so it's not legal to refer to them by the
24469 // identifier here.
24470 if (!result.synthetic) {
24471 result.addIdentifier(node);
24472 }
24473 }
24474 else if (result instanceof DynamicValue) {
24475 return DynamicValue.fromDynamicInput(node, result);
24476 }
24477 return result;
24478 }
24479 visitDeclaration(node, context) {
24480 if (this.dependencyTracker !== null) {
24481 this.dependencyTracker.addDependency(context.originatingFile, node.getSourceFile());
24482 }
24483 if (this.host.isClass(node)) {
24484 return this.getReference(node, context);
24485 }
24486 else if (ts$1.isVariableDeclaration(node)) {
24487 return this.visitVariableDeclaration(node, context);
24488 }
24489 else if (ts$1.isParameter(node) && context.scope.has(node)) {
24490 return context.scope.get(node);
24491 }
24492 else if (ts$1.isExportAssignment(node)) {
24493 return this.visitExpression(node.expression, context);
24494 }
24495 else if (ts$1.isEnumDeclaration(node)) {
24496 return this.visitEnumDeclaration(node, context);
24497 }
24498 else if (ts$1.isSourceFile(node)) {
24499 return this.visitSourceFile(node, context);
24500 }
24501 else if (ts$1.isBindingElement(node)) {
24502 return this.visitBindingElement(node, context);
24503 }
24504 else {
24505 return this.getReference(node, context);
24506 }
24507 }
24508 visitVariableDeclaration(node, context) {
24509 const value = this.host.getVariableValue(node);
24510 if (value !== null) {
24511 return this.visitExpression(value, context);
24512 }
24513 else if (isVariableDeclarationDeclared(node)) {
24514 return this.getReference(node, context);
24515 }
24516 else {
24517 return undefined;
24518 }
24519 }
24520 visitEnumDeclaration(node, context) {
24521 const enumRef = this.getReference(node, context);
24522 const map = new Map();
24523 node.members.forEach(member => {
24524 const name = this.stringNameFromPropertyName(member.name, context);
24525 if (name !== undefined) {
24526 const resolved = member.initializer && this.visit(member.initializer, context);
24527 map.set(name, new EnumValue(enumRef, name, resolved));
24528 }
24529 });
24530 return map;
24531 }
24532 visitElementAccessExpression(node, context) {
24533 const lhs = this.visitExpression(node.expression, context);
24534 if (lhs instanceof DynamicValue) {
24535 return DynamicValue.fromDynamicInput(node, lhs);
24536 }
24537 const rhs = this.visitExpression(node.argumentExpression, context);
24538 if (rhs instanceof DynamicValue) {
24539 return DynamicValue.fromDynamicInput(node, rhs);
24540 }
24541 if (typeof rhs !== 'string' && typeof rhs !== 'number') {
24542 return DynamicValue.fromInvalidExpressionType(node, rhs);
24543 }
24544 return this.accessHelper(node, lhs, rhs, context);
24545 }
24546 visitPropertyAccessExpression(node, context) {
24547 const lhs = this.visitExpression(node.expression, context);
24548 const rhs = node.name.text;
24549 // TODO: handle reference to class declaration.
24550 if (lhs instanceof DynamicValue) {
24551 return DynamicValue.fromDynamicInput(node, lhs);
24552 }
24553 return this.accessHelper(node, lhs, rhs, context);
24554 }
24555 visitSourceFile(node, context) {
24556 const declarations = this.host.getExportsOfModule(node);
24557 if (declarations === null) {
24558 return DynamicValue.fromUnknown(node);
24559 }
24560 return new ResolvedModule(declarations, decl => {
24561 if (decl.known !== null) {
24562 return resolveKnownDeclaration(decl.known);
24563 }
24564 const declContext = Object.assign(Object.assign({}, context), joinModuleContext(context, node, decl));
24565 // Visit both concrete and inline declarations.
24566 return this.visitAmbiguousDeclaration(decl, declContext);
24567 });
24568 }
24569 visitAmbiguousDeclaration(decl, declContext) {
24570 return decl.kind === 1 /* Inline */ && decl.implementation !== undefined &&
24571 !isDeclaration(decl.implementation) ?
24572 // Inline declarations whose `implementation` is a `ts.Expression` should be visited as
24573 // an expression.
24574 this.visitExpression(decl.implementation, declContext) :
24575 // Otherwise just visit the `node` as a declaration.
24576 this.visitDeclaration(decl.node, declContext);
24577 }
24578 accessHelper(node, lhs, rhs, context) {
24579 const strIndex = `${rhs}`;
24580 if (lhs instanceof Map) {
24581 if (lhs.has(strIndex)) {
24582 return lhs.get(strIndex);
24583 }
24584 else {
24585 return undefined;
24586 }
24587 }
24588 else if (lhs instanceof ResolvedModule) {
24589 return lhs.getExport(strIndex);
24590 }
24591 else if (Array.isArray(lhs)) {
24592 if (rhs === 'length') {
24593 return lhs.length;
24594 }
24595 else if (rhs === 'slice') {
24596 return new ArraySliceBuiltinFn(lhs);
24597 }
24598 else if (rhs === 'concat') {
24599 return new ArrayConcatBuiltinFn(lhs);
24600 }
24601 if (typeof rhs !== 'number' || !Number.isInteger(rhs)) {
24602 return DynamicValue.fromInvalidExpressionType(node, rhs);
24603 }
24604 return lhs[rhs];
24605 }
24606 else if (lhs instanceof Reference$1) {
24607 const ref = lhs.node;
24608 if (this.host.isClass(ref)) {
24609 const module = owningModule(context, lhs.bestGuessOwningModule);
24610 let value = undefined;
24611 const member = this.host.getMembersOfClass(ref).find(member => member.isStatic && member.name === strIndex);
24612 if (member !== undefined) {
24613 if (member.value !== null) {
24614 value = this.visitExpression(member.value, context);
24615 }
24616 else if (member.implementation !== null) {
24617 value = new Reference$1(member.implementation, module);
24618 }
24619 else if (member.node) {
24620 value = new Reference$1(member.node, module);
24621 }
24622 }
24623 return value;
24624 }
24625 else if (isDeclaration(ref)) {
24626 return DynamicValue.fromDynamicInput(node, DynamicValue.fromExternalReference(ref, lhs));
24627 }
24628 }
24629 else if (lhs instanceof DynamicValue) {
24630 return DynamicValue.fromDynamicInput(node, lhs);
24631 }
24632 return DynamicValue.fromUnknown(node);
24633 }
24634 visitCallExpression(node, context) {
24635 const lhs = this.visitExpression(node.expression, context);
24636 if (lhs instanceof DynamicValue) {
24637 return DynamicValue.fromDynamicInput(node, lhs);
24638 }
24639 // If the call refers to a builtin function, attempt to evaluate the function.
24640 if (lhs instanceof KnownFn) {
24641 return lhs.evaluate(node, this.evaluateFunctionArguments(node, context));
24642 }
24643 if (!(lhs instanceof Reference$1)) {
24644 return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
24645 }
24646 const fn = this.host.getDefinitionOfFunction(lhs.node);
24647 if (fn === null) {
24648 return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
24649 }
24650 if (!isFunctionOrMethodReference(lhs)) {
24651 return DynamicValue.fromInvalidExpressionType(node.expression, lhs);
24652 }
24653 // If the function is foreign (declared through a d.ts file), attempt to resolve it with the
24654 // foreignFunctionResolver, if one is specified.
24655 if (fn.body === null) {
24656 let expr = null;
24657 if (context.foreignFunctionResolver) {
24658 expr = context.foreignFunctionResolver(lhs, node.arguments);
24659 }
24660 if (expr === null) {
24661 return DynamicValue.fromDynamicInput(node, DynamicValue.fromExternalReference(node.expression, lhs));
24662 }
24663 // If the function is declared in a different file, resolve the foreign function expression
24664 // using the absolute module name of that file (if any).
24665 if (lhs.bestGuessOwningModule !== null) {
24666 context = Object.assign(Object.assign({}, context), { absoluteModuleName: lhs.bestGuessOwningModule.specifier, resolutionContext: node.getSourceFile().fileName });
24667 }
24668 return this.visitFfrExpression(expr, context);
24669 }
24670 let res = this.visitFunctionBody(node, fn, context);
24671 // If the result of attempting to resolve the function body was a DynamicValue, attempt to use
24672 // the foreignFunctionResolver if one is present. This could still potentially yield a usable
24673 // value.
24674 if (res instanceof DynamicValue && context.foreignFunctionResolver !== undefined) {
24675 const ffrExpr = context.foreignFunctionResolver(lhs, node.arguments);
24676 if (ffrExpr !== null) {
24677 // The foreign function resolver was able to extract an expression from this function. See
24678 // if that expression leads to a non-dynamic result.
24679 const ffrRes = this.visitFfrExpression(ffrExpr, context);
24680 if (!(ffrRes instanceof DynamicValue)) {
24681 // FFR yielded an actual result that's not dynamic, so use that instead of the original
24682 // resolution.
24683 res = ffrRes;
24684 }
24685 }
24686 }
24687 return res;
24688 }
24689 /**
24690 * Visit an expression which was extracted from a foreign-function resolver.
24691 *
24692 * This will process the result and ensure it's correct for FFR-resolved values, including marking
24693 * `Reference`s as synthetic.
24694 */
24695 visitFfrExpression(expr, context) {
24696 const res = this.visitExpression(expr, context);
24697 if (res instanceof Reference$1) {
24698 // This Reference was created synthetically, via a foreign function resolver. The real
24699 // runtime value of the function expression may be different than the foreign function
24700 // resolved value, so mark the Reference as synthetic to avoid it being misinterpreted.
24701 res.synthetic = true;
24702 }
24703 return res;
24704 }
24705 visitFunctionBody(node, fn, context) {
24706 if (fn.body === null) {
24707 return DynamicValue.fromUnknown(node);
24708 }
24709 else if (fn.body.length !== 1 || !ts$1.isReturnStatement(fn.body[0])) {
24710 return DynamicValue.fromComplexFunctionCall(node, fn);
24711 }
24712 const ret = fn.body[0];
24713 const args = this.evaluateFunctionArguments(node, context);
24714 const newScope = new Map();
24715 const calleeContext = Object.assign(Object.assign({}, context), { scope: newScope });
24716 fn.parameters.forEach((param, index) => {
24717 let arg = args[index];
24718 if (param.node.dotDotDotToken !== undefined) {
24719 arg = args.slice(index);
24720 }
24721 if (arg === undefined && param.initializer !== null) {
24722 arg = this.visitExpression(param.initializer, calleeContext);
24723 }
24724 newScope.set(param.node, arg);
24725 });
24726 return ret.expression !== undefined ? this.visitExpression(ret.expression, calleeContext) :
24727 undefined;
24728 }
24729 visitConditionalExpression(node, context) {
24730 const condition = this.visitExpression(node.condition, context);
24731 if (condition instanceof DynamicValue) {
24732 return DynamicValue.fromDynamicInput(node, condition);
24733 }
24734 if (condition) {
24735 return this.visitExpression(node.whenTrue, context);
24736 }
24737 else {
24738 return this.visitExpression(node.whenFalse, context);
24739 }
24740 }
24741 visitPrefixUnaryExpression(node, context) {
24742 const operatorKind = node.operator;
24743 if (!UNARY_OPERATORS.has(operatorKind)) {
24744 return DynamicValue.fromUnsupportedSyntax(node);
24745 }
24746 const op = UNARY_OPERATORS.get(operatorKind);
24747 const value = this.visitExpression(node.operand, context);
24748 if (value instanceof DynamicValue) {
24749 return DynamicValue.fromDynamicInput(node, value);
24750 }
24751 else {
24752 return op(value);
24753 }
24754 }
24755 visitBinaryExpression(node, context) {
24756 const tokenKind = node.operatorToken.kind;
24757 if (!BINARY_OPERATORS.has(tokenKind)) {
24758 return DynamicValue.fromUnsupportedSyntax(node);
24759 }
24760 const opRecord = BINARY_OPERATORS.get(tokenKind);
24761 let lhs, rhs;
24762 if (opRecord.literal) {
24763 lhs = literal$1(this.visitExpression(node.left, context), value => DynamicValue.fromInvalidExpressionType(node.left, value));
24764 rhs = literal$1(this.visitExpression(node.right, context), value => DynamicValue.fromInvalidExpressionType(node.right, value));
24765 }
24766 else {
24767 lhs = this.visitExpression(node.left, context);
24768 rhs = this.visitExpression(node.right, context);
24769 }
24770 if (lhs instanceof DynamicValue) {
24771 return DynamicValue.fromDynamicInput(node, lhs);
24772 }
24773 else if (rhs instanceof DynamicValue) {
24774 return DynamicValue.fromDynamicInput(node, rhs);
24775 }
24776 else {
24777 return opRecord.op(lhs, rhs);
24778 }
24779 }
24780 visitParenthesizedExpression(node, context) {
24781 return this.visitExpression(node.expression, context);
24782 }
24783 evaluateFunctionArguments(node, context) {
24784 const args = [];
24785 for (const arg of node.arguments) {
24786 if (ts$1.isSpreadElement(arg)) {
24787 args.push(...this.visitSpreadElement(arg, context));
24788 }
24789 else {
24790 args.push(this.visitExpression(arg, context));
24791 }
24792 }
24793 return args;
24794 }
24795 visitSpreadElement(node, context) {
24796 const spread = this.visitExpression(node.expression, context);
24797 if (spread instanceof DynamicValue) {
24798 return [DynamicValue.fromDynamicInput(node, spread)];
24799 }
24800 else if (!Array.isArray(spread)) {
24801 return [DynamicValue.fromInvalidExpressionType(node, spread)];
24802 }
24803 else {
24804 return spread;
24805 }
24806 }
24807 visitBindingElement(node, context) {
24808 const path = [];
24809 let closestDeclaration = node;
24810 while (ts$1.isBindingElement(closestDeclaration) ||
24811 ts$1.isArrayBindingPattern(closestDeclaration) ||
24812 ts$1.isObjectBindingPattern(closestDeclaration)) {
24813 if (ts$1.isBindingElement(closestDeclaration)) {
24814 path.unshift(closestDeclaration);
24815 }
24816 closestDeclaration = closestDeclaration.parent;
24817 }
24818 if (!ts$1.isVariableDeclaration(closestDeclaration) ||
24819 closestDeclaration.initializer === undefined) {
24820 return DynamicValue.fromUnknown(node);
24821 }
24822 let value = this.visit(closestDeclaration.initializer, context);
24823 for (const element of path) {
24824 let key;
24825 if (ts$1.isArrayBindingPattern(element.parent)) {
24826 key = element.parent.elements.indexOf(element);
24827 }
24828 else {
24829 const name = element.propertyName || element.name;
24830 if (ts$1.isIdentifier(name)) {
24831 key = name.text;
24832 }
24833 else {
24834 return DynamicValue.fromUnknown(element);
24835 }
24836 }
24837 value = this.accessHelper(element, value, key, context);
24838 if (value instanceof DynamicValue) {
24839 return value;
24840 }
24841 }
24842 return value;
24843 }
24844 stringNameFromPropertyName(node, context) {
24845 if (ts$1.isIdentifier(node) || ts$1.isStringLiteral(node) || ts$1.isNumericLiteral(node)) {
24846 return node.text;
24847 }
24848 else if (ts$1.isComputedPropertyName(node)) {
24849 const literal = this.visitExpression(node.expression, context);
24850 return typeof literal === 'string' ? literal : undefined;
24851 }
24852 else {
24853 return undefined;
24854 }
24855 }
24856 getResolvedEnum(node, enumMembers, context) {
24857 const enumRef = this.getReference(node, context);
24858 const map = new Map();
24859 enumMembers.forEach(member => {
24860 const name = this.stringNameFromPropertyName(member.name, context);
24861 if (name !== undefined) {
24862 const resolved = this.visit(member.initializer, context);
24863 map.set(name, new EnumValue(enumRef, name, resolved));
24864 }
24865 });
24866 return map;
24867 }
24868 getReference(node, context) {
24869 return new Reference$1(node, owningModule(context));
24870 }
24871 }
24872 function isFunctionOrMethodReference(ref) {
24873 return ts$1.isFunctionDeclaration(ref.node) || ts$1.isMethodDeclaration(ref.node) ||
24874 ts$1.isFunctionExpression(ref.node);
24875 }
24876 function literal$1(value, reject) {
24877 if (value instanceof EnumValue) {
24878 value = value.resolved;
24879 }
24880 if (value instanceof DynamicValue || value === null || value === undefined ||
24881 typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
24882 return value;
24883 }
24884 return reject(value);
24885 }
24886 function isVariableDeclarationDeclared(node) {
24887 if (node.parent === undefined || !ts$1.isVariableDeclarationList(node.parent)) {
24888 return false;
24889 }
24890 const declList = node.parent;
24891 if (declList.parent === undefined || !ts$1.isVariableStatement(declList.parent)) {
24892 return false;
24893 }
24894 const varStmt = declList.parent;
24895 return varStmt.modifiers !== undefined &&
24896 varStmt.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.DeclareKeyword);
24897 }
24898 const EMPTY = {};
24899 function joinModuleContext(existing, node, decl) {
24900 if (decl.viaModule !== null && decl.viaModule !== existing.absoluteModuleName) {
24901 return {
24902 absoluteModuleName: decl.viaModule,
24903 resolutionContext: node.getSourceFile().fileName,
24904 };
24905 }
24906 else {
24907 return EMPTY;
24908 }
24909 }
24910 function owningModule(context, override = null) {
24911 let specifier = context.absoluteModuleName;
24912 if (override !== null) {
24913 specifier = override.specifier;
24914 }
24915 if (specifier !== null) {
24916 return {
24917 specifier,
24918 resolutionContext: context.resolutionContext,
24919 };
24920 }
24921 else {
24922 return null;
24923 }
24924 }
24925
24926 /**
24927 * @license
24928 * Copyright Google LLC All Rights Reserved.
24929 *
24930 * Use of this source code is governed by an MIT-style license that can be
24931 * found in the LICENSE file at https://angular.io/license
24932 */
24933 class PartialEvaluator {
24934 constructor(host, checker, dependencyTracker) {
24935 this.host = host;
24936 this.checker = checker;
24937 this.dependencyTracker = dependencyTracker;
24938 }
24939 evaluate(expr, foreignFunctionResolver) {
24940 const interpreter = new StaticInterpreter(this.host, this.checker, this.dependencyTracker);
24941 const sourceFile = expr.getSourceFile();
24942 return interpreter.visit(expr, {
24943 originatingFile: sourceFile,
24944 absoluteModuleName: null,
24945 resolutionContext: sourceFile.fileName,
24946 scope: new Map(),
24947 foreignFunctionResolver,
24948 });
24949 }
24950 }
24951
24952 /**
24953 * @license
24954 * Copyright Google LLC All Rights Reserved.
24955 *
24956 * Use of this source code is governed by an MIT-style license that can be
24957 * found in the LICENSE file at https://angular.io/license
24958 */
24959 /**
24960 * Specifies the compilation mode that is used for the compilation.
24961 */
24962 var CompilationMode;
24963 (function (CompilationMode) {
24964 /**
24965 * Generates fully AOT compiled code using Ivy instructions.
24966 */
24967 CompilationMode[CompilationMode["FULL"] = 0] = "FULL";
24968 /**
24969 * Generates code using a stable, but intermediate format suitable to be published to NPM.
24970 */
24971 CompilationMode[CompilationMode["PARTIAL"] = 1] = "PARTIAL";
24972 })(CompilationMode || (CompilationMode = {}));
24973 var HandlerPrecedence;
24974 (function (HandlerPrecedence) {
24975 /**
24976 * Handler with PRIMARY precedence cannot overlap - there can only be one on a given class.
24977 *
24978 * If more than one PRIMARY handler matches a class, an error is produced.
24979 */
24980 HandlerPrecedence[HandlerPrecedence["PRIMARY"] = 0] = "PRIMARY";
24981 /**
24982 * Handlers with SHARED precedence can match any class, possibly in addition to a single PRIMARY
24983 * handler.
24984 *
24985 * It is not an error for a class to have any number of SHARED handlers.
24986 */
24987 HandlerPrecedence[HandlerPrecedence["SHARED"] = 1] = "SHARED";
24988 /**
24989 * Handlers with WEAK precedence that match a class are ignored if any handlers with stronger
24990 * precedence match a class.
24991 */
24992 HandlerPrecedence[HandlerPrecedence["WEAK"] = 2] = "WEAK";
24993 })(HandlerPrecedence || (HandlerPrecedence = {}));
24994 /**
24995 * A set of options which can be passed to a `DecoratorHandler` by a consumer, to tailor the output
24996 * of compilation beyond the decorators themselves.
24997 */
24998 var HandlerFlags;
24999 (function (HandlerFlags) {
25000 /**
25001 * No flags set.
25002 */
25003 HandlerFlags[HandlerFlags["NONE"] = 0] = "NONE";
25004 /**
25005 * Indicates that this decorator is fully inherited from its parent at runtime. In addition to
25006 * normally inherited aspects such as inputs and queries, full inheritance applies to every aspect
25007 * of the component or directive, such as the template function itself.
25008 *
25009 * Its primary effect is to cause the `CopyDefinitionFeature` to be applied to the definition
25010 * being compiled. See that class for more information.
25011 */
25012 HandlerFlags[HandlerFlags["FULL_INHERITANCE"] = 1] = "FULL_INHERITANCE";
25013 })(HandlerFlags || (HandlerFlags = {}));
25014
25015 /**
25016 * @license
25017 * Copyright Google LLC All Rights Reserved.
25018 *
25019 * Use of this source code is governed by an MIT-style license that can be
25020 * found in the LICENSE file at https://angular.io/license
25021 */
25022 function aliasTransformFactory(exportStatements) {
25023 return (context) => {
25024 return (file) => {
25025 if (ts$1.isBundle(file) || !exportStatements.has(file.fileName)) {
25026 return file;
25027 }
25028 const statements = [...file.statements];
25029 exportStatements.get(file.fileName).forEach(([moduleName, symbolName], aliasName) => {
25030 const stmt = ts$1.createExportDeclaration(
25031 /* decorators */ undefined,
25032 /* modifiers */ undefined,
25033 /* exportClause */ ts$1.createNamedExports([ts$1.createExportSpecifier(
25034 /* propertyName */ symbolName,
25035 /* name */ aliasName)]),
25036 /* moduleSpecifier */ ts$1.createStringLiteral(moduleName));
25037 statements.push(stmt);
25038 });
25039 return ts$1.updateSourceFileNode(file, statements);
25040 };
25041 };
25042 }
25043
25044 /**
25045 * @license
25046 * Copyright Google LLC All Rights Reserved.
25047 *
25048 * Use of this source code is governed by an MIT-style license that can be
25049 * found in the LICENSE file at https://angular.io/license
25050 */
25051 var TraitState;
25052 (function (TraitState) {
25053 /**
25054 * Pending traits are freshly created and have never been analyzed.
25055 */
25056 TraitState[TraitState["Pending"] = 0] = "Pending";
25057 /**
25058 * Analyzed traits have successfully been analyzed, but are pending resolution.
25059 */
25060 TraitState[TraitState["Analyzed"] = 1] = "Analyzed";
25061 /**
25062 * Resolved traits have successfully been analyzed and resolved and are ready for compilation.
25063 */
25064 TraitState[TraitState["Resolved"] = 2] = "Resolved";
25065 /**
25066 * Skipped traits are no longer considered for compilation.
25067 */
25068 TraitState[TraitState["Skipped"] = 3] = "Skipped";
25069 })(TraitState || (TraitState = {}));
25070 /**
25071 * The value side of `Trait` exposes a helper to create a `Trait` in a pending state (by delegating
25072 * to `TraitImpl`).
25073 */
25074 const Trait = {
25075 pending: (handler, detected) => TraitImpl.pending(handler, detected),
25076 };
25077 /**
25078 * An implementation of the `Trait` type which transitions safely between the various
25079 * `TraitState`s.
25080 */
25081 class TraitImpl {
25082 constructor(handler, detected) {
25083 this.state = TraitState.Pending;
25084 this.analysis = null;
25085 this.resolution = null;
25086 this.analysisDiagnostics = null;
25087 this.resolveDiagnostics = null;
25088 this.handler = handler;
25089 this.detected = detected;
25090 }
25091 toAnalyzed(analysis, diagnostics) {
25092 // Only pending traits can be analyzed.
25093 this.assertTransitionLegal(TraitState.Pending, TraitState.Analyzed);
25094 this.analysis = analysis;
25095 this.analysisDiagnostics = diagnostics;
25096 this.state = TraitState.Analyzed;
25097 return this;
25098 }
25099 toResolved(resolution, diagnostics) {
25100 // Only analyzed traits can be resolved.
25101 this.assertTransitionLegal(TraitState.Analyzed, TraitState.Resolved);
25102 if (this.analysis === null) {
25103 throw new Error(`Cannot transition an Analyzed trait with a null analysis to Resolved`);
25104 }
25105 this.resolution = resolution;
25106 this.state = TraitState.Resolved;
25107 this.resolveDiagnostics = diagnostics;
25108 return this;
25109 }
25110 toSkipped() {
25111 // Only pending traits can be skipped.
25112 this.assertTransitionLegal(TraitState.Pending, TraitState.Skipped);
25113 this.state = TraitState.Skipped;
25114 return this;
25115 }
25116 /**
25117 * Verifies that the trait is currently in one of the `allowedState`s.
25118 *
25119 * If correctly used, the `Trait` type and transition methods prevent illegal transitions from
25120 * occurring. However, if a reference to the `TraitImpl` instance typed with the previous
25121 * interface is retained after calling one of its transition methods, it will allow for illegal
25122 * transitions to take place. Hence, this assertion provides a little extra runtime protection.
25123 */
25124 assertTransitionLegal(allowedState, transitionTo) {
25125 if (!(this.state === allowedState)) {
25126 throw new Error(`Assertion failure: cannot transition from ${TraitState[this.state]} to ${TraitState[transitionTo]}.`);
25127 }
25128 }
25129 /**
25130 * Construct a new `TraitImpl` in the pending state.
25131 */
25132 static pending(handler, detected) {
25133 return new TraitImpl(handler, detected);
25134 }
25135 }
25136
25137 /**
25138 * @license
25139 * Copyright Google LLC All Rights Reserved.
25140 *
25141 * Use of this source code is governed by an MIT-style license that can be
25142 * found in the LICENSE file at https://angular.io/license
25143 */
25144 /**
25145 * The heart of Angular compilation.
25146 *
25147 * The `TraitCompiler` is responsible for processing all classes in the program. Any time a
25148 * `DecoratorHandler` matches a class, a "trait" is created to represent that Angular aspect of the
25149 * class (such as the class having a component definition).
25150 *
25151 * The `TraitCompiler` transitions each trait through the various phases of compilation, culminating
25152 * in the production of `CompileResult`s instructing the compiler to apply various mutations to the
25153 * class (like adding fields or type declarations).
25154 */
25155 class TraitCompiler {
25156 constructor(handlers, reflector, perf, incrementalBuild, compileNonExportedClasses, compilationMode, dtsTransforms) {
25157 this.handlers = handlers;
25158 this.reflector = reflector;
25159 this.perf = perf;
25160 this.incrementalBuild = incrementalBuild;
25161 this.compileNonExportedClasses = compileNonExportedClasses;
25162 this.compilationMode = compilationMode;
25163 this.dtsTransforms = dtsTransforms;
25164 /**
25165 * Maps class declarations to their `ClassRecord`, which tracks the Ivy traits being applied to
25166 * those classes.
25167 */
25168 this.classes = new Map();
25169 /**
25170 * Maps source files to any class declaration(s) within them which have been discovered to contain
25171 * Ivy traits.
25172 */
25173 this.fileToClasses = new Map();
25174 this.reexportMap = new Map();
25175 this.handlersByName = new Map();
25176 for (const handler of handlers) {
25177 this.handlersByName.set(handler.name, handler);
25178 }
25179 }
25180 analyzeSync(sf) {
25181 this.analyze(sf, false);
25182 }
25183 analyzeAsync(sf) {
25184 return this.analyze(sf, true);
25185 }
25186 analyze(sf, preanalyze) {
25187 // We shouldn't analyze declaration files.
25188 if (sf.isDeclarationFile) {
25189 return undefined;
25190 }
25191 // analyze() really wants to return `Promise<void>|void`, but TypeScript cannot narrow a return
25192 // type of 'void', so `undefined` is used instead.
25193 const promises = [];
25194 const priorWork = this.incrementalBuild.priorWorkFor(sf);
25195 if (priorWork !== null) {
25196 for (const priorRecord of priorWork) {
25197 this.adopt(priorRecord);
25198 }
25199 // Skip the rest of analysis, as this file's prior traits are being reused.
25200 return;
25201 }
25202 const visit = (node) => {
25203 if (this.reflector.isClass(node)) {
25204 this.analyzeClass(node, preanalyze ? promises : null);
25205 }
25206 ts$1.forEachChild(node, visit);
25207 };
25208 visit(sf);
25209 if (preanalyze && promises.length > 0) {
25210 return Promise.all(promises).then(() => undefined);
25211 }
25212 else {
25213 return undefined;
25214 }
25215 }
25216 recordFor(clazz) {
25217 if (this.classes.has(clazz)) {
25218 return this.classes.get(clazz);
25219 }
25220 else {
25221 return null;
25222 }
25223 }
25224 recordsFor(sf) {
25225 if (!this.fileToClasses.has(sf)) {
25226 return null;
25227 }
25228 const records = [];
25229 for (const clazz of this.fileToClasses.get(sf)) {
25230 records.push(this.classes.get(clazz));
25231 }
25232 return records;
25233 }
25234 /**
25235 * Import a `ClassRecord` from a previous compilation.
25236 *
25237 * Traits from the `ClassRecord` have accurate metadata, but the `handler` is from the old program
25238 * and needs to be updated (matching is done by name). A new pending trait is created and then
25239 * transitioned to analyzed using the previous analysis. If the trait is in the errored state,
25240 * instead the errors are copied over.
25241 */
25242 adopt(priorRecord) {
25243 const record = {
25244 hasPrimaryHandler: priorRecord.hasPrimaryHandler,
25245 hasWeakHandlers: priorRecord.hasWeakHandlers,
25246 metaDiagnostics: priorRecord.metaDiagnostics,
25247 node: priorRecord.node,
25248 traits: [],
25249 };
25250 for (const priorTrait of priorRecord.traits) {
25251 const handler = this.handlersByName.get(priorTrait.handler.name);
25252 let trait = Trait.pending(handler, priorTrait.detected);
25253 if (priorTrait.state === TraitState.Analyzed || priorTrait.state === TraitState.Resolved) {
25254 trait = trait.toAnalyzed(priorTrait.analysis, priorTrait.analysisDiagnostics);
25255 if (trait.analysis !== null && trait.handler.register !== undefined) {
25256 trait.handler.register(record.node, trait.analysis);
25257 }
25258 }
25259 else if (priorTrait.state === TraitState.Skipped) {
25260 trait = trait.toSkipped();
25261 }
25262 record.traits.push(trait);
25263 }
25264 this.classes.set(record.node, record);
25265 const sf = record.node.getSourceFile();
25266 if (!this.fileToClasses.has(sf)) {
25267 this.fileToClasses.set(sf, new Set());
25268 }
25269 this.fileToClasses.get(sf).add(record.node);
25270 }
25271 scanClassForTraits(clazz) {
25272 if (!this.compileNonExportedClasses && !isExported(clazz)) {
25273 return null;
25274 }
25275 const decorators = this.reflector.getDecoratorsOfDeclaration(clazz);
25276 return this.detectTraits(clazz, decorators);
25277 }
25278 detectTraits(clazz, decorators) {
25279 let record = this.recordFor(clazz);
25280 let foundTraits = [];
25281 for (const handler of this.handlers) {
25282 const result = handler.detect(clazz, decorators);
25283 if (result === undefined) {
25284 continue;
25285 }
25286 const isPrimaryHandler = handler.precedence === HandlerPrecedence.PRIMARY;
25287 const isWeakHandler = handler.precedence === HandlerPrecedence.WEAK;
25288 const trait = Trait.pending(handler, result);
25289 foundTraits.push(trait);
25290 if (record === null) {
25291 // This is the first handler to match this class. This path is a fast path through which
25292 // most classes will flow.
25293 record = {
25294 node: clazz,
25295 traits: [trait],
25296 metaDiagnostics: null,
25297 hasPrimaryHandler: isPrimaryHandler,
25298 hasWeakHandlers: isWeakHandler,
25299 };
25300 this.classes.set(clazz, record);
25301 const sf = clazz.getSourceFile();
25302 if (!this.fileToClasses.has(sf)) {
25303 this.fileToClasses.set(sf, new Set());
25304 }
25305 this.fileToClasses.get(sf).add(clazz);
25306 }
25307 else {
25308 // This is at least the second handler to match this class. This is a slower path that some
25309 // classes will go through, which validates that the set of decorators applied to the class
25310 // is valid.
25311 // Validate according to rules as follows:
25312 //
25313 // * WEAK handlers are removed if a non-WEAK handler matches.
25314 // * Only one PRIMARY handler can match at a time. Any other PRIMARY handler matching a
25315 // class with an existing PRIMARY handler is an error.
25316 if (!isWeakHandler && record.hasWeakHandlers) {
25317 // The current handler is not a WEAK handler, but the class has other WEAK handlers.
25318 // Remove them.
25319 record.traits =
25320 record.traits.filter(field => field.handler.precedence !== HandlerPrecedence.WEAK);
25321 record.hasWeakHandlers = false;
25322 }
25323 else if (isWeakHandler && !record.hasWeakHandlers) {
25324 // The current handler is a WEAK handler, but the class has non-WEAK handlers already.
25325 // Drop the current one.
25326 continue;
25327 }
25328 if (isPrimaryHandler && record.hasPrimaryHandler) {
25329 // The class already has a PRIMARY handler, and another one just matched.
25330 record.metaDiagnostics = [{
25331 category: ts$1.DiagnosticCategory.Error,
25332 code: Number('-99' + ErrorCode.DECORATOR_COLLISION),
25333 file: getSourceFile(clazz),
25334 start: clazz.getStart(undefined, false),
25335 length: clazz.getWidth(),
25336 messageText: 'Two incompatible decorators on class',
25337 }];
25338 record.traits = foundTraits = [];
25339 break;
25340 }
25341 // Otherwise, it's safe to accept the multiple decorators here. Update some of the metadata
25342 // regarding this class.
25343 record.traits.push(trait);
25344 record.hasPrimaryHandler = record.hasPrimaryHandler || isPrimaryHandler;
25345 }
25346 }
25347 return foundTraits.length > 0 ? foundTraits : null;
25348 }
25349 analyzeClass(clazz, preanalyzeQueue) {
25350 const traits = this.scanClassForTraits(clazz);
25351 if (traits === null) {
25352 // There are no Ivy traits on the class, so it can safely be skipped.
25353 return;
25354 }
25355 for (const trait of traits) {
25356 const analyze = () => this.analyzeTrait(clazz, trait);
25357 let preanalysis = null;
25358 if (preanalyzeQueue !== null && trait.handler.preanalyze !== undefined) {
25359 // Attempt to run preanalysis. This could fail with a `FatalDiagnosticError`; catch it if it
25360 // does.
25361 try {
25362 preanalysis = trait.handler.preanalyze(clazz, trait.detected.metadata) || null;
25363 }
25364 catch (err) {
25365 if (err instanceof FatalDiagnosticError) {
25366 trait.toAnalyzed(null, [err.toDiagnostic()]);
25367 return;
25368 }
25369 else {
25370 throw err;
25371 }
25372 }
25373 }
25374 if (preanalysis !== null) {
25375 preanalyzeQueue.push(preanalysis.then(analyze));
25376 }
25377 else {
25378 analyze();
25379 }
25380 }
25381 }
25382 analyzeTrait(clazz, trait, flags) {
25383 var _a, _b;
25384 if (trait.state !== TraitState.Pending) {
25385 throw new Error(`Attempt to analyze trait of ${clazz.name.text} in state ${TraitState[trait.state]} (expected DETECTED)`);
25386 }
25387 // Attempt analysis. This could fail with a `FatalDiagnosticError`; catch it if it does.
25388 let result;
25389 try {
25390 result = trait.handler.analyze(clazz, trait.detected.metadata, flags);
25391 }
25392 catch (err) {
25393 if (err instanceof FatalDiagnosticError) {
25394 trait.toAnalyzed(null, [err.toDiagnostic()]);
25395 return;
25396 }
25397 else {
25398 throw err;
25399 }
25400 }
25401 if (result.analysis !== undefined && trait.handler.register !== undefined) {
25402 trait.handler.register(clazz, result.analysis);
25403 }
25404 trait = trait.toAnalyzed((_a = result.analysis) !== null && _a !== void 0 ? _a : null, (_b = result.diagnostics) !== null && _b !== void 0 ? _b : null);
25405 }
25406 resolve() {
25407 var _a, _b;
25408 const classes = Array.from(this.classes.keys());
25409 for (const clazz of classes) {
25410 const record = this.classes.get(clazz);
25411 for (let trait of record.traits) {
25412 const handler = trait.handler;
25413 switch (trait.state) {
25414 case TraitState.Skipped:
25415 continue;
25416 case TraitState.Pending:
25417 throw new Error(`Resolving a trait that hasn't been analyzed: ${clazz.name.text} / ${Object.getPrototypeOf(trait.handler).constructor.name}`);
25418 case TraitState.Resolved:
25419 throw new Error(`Resolving an already resolved trait`);
25420 }
25421 if (trait.analysis === null) {
25422 // No analysis results, cannot further process this trait.
25423 continue;
25424 }
25425 if (handler.resolve === undefined) {
25426 // No resolution of this trait needed - it's considered successful by default.
25427 trait = trait.toResolved(null, null);
25428 continue;
25429 }
25430 let result;
25431 try {
25432 result = handler.resolve(clazz, trait.analysis);
25433 }
25434 catch (err) {
25435 if (err instanceof FatalDiagnosticError) {
25436 trait = trait.toResolved(null, [err.toDiagnostic()]);
25437 continue;
25438 }
25439 else {
25440 throw err;
25441 }
25442 }
25443 trait = trait.toResolved((_a = result.data) !== null && _a !== void 0 ? _a : null, (_b = result.diagnostics) !== null && _b !== void 0 ? _b : null);
25444 if (result.reexports !== undefined) {
25445 const fileName = clazz.getSourceFile().fileName;
25446 if (!this.reexportMap.has(fileName)) {
25447 this.reexportMap.set(fileName, new Map());
25448 }
25449 const fileReexports = this.reexportMap.get(fileName);
25450 for (const reexport of result.reexports) {
25451 fileReexports.set(reexport.asAlias, [reexport.fromModule, reexport.symbolName]);
25452 }
25453 }
25454 }
25455 }
25456 }
25457 /**
25458 * Generate type-checking code into the `TypeCheckContext` for any components within the given
25459 * `ts.SourceFile`.
25460 */
25461 typeCheck(sf, ctx) {
25462 if (!this.fileToClasses.has(sf)) {
25463 return;
25464 }
25465 for (const clazz of this.fileToClasses.get(sf)) {
25466 const record = this.classes.get(clazz);
25467 for (const trait of record.traits) {
25468 if (trait.state !== TraitState.Resolved) {
25469 continue;
25470 }
25471 else if (trait.handler.typeCheck === undefined) {
25472 continue;
25473 }
25474 if (trait.resolution !== null) {
25475 trait.handler.typeCheck(ctx, clazz, trait.analysis, trait.resolution);
25476 }
25477 }
25478 }
25479 }
25480 index(ctx) {
25481 for (const clazz of this.classes.keys()) {
25482 const record = this.classes.get(clazz);
25483 for (const trait of record.traits) {
25484 if (trait.state !== TraitState.Resolved) {
25485 // Skip traits that haven't been resolved successfully.
25486 continue;
25487 }
25488 else if (trait.handler.index === undefined) {
25489 // Skip traits that don't affect indexing.
25490 continue;
25491 }
25492 if (trait.resolution !== null) {
25493 trait.handler.index(ctx, clazz, trait.analysis, trait.resolution);
25494 }
25495 }
25496 }
25497 }
25498 updateResources(clazz) {
25499 if (!this.reflector.isClass(clazz) || !this.classes.has(clazz)) {
25500 return;
25501 }
25502 const record = this.classes.get(clazz);
25503 for (const trait of record.traits) {
25504 if (trait.state !== TraitState.Resolved || trait.handler.updateResources === undefined) {
25505 continue;
25506 }
25507 trait.handler.updateResources(clazz, trait.analysis, trait.resolution);
25508 }
25509 }
25510 compile(clazz, constantPool) {
25511 const original = ts$1.getOriginalNode(clazz);
25512 if (!this.reflector.isClass(clazz) || !this.reflector.isClass(original) ||
25513 !this.classes.has(original)) {
25514 return null;
25515 }
25516 const record = this.classes.get(original);
25517 let res = [];
25518 for (const trait of record.traits) {
25519 if (trait.state !== TraitState.Resolved || trait.analysisDiagnostics !== null ||
25520 trait.resolveDiagnostics !== null) {
25521 // Cannot compile a trait that is not resolved, or had any errors in its declaration.
25522 continue;
25523 }
25524 const compileSpan = this.perf.start('compileClass', original);
25525 // `trait.resolution` is non-null asserted here because TypeScript does not recognize that
25526 // `Readonly<unknown>` is nullable (as `unknown` itself is nullable) due to the way that
25527 // `Readonly` works.
25528 let compileRes;
25529 if (this.compilationMode === CompilationMode.PARTIAL &&
25530 trait.handler.compilePartial !== undefined) {
25531 compileRes = trait.handler.compilePartial(clazz, trait.analysis, trait.resolution);
25532 }
25533 else {
25534 compileRes =
25535 trait.handler.compileFull(clazz, trait.analysis, trait.resolution, constantPool);
25536 }
25537 const compileMatchRes = compileRes;
25538 this.perf.stop(compileSpan);
25539 if (Array.isArray(compileMatchRes)) {
25540 for (const result of compileMatchRes) {
25541 if (!res.some(r => r.name === result.name)) {
25542 res.push(result);
25543 }
25544 }
25545 }
25546 else if (!res.some(result => result.name === compileMatchRes.name)) {
25547 res.push(compileMatchRes);
25548 }
25549 }
25550 // Look up the .d.ts transformer for the input file and record that at least one field was
25551 // generated, which will allow the .d.ts to be transformed later.
25552 this.dtsTransforms.getIvyDeclarationTransform(original.getSourceFile())
25553 .addFields(original, res);
25554 // Return the instruction to the transformer so the fields will be added.
25555 return res.length > 0 ? res : null;
25556 }
25557 decoratorsFor(node) {
25558 const original = ts$1.getOriginalNode(node);
25559 if (!this.reflector.isClass(original) || !this.classes.has(original)) {
25560 return [];
25561 }
25562 const record = this.classes.get(original);
25563 const decorators = [];
25564 for (const trait of record.traits) {
25565 if (trait.state !== TraitState.Resolved) {
25566 continue;
25567 }
25568 if (trait.detected.trigger !== null && ts$1.isDecorator(trait.detected.trigger)) {
25569 decorators.push(trait.detected.trigger);
25570 }
25571 }
25572 return decorators;
25573 }
25574 get diagnostics() {
25575 const diagnostics = [];
25576 for (const clazz of this.classes.keys()) {
25577 const record = this.classes.get(clazz);
25578 if (record.metaDiagnostics !== null) {
25579 diagnostics.push(...record.metaDiagnostics);
25580 }
25581 for (const trait of record.traits) {
25582 if ((trait.state === TraitState.Analyzed || trait.state === TraitState.Resolved) &&
25583 trait.analysisDiagnostics !== null) {
25584 diagnostics.push(...trait.analysisDiagnostics);
25585 }
25586 if (trait.state === TraitState.Resolved && trait.resolveDiagnostics !== null) {
25587 diagnostics.push(...trait.resolveDiagnostics);
25588 }
25589 }
25590 }
25591 return diagnostics;
25592 }
25593 get exportStatements() {
25594 return this.reexportMap;
25595 }
25596 }
25597
25598 /**
25599 * @license
25600 * Copyright Google LLC All Rights Reserved.
25601 *
25602 * Use of this source code is governed by an MIT-style license that can be
25603 * found in the LICENSE file at https://angular.io/license
25604 */
25605 /**
25606 * The current context of a translator visitor as it traverses the AST tree.
25607 *
25608 * It tracks whether we are in the process of outputting a statement or an expression.
25609 */
25610 class Context {
25611 constructor(isStatement) {
25612 this.isStatement = isStatement;
25613 }
25614 get withExpressionMode() {
25615 return this.isStatement ? new Context(false) : this;
25616 }
25617 get withStatementMode() {
25618 return !this.isStatement ? new Context(true) : this;
25619 }
25620 }
25621
25622 /**
25623 * @license
25624 * Copyright Google LLC All Rights Reserved.
25625 *
25626 * Use of this source code is governed by an MIT-style license that can be
25627 * found in the LICENSE file at https://angular.io/license
25628 */
25629 class ImportManager {
25630 constructor(rewriter = new NoopImportRewriter(), prefix = 'i') {
25631 this.rewriter = rewriter;
25632 this.prefix = prefix;
25633 this.specifierToIdentifier = new Map();
25634 this.nextIndex = 0;
25635 }
25636 generateNamespaceImport(moduleName) {
25637 if (!this.specifierToIdentifier.has(moduleName)) {
25638 this.specifierToIdentifier.set(moduleName, ts$1.createIdentifier(`${this.prefix}${this.nextIndex++}`));
25639 }
25640 return this.specifierToIdentifier.get(moduleName);
25641 }
25642 generateNamedImport(moduleName, originalSymbol) {
25643 // First, rewrite the symbol name.
25644 const symbol = this.rewriter.rewriteSymbol(originalSymbol, moduleName);
25645 // Ask the rewriter if this symbol should be imported at all. If not, it can be referenced
25646 // directly (moduleImport: null).
25647 if (!this.rewriter.shouldImportSymbol(symbol, moduleName)) {
25648 // The symbol should be referenced directly.
25649 return { moduleImport: null, symbol };
25650 }
25651 // If not, this symbol will be imported using a generated namespace import.
25652 const moduleImport = this.generateNamespaceImport(moduleName);
25653 return { moduleImport, symbol };
25654 }
25655 getAllImports(contextPath) {
25656 const imports = [];
25657 this.specifierToIdentifier.forEach((qualifier, specifier) => {
25658 specifier = this.rewriter.rewriteSpecifier(specifier, contextPath);
25659 imports.push({ specifier, qualifier: qualifier.text });
25660 });
25661 return imports;
25662 }
25663 }
25664
25665 /**
25666 * @license
25667 * Copyright Google LLC All Rights Reserved.
25668 *
25669 * Use of this source code is governed by an MIT-style license that can be
25670 * found in the LICENSE file at https://angular.io/license
25671 */
25672 const UNARY_OPERATORS$1 = new Map([
25673 [UnaryOperator.Minus, '-'],
25674 [UnaryOperator.Plus, '+'],
25675 ]);
25676 const BINARY_OPERATORS$1 = new Map([
25677 [BinaryOperator.And, '&&'],
25678 [BinaryOperator.Bigger, '>'],
25679 [BinaryOperator.BiggerEquals, '>='],
25680 [BinaryOperator.BitwiseAnd, '&'],
25681 [BinaryOperator.Divide, '/'],
25682 [BinaryOperator.Equals, '=='],
25683 [BinaryOperator.Identical, '==='],
25684 [BinaryOperator.Lower, '<'],
25685 [BinaryOperator.LowerEquals, '<='],
25686 [BinaryOperator.Minus, '-'],
25687 [BinaryOperator.Modulo, '%'],
25688 [BinaryOperator.Multiply, '*'],
25689 [BinaryOperator.NotEquals, '!='],
25690 [BinaryOperator.NotIdentical, '!=='],
25691 [BinaryOperator.Or, '||'],
25692 [BinaryOperator.Plus, '+'],
25693 ]);
25694 class ExpressionTranslatorVisitor {
25695 constructor(factory, imports, options) {
25696 this.factory = factory;
25697 this.imports = imports;
25698 this.downlevelTaggedTemplates = options.downlevelTaggedTemplates === true;
25699 this.downlevelVariableDeclarations = options.downlevelVariableDeclarations === true;
25700 this.recordWrappedNodeExpr = options.recordWrappedNodeExpr || (() => { });
25701 }
25702 visitDeclareVarStmt(stmt, context) {
25703 var _a;
25704 const varType = this.downlevelVariableDeclarations ?
25705 'var' :
25706 stmt.hasModifier(StmtModifier.Final) ? 'const' : 'let';
25707 return this.attachComments(this.factory.createVariableDeclaration(stmt.name, (_a = stmt.value) === null || _a === void 0 ? void 0 : _a.visitExpression(this, context.withExpressionMode), varType), stmt.leadingComments);
25708 }
25709 visitDeclareFunctionStmt(stmt, context) {
25710 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);
25711 }
25712 visitExpressionStmt(stmt, context) {
25713 return this.attachComments(this.factory.createExpressionStatement(stmt.expr.visitExpression(this, context.withStatementMode)), stmt.leadingComments);
25714 }
25715 visitReturnStmt(stmt, context) {
25716 return this.attachComments(this.factory.createReturnStatement(stmt.value.visitExpression(this, context.withExpressionMode)), stmt.leadingComments);
25717 }
25718 visitDeclareClassStmt(_stmt, _context) {
25719 throw new Error('Method not implemented.');
25720 }
25721 visitIfStmt(stmt, context) {
25722 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)) :
25723 null), stmt.leadingComments);
25724 }
25725 visitTryCatchStmt(_stmt, _context) {
25726 throw new Error('Method not implemented.');
25727 }
25728 visitThrowStmt(stmt, context) {
25729 return this.attachComments(this.factory.createThrowStatement(stmt.error.visitExpression(this, context.withExpressionMode)), stmt.leadingComments);
25730 }
25731 visitReadVarExpr(ast, _context) {
25732 const identifier = this.factory.createIdentifier(ast.name);
25733 this.setSourceMapRange(identifier, ast.sourceSpan);
25734 return identifier;
25735 }
25736 visitWriteVarExpr(expr, context) {
25737 const assignment = this.factory.createAssignment(this.setSourceMapRange(this.factory.createIdentifier(expr.name), expr.sourceSpan), expr.value.visitExpression(this, context));
25738 return context.isStatement ? assignment :
25739 this.factory.createParenthesizedExpression(assignment);
25740 }
25741 visitWriteKeyExpr(expr, context) {
25742 const exprContext = context.withExpressionMode;
25743 const target = this.factory.createElementAccess(expr.receiver.visitExpression(this, exprContext), expr.index.visitExpression(this, exprContext));
25744 const assignment = this.factory.createAssignment(target, expr.value.visitExpression(this, exprContext));
25745 return context.isStatement ? assignment :
25746 this.factory.createParenthesizedExpression(assignment);
25747 }
25748 visitWritePropExpr(expr, context) {
25749 const target = this.factory.createPropertyAccess(expr.receiver.visitExpression(this, context), expr.name);
25750 return this.factory.createAssignment(target, expr.value.visitExpression(this, context));
25751 }
25752 visitInvokeMethodExpr(ast, context) {
25753 const target = ast.receiver.visitExpression(this, context);
25754 return this.setSourceMapRange(this.factory.createCallExpression(ast.name !== null ? this.factory.createPropertyAccess(target, ast.name) : target, ast.args.map(arg => arg.visitExpression(this, context)),
25755 /* pure */ false), ast.sourceSpan);
25756 }
25757 visitInvokeFunctionExpr(ast, context) {
25758 return this.setSourceMapRange(this.factory.createCallExpression(ast.fn.visitExpression(this, context), ast.args.map(arg => arg.visitExpression(this, context)), ast.pure), ast.sourceSpan);
25759 }
25760 visitTaggedTemplateExpr(ast, context) {
25761 return this.setSourceMapRange(this.createTaggedTemplateExpression(ast.tag.visitExpression(this, context), {
25762 elements: ast.template.elements.map(e => {
25763 var _a;
25764 return createTemplateElement({
25765 cooked: e.text,
25766 raw: e.rawText,
25767 range: (_a = e.sourceSpan) !== null && _a !== void 0 ? _a : ast.sourceSpan,
25768 });
25769 }),
25770 expressions: ast.template.expressions.map(e => e.visitExpression(this, context))
25771 }), ast.sourceSpan);
25772 }
25773 visitInstantiateExpr(ast, context) {
25774 return this.factory.createNewExpression(ast.classExpr.visitExpression(this, context), ast.args.map(arg => arg.visitExpression(this, context)));
25775 }
25776 visitLiteralExpr(ast, _context) {
25777 return this.setSourceMapRange(this.factory.createLiteral(ast.value), ast.sourceSpan);
25778 }
25779 visitLocalizedString(ast, context) {
25780 // A `$localize` message consists of `messageParts` and `expressions`, which get interleaved
25781 // together. The interleaved pieces look like:
25782 // `[messagePart0, expression0, messagePart1, expression1, messagePart2]`
25783 //
25784 // Note that there is always a message part at the start and end, and so therefore
25785 // `messageParts.length === expressions.length + 1`.
25786 //
25787 // Each message part may be prefixed with "metadata", which is wrapped in colons (:) delimiters.
25788 // The metadata is attached to the first and subsequent message parts by calls to
25789 // `serializeI18nHead()` and `serializeI18nTemplatePart()` respectively.
25790 //
25791 // The first message part (i.e. `ast.messageParts[0]`) is used to initialize `messageParts`
25792 // array.
25793 const elements = [createTemplateElement(ast.serializeI18nHead())];
25794 const expressions = [];
25795 for (let i = 0; i < ast.expressions.length; i++) {
25796 const placeholder = this.setSourceMapRange(ast.expressions[i].visitExpression(this, context), ast.getPlaceholderSourceSpan(i));
25797 expressions.push(placeholder);
25798 elements.push(createTemplateElement(ast.serializeI18nTemplatePart(i + 1)));
25799 }
25800 const localizeTag = this.factory.createIdentifier('$localize');
25801 return this.setSourceMapRange(this.createTaggedTemplateExpression(localizeTag, { elements, expressions }), ast.sourceSpan);
25802 }
25803 createTaggedTemplateExpression(tag, template) {
25804 return this.downlevelTaggedTemplates ? this.createES5TaggedTemplateFunctionCall(tag, template) :
25805 this.factory.createTaggedTemplate(tag, template);
25806 }
25807 /**
25808 * Translate the tagged template literal into a call that is compatible with ES5, using the
25809 * imported `__makeTemplateObject` helper for ES5 formatted output.
25810 */
25811 createES5TaggedTemplateFunctionCall(tagHandler, { elements, expressions }) {
25812 // Ensure that the `__makeTemplateObject()` helper has been imported.
25813 const { moduleImport, symbol } = this.imports.generateNamedImport('tslib', '__makeTemplateObject');
25814 const __makeTemplateObjectHelper = (moduleImport === null) ?
25815 this.factory.createIdentifier(symbol) :
25816 this.factory.createPropertyAccess(moduleImport, symbol);
25817 // Collect up the cooked and raw strings into two separate arrays.
25818 const cooked = [];
25819 const raw = [];
25820 for (const element of elements) {
25821 cooked.push(this.factory.setSourceMapRange(this.factory.createLiteral(element.cooked), element.range));
25822 raw.push(this.factory.setSourceMapRange(this.factory.createLiteral(element.raw), element.range));
25823 }
25824 // Generate the helper call in the form: `__makeTemplateObject([cooked], [raw]);`
25825 const templateHelperCall = this.factory.createCallExpression(__makeTemplateObjectHelper, [this.factory.createArrayLiteral(cooked), this.factory.createArrayLiteral(raw)],
25826 /* pure */ false);
25827 // Finally create the tagged handler call in the form:
25828 // `tag(__makeTemplateObject([cooked], [raw]), ...expressions);`
25829 return this.factory.createCallExpression(tagHandler, [templateHelperCall, ...expressions],
25830 /* pure */ false);
25831 }
25832 visitExternalExpr(ast, _context) {
25833 if (ast.value.name === null) {
25834 if (ast.value.moduleName === null) {
25835 throw new Error('Invalid import without name nor moduleName');
25836 }
25837 return this.imports.generateNamespaceImport(ast.value.moduleName);
25838 }
25839 // If a moduleName is specified, this is a normal import. If there's no module name, it's a
25840 // reference to a global/ambient symbol.
25841 if (ast.value.moduleName !== null) {
25842 // This is a normal import. Find the imported module.
25843 const { moduleImport, symbol } = this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
25844 if (moduleImport === null) {
25845 // The symbol was ambient after all.
25846 return this.factory.createIdentifier(symbol);
25847 }
25848 else {
25849 return this.factory.createPropertyAccess(moduleImport, symbol);
25850 }
25851 }
25852 else {
25853 // The symbol is ambient, so just reference it.
25854 return this.factory.createIdentifier(ast.value.name);
25855 }
25856 }
25857 visitConditionalExpr(ast, context) {
25858 let cond = ast.condition.visitExpression(this, context);
25859 // Ordinarily the ternary operator is right-associative. The following are equivalent:
25860 // `a ? b : c ? d : e` => `a ? b : (c ? d : e)`
25861 //
25862 // However, occasionally Angular needs to produce a left-associative conditional, such as in
25863 // the case of a null-safe navigation production: `{{a?.b ? c : d}}`. This template produces
25864 // a ternary of the form:
25865 // `a == null ? null : rest of expression`
25866 // If the rest of the expression is also a ternary though, this would produce the form:
25867 // `a == null ? null : a.b ? c : d`
25868 // which, if left as right-associative, would be incorrectly associated as:
25869 // `a == null ? null : (a.b ? c : d)`
25870 //
25871 // In such cases, the left-associativity needs to be enforced with parentheses:
25872 // `(a == null ? null : a.b) ? c : d`
25873 //
25874 // Such parentheses could always be included in the condition (guaranteeing correct behavior) in
25875 // all cases, but this has a code size cost. Instead, parentheses are added only when a
25876 // conditional expression is directly used as the condition of another.
25877 //
25878 // TODO(alxhub): investigate better logic for precendence of conditional operators
25879 if (ast.condition instanceof ConditionalExpr) {
25880 // The condition of this ternary needs to be wrapped in parentheses to maintain
25881 // left-associativity.
25882 cond = this.factory.createParenthesizedExpression(cond);
25883 }
25884 return this.factory.createConditional(cond, ast.trueCase.visitExpression(this, context), ast.falseCase.visitExpression(this, context));
25885 }
25886 visitNotExpr(ast, context) {
25887 return this.factory.createUnaryExpression('!', ast.condition.visitExpression(this, context));
25888 }
25889 visitAssertNotNullExpr(ast, context) {
25890 return ast.condition.visitExpression(this, context);
25891 }
25892 visitCastExpr(ast, context) {
25893 return ast.value.visitExpression(this, context);
25894 }
25895 visitFunctionExpr(ast, context) {
25896 var _a;
25897 return this.factory.createFunctionExpression((_a = ast.name) !== null && _a !== void 0 ? _a : null, ast.params.map(param => param.name), this.factory.createBlock(this.visitStatements(ast.statements, context)));
25898 }
25899 visitBinaryOperatorExpr(ast, context) {
25900 if (!BINARY_OPERATORS$1.has(ast.operator)) {
25901 throw new Error(`Unknown binary operator: ${BinaryOperator[ast.operator]}`);
25902 }
25903 return this.factory.createBinaryExpression(ast.lhs.visitExpression(this, context), BINARY_OPERATORS$1.get(ast.operator), ast.rhs.visitExpression(this, context));
25904 }
25905 visitReadPropExpr(ast, context) {
25906 return this.factory.createPropertyAccess(ast.receiver.visitExpression(this, context), ast.name);
25907 }
25908 visitReadKeyExpr(ast, context) {
25909 return this.factory.createElementAccess(ast.receiver.visitExpression(this, context), ast.index.visitExpression(this, context));
25910 }
25911 visitLiteralArrayExpr(ast, context) {
25912 return this.factory.createArrayLiteral(ast.entries.map(expr => this.setSourceMapRange(expr.visitExpression(this, context), ast.sourceSpan)));
25913 }
25914 visitLiteralMapExpr(ast, context) {
25915 const properties = ast.entries.map(entry => {
25916 return {
25917 propertyName: entry.key,
25918 quoted: entry.quoted,
25919 value: entry.value.visitExpression(this, context)
25920 };
25921 });
25922 return this.setSourceMapRange(this.factory.createObjectLiteral(properties), ast.sourceSpan);
25923 }
25924 visitCommaExpr(ast, context) {
25925 throw new Error('Method not implemented.');
25926 }
25927 visitWrappedNodeExpr(ast, _context) {
25928 this.recordWrappedNodeExpr(ast.node);
25929 return ast.node;
25930 }
25931 visitTypeofExpr(ast, context) {
25932 return this.factory.createTypeOfExpression(ast.expr.visitExpression(this, context));
25933 }
25934 visitUnaryOperatorExpr(ast, context) {
25935 if (!UNARY_OPERATORS$1.has(ast.operator)) {
25936 throw new Error(`Unknown unary operator: ${UnaryOperator[ast.operator]}`);
25937 }
25938 return this.factory.createUnaryExpression(UNARY_OPERATORS$1.get(ast.operator), ast.expr.visitExpression(this, context));
25939 }
25940 visitStatements(statements, context) {
25941 return statements.map(stmt => stmt.visitStatement(this, context))
25942 .filter(stmt => stmt !== undefined);
25943 }
25944 setSourceMapRange(ast, span) {
25945 return this.factory.setSourceMapRange(ast, createRange(span));
25946 }
25947 attachComments(statement, leadingComments) {
25948 if (leadingComments !== undefined) {
25949 this.factory.attachComments(statement, leadingComments);
25950 }
25951 return statement;
25952 }
25953 }
25954 /**
25955 * Convert a cooked-raw string object into one that can be used by the AST factories.
25956 */
25957 function createTemplateElement({ cooked, raw, range }) {
25958 return { cooked, raw, range: createRange(range) };
25959 }
25960 /**
25961 * Convert an OutputAST source-span into a range that can be used by the AST factories.
25962 */
25963 function createRange(span) {
25964 if (span === null) {
25965 return null;
25966 }
25967 const { start, end } = span;
25968 const { url, content } = start.file;
25969 if (!url) {
25970 return null;
25971 }
25972 return {
25973 url,
25974 content,
25975 start: { offset: start.offset, line: start.line, column: start.col },
25976 end: { offset: end.offset, line: end.line, column: end.col },
25977 };
25978 }
25979
25980 /**
25981 * @license
25982 * Copyright Google LLC All Rights Reserved.
25983 *
25984 * Use of this source code is governed by an MIT-style license that can be
25985 * found in the LICENSE file at https://angular.io/license
25986 */
25987 function translateType(type, imports) {
25988 return type.visitType(new TypeTranslatorVisitor(imports), new Context(false));
25989 }
25990 class TypeTranslatorVisitor {
25991 constructor(imports) {
25992 this.imports = imports;
25993 }
25994 visitBuiltinType(type, context) {
25995 switch (type.name) {
25996 case BuiltinTypeName.Bool:
25997 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.BooleanKeyword);
25998 case BuiltinTypeName.Dynamic:
25999 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword);
26000 case BuiltinTypeName.Int:
26001 case BuiltinTypeName.Number:
26002 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.NumberKeyword);
26003 case BuiltinTypeName.String:
26004 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.StringKeyword);
26005 case BuiltinTypeName.None:
26006 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.NeverKeyword);
26007 default:
26008 throw new Error(`Unsupported builtin type: ${BuiltinTypeName[type.name]}`);
26009 }
26010 }
26011 visitExpressionType(type, context) {
26012 const typeNode = this.translateExpression(type.value, context);
26013 if (type.typeParams === null) {
26014 return typeNode;
26015 }
26016 if (!ts$1.isTypeReferenceNode(typeNode)) {
26017 throw new Error('An ExpressionType with type arguments must translate into a TypeReferenceNode');
26018 }
26019 else if (typeNode.typeArguments !== undefined) {
26020 throw new Error(`An ExpressionType with type arguments cannot have multiple levels of type arguments`);
26021 }
26022 const typeArgs = type.typeParams.map(param => this.translateType(param, context));
26023 return ts$1.createTypeReferenceNode(typeNode.typeName, typeArgs);
26024 }
26025 visitArrayType(type, context) {
26026 return ts$1.createArrayTypeNode(this.translateType(type.of, context));
26027 }
26028 visitMapType(type, context) {
26029 const parameter = ts$1.createParameter(undefined, undefined, undefined, 'key', undefined, ts$1.createKeywordTypeNode(ts$1.SyntaxKind.StringKeyword));
26030 const typeArgs = type.valueType !== null ?
26031 this.translateType(type.valueType, context) :
26032 ts$1.createKeywordTypeNode(ts$1.SyntaxKind.UnknownKeyword);
26033 const indexSignature = ts$1.createIndexSignature(undefined, undefined, [parameter], typeArgs);
26034 return ts$1.createTypeLiteralNode([indexSignature]);
26035 }
26036 visitReadVarExpr(ast, context) {
26037 if (ast.name === null) {
26038 throw new Error(`ReadVarExpr with no variable name in type`);
26039 }
26040 return ts$1.createTypeQueryNode(ts$1.createIdentifier(ast.name));
26041 }
26042 visitWriteVarExpr(expr, context) {
26043 throw new Error('Method not implemented.');
26044 }
26045 visitWriteKeyExpr(expr, context) {
26046 throw new Error('Method not implemented.');
26047 }
26048 visitWritePropExpr(expr, context) {
26049 throw new Error('Method not implemented.');
26050 }
26051 visitInvokeMethodExpr(ast, context) {
26052 throw new Error('Method not implemented.');
26053 }
26054 visitInvokeFunctionExpr(ast, context) {
26055 throw new Error('Method not implemented.');
26056 }
26057 visitTaggedTemplateExpr(ast, context) {
26058 throw new Error('Method not implemented.');
26059 }
26060 visitInstantiateExpr(ast, context) {
26061 throw new Error('Method not implemented.');
26062 }
26063 visitLiteralExpr(ast, context) {
26064 if (ast.value === null) {
26065 return ts$1.createLiteralTypeNode(ts$1.createNull());
26066 }
26067 else if (ast.value === undefined) {
26068 return ts$1.createKeywordTypeNode(ts$1.SyntaxKind.UndefinedKeyword);
26069 }
26070 else if (typeof ast.value === 'boolean') {
26071 return ts$1.createLiteralTypeNode(ts$1.createLiteral(ast.value));
26072 }
26073 else if (typeof ast.value === 'number') {
26074 return ts$1.createLiteralTypeNode(ts$1.createLiteral(ast.value));
26075 }
26076 else {
26077 return ts$1.createLiteralTypeNode(ts$1.createLiteral(ast.value));
26078 }
26079 }
26080 visitLocalizedString(ast, context) {
26081 throw new Error('Method not implemented.');
26082 }
26083 visitExternalExpr(ast, context) {
26084 if (ast.value.moduleName === null || ast.value.name === null) {
26085 throw new Error(`Import unknown module or symbol`);
26086 }
26087 const { moduleImport, symbol } = this.imports.generateNamedImport(ast.value.moduleName, ast.value.name);
26088 const symbolIdentifier = ts$1.createIdentifier(symbol);
26089 const typeName = moduleImport ? ts$1.createQualifiedName(moduleImport, symbolIdentifier) : symbolIdentifier;
26090 const typeArguments = ast.typeParams !== null ?
26091 ast.typeParams.map(type => this.translateType(type, context)) :
26092 undefined;
26093 return ts$1.createTypeReferenceNode(typeName, typeArguments);
26094 }
26095 visitConditionalExpr(ast, context) {
26096 throw new Error('Method not implemented.');
26097 }
26098 visitNotExpr(ast, context) {
26099 throw new Error('Method not implemented.');
26100 }
26101 visitAssertNotNullExpr(ast, context) {
26102 throw new Error('Method not implemented.');
26103 }
26104 visitCastExpr(ast, context) {
26105 throw new Error('Method not implemented.');
26106 }
26107 visitFunctionExpr(ast, context) {
26108 throw new Error('Method not implemented.');
26109 }
26110 visitUnaryOperatorExpr(ast, context) {
26111 throw new Error('Method not implemented.');
26112 }
26113 visitBinaryOperatorExpr(ast, context) {
26114 throw new Error('Method not implemented.');
26115 }
26116 visitReadPropExpr(ast, context) {
26117 throw new Error('Method not implemented.');
26118 }
26119 visitReadKeyExpr(ast, context) {
26120 throw new Error('Method not implemented.');
26121 }
26122 visitLiteralArrayExpr(ast, context) {
26123 const values = ast.entries.map(expr => this.translateExpression(expr, context));
26124 return ts$1.createTupleTypeNode(values);
26125 }
26126 visitLiteralMapExpr(ast, context) {
26127 const entries = ast.entries.map(entry => {
26128 const { key, quoted } = entry;
26129 const type = this.translateExpression(entry.value, context);
26130 return ts$1.createPropertySignature(
26131 /* modifiers */ undefined,
26132 /* name */ quoted ? ts$1.createStringLiteral(key) : key,
26133 /* questionToken */ undefined,
26134 /* type */ type,
26135 /* initializer */ undefined);
26136 });
26137 return ts$1.createTypeLiteralNode(entries);
26138 }
26139 visitCommaExpr(ast, context) {
26140 throw new Error('Method not implemented.');
26141 }
26142 visitWrappedNodeExpr(ast, context) {
26143 const node = ast.node;
26144 if (ts$1.isEntityName(node)) {
26145 return ts$1.createTypeReferenceNode(node, /* typeArguments */ undefined);
26146 }
26147 else if (ts$1.isTypeNode(node)) {
26148 return node;
26149 }
26150 else if (ts$1.isLiteralExpression(node)) {
26151 return ts$1.createLiteralTypeNode(node);
26152 }
26153 else {
26154 throw new Error(`Unsupported WrappedNodeExpr in TypeTranslatorVisitor: ${ts$1.SyntaxKind[node.kind]}`);
26155 }
26156 }
26157 visitTypeofExpr(ast, context) {
26158 const typeNode = this.translateExpression(ast.expr, context);
26159 if (!ts$1.isTypeReferenceNode(typeNode)) {
26160 throw new Error(`The target of a typeof expression must be a type reference, but it was
26161 ${ts$1.SyntaxKind[typeNode.kind]}`);
26162 }
26163 return ts$1.createTypeQueryNode(typeNode.typeName);
26164 }
26165 translateType(type, context) {
26166 const typeNode = type.visitType(this, context);
26167 if (!ts$1.isTypeNode(typeNode)) {
26168 throw new Error(`A Type must translate to a TypeNode, but was ${ts$1.SyntaxKind[typeNode.kind]}`);
26169 }
26170 return typeNode;
26171 }
26172 translateExpression(expr, context) {
26173 const typeNode = expr.visitExpression(this, context);
26174 if (!ts$1.isTypeNode(typeNode)) {
26175 throw new Error(`An Expression must translate to a TypeNode, but was ${ts$1.SyntaxKind[typeNode.kind]}`);
26176 }
26177 return typeNode;
26178 }
26179 }
26180
26181 /**
26182 * @license
26183 * Copyright Google LLC All Rights Reserved.
26184 *
26185 * Use of this source code is governed by an MIT-style license that can be
26186 * found in the LICENSE file at https://angular.io/license
26187 */
26188 const UNARY_OPERATORS$2 = {
26189 '+': ts$1.SyntaxKind.PlusToken,
26190 '-': ts$1.SyntaxKind.MinusToken,
26191 '!': ts$1.SyntaxKind.ExclamationToken,
26192 };
26193 const BINARY_OPERATORS$2 = {
26194 '&&': ts$1.SyntaxKind.AmpersandAmpersandToken,
26195 '>': ts$1.SyntaxKind.GreaterThanToken,
26196 '>=': ts$1.SyntaxKind.GreaterThanEqualsToken,
26197 '&': ts$1.SyntaxKind.AmpersandToken,
26198 '/': ts$1.SyntaxKind.SlashToken,
26199 '==': ts$1.SyntaxKind.EqualsEqualsToken,
26200 '===': ts$1.SyntaxKind.EqualsEqualsEqualsToken,
26201 '<': ts$1.SyntaxKind.LessThanToken,
26202 '<=': ts$1.SyntaxKind.LessThanEqualsToken,
26203 '-': ts$1.SyntaxKind.MinusToken,
26204 '%': ts$1.SyntaxKind.PercentToken,
26205 '*': ts$1.SyntaxKind.AsteriskToken,
26206 '!=': ts$1.SyntaxKind.ExclamationEqualsToken,
26207 '!==': ts$1.SyntaxKind.ExclamationEqualsEqualsToken,
26208 '||': ts$1.SyntaxKind.BarBarToken,
26209 '+': ts$1.SyntaxKind.PlusToken,
26210 };
26211 const VAR_TYPES = {
26212 'const': ts$1.NodeFlags.Const,
26213 'let': ts$1.NodeFlags.Let,
26214 'var': ts$1.NodeFlags.None,
26215 };
26216 /**
26217 * A TypeScript flavoured implementation of the AstFactory.
26218 */
26219 class TypeScriptAstFactory {
26220 constructor() {
26221 this.externalSourceFiles = new Map();
26222 this.attachComments = attachComments;
26223 this.createArrayLiteral = ts$1.createArrayLiteral;
26224 this.createConditional = ts$1.createConditional;
26225 this.createElementAccess = ts$1.createElementAccess;
26226 this.createExpressionStatement = ts$1.createExpressionStatement;
26227 this.createIdentifier = ts$1.createIdentifier;
26228 this.createParenthesizedExpression = ts$1.createParen;
26229 this.createPropertyAccess = ts$1.createPropertyAccess;
26230 this.createThrowStatement = ts$1.createThrow;
26231 this.createTypeOfExpression = ts$1.createTypeOf;
26232 }
26233 createAssignment(target, value) {
26234 return ts$1.createBinary(target, ts$1.SyntaxKind.EqualsToken, value);
26235 }
26236 createBinaryExpression(leftOperand, operator, rightOperand) {
26237 return ts$1.createBinary(leftOperand, BINARY_OPERATORS$2[operator], rightOperand);
26238 }
26239 createBlock(body) {
26240 return ts$1.createBlock(body);
26241 }
26242 createCallExpression(callee, args, pure) {
26243 const call = ts$1.createCall(callee, undefined, args);
26244 if (pure) {
26245 ts$1.addSyntheticLeadingComment(call, ts$1.SyntaxKind.MultiLineCommentTrivia, '@__PURE__', /* trailing newline */ false);
26246 }
26247 return call;
26248 }
26249 createFunctionDeclaration(functionName, parameters, body) {
26250 if (!ts$1.isBlock(body)) {
26251 throw new Error(`Invalid syntax, expected a block, but got ${ts$1.SyntaxKind[body.kind]}.`);
26252 }
26253 return ts$1.createFunctionDeclaration(undefined, undefined, undefined, functionName, undefined, parameters.map(param => ts$1.createParameter(undefined, undefined, undefined, param)), undefined, body);
26254 }
26255 createFunctionExpression(functionName, parameters, body) {
26256 if (!ts$1.isBlock(body)) {
26257 throw new Error(`Invalid syntax, expected a block, but got ${ts$1.SyntaxKind[body.kind]}.`);
26258 }
26259 return ts$1.createFunctionExpression(undefined, undefined, functionName !== null && functionName !== void 0 ? functionName : undefined, undefined, parameters.map(param => ts$1.createParameter(undefined, undefined, undefined, param)), undefined, body);
26260 }
26261 createIfStatement(condition, thenStatement, elseStatement) {
26262 return ts$1.createIf(condition, thenStatement, elseStatement !== null && elseStatement !== void 0 ? elseStatement : undefined);
26263 }
26264 createLiteral(value) {
26265 if (value === undefined) {
26266 return ts$1.createIdentifier('undefined');
26267 }
26268 else if (value === null) {
26269 return ts$1.createNull();
26270 }
26271 else {
26272 return ts$1.createLiteral(value);
26273 }
26274 }
26275 createNewExpression(expression, args) {
26276 return ts$1.createNew(expression, undefined, args);
26277 }
26278 createObjectLiteral(properties) {
26279 return ts$1.createObjectLiteral(properties.map(prop => ts$1.createPropertyAssignment(prop.quoted ? ts$1.createLiteral(prop.propertyName) :
26280 ts$1.createIdentifier(prop.propertyName), prop.value)));
26281 }
26282 createReturnStatement(expression) {
26283 return ts$1.createReturn(expression !== null && expression !== void 0 ? expression : undefined);
26284 }
26285 createTaggedTemplate(tag, template) {
26286 let templateLiteral;
26287 const length = template.elements.length;
26288 const head = template.elements[0];
26289 if (length === 1) {
26290 templateLiteral = ts$1.createNoSubstitutionTemplateLiteral(head.cooked, head.raw);
26291 }
26292 else {
26293 const spans = [];
26294 // Create the middle parts
26295 for (let i = 1; i < length - 1; i++) {
26296 const { cooked, raw, range } = template.elements[i];
26297 const middle = createTemplateMiddle(cooked, raw);
26298 if (range !== null) {
26299 this.setSourceMapRange(middle, range);
26300 }
26301 spans.push(ts$1.createTemplateSpan(template.expressions[i - 1], middle));
26302 }
26303 // Create the tail part
26304 const resolvedExpression = template.expressions[length - 2];
26305 const templatePart = template.elements[length - 1];
26306 const templateTail = createTemplateTail(templatePart.cooked, templatePart.raw);
26307 if (templatePart.range !== null) {
26308 this.setSourceMapRange(templateTail, templatePart.range);
26309 }
26310 spans.push(ts$1.createTemplateSpan(resolvedExpression, templateTail));
26311 // Put it all together
26312 templateLiteral =
26313 ts$1.createTemplateExpression(ts$1.createTemplateHead(head.cooked, head.raw), spans);
26314 }
26315 if (head.range !== null) {
26316 this.setSourceMapRange(templateLiteral, head.range);
26317 }
26318 return ts$1.createTaggedTemplate(tag, templateLiteral);
26319 }
26320 createUnaryExpression(operator, operand) {
26321 return ts$1.createPrefix(UNARY_OPERATORS$2[operator], operand);
26322 }
26323 createVariableDeclaration(variableName, initializer, type) {
26324 return ts$1.createVariableStatement(undefined, ts$1.createVariableDeclarationList([ts$1.createVariableDeclaration(variableName, undefined, initializer !== null && initializer !== void 0 ? initializer : undefined)], VAR_TYPES[type]));
26325 }
26326 setSourceMapRange(node, sourceMapRange) {
26327 if (sourceMapRange === null) {
26328 return node;
26329 }
26330 const url = sourceMapRange.url;
26331 if (!this.externalSourceFiles.has(url)) {
26332 this.externalSourceFiles.set(url, ts$1.createSourceMapSource(url, sourceMapRange.content, pos => pos));
26333 }
26334 const source = this.externalSourceFiles.get(url);
26335 ts$1.setSourceMapRange(node, { pos: sourceMapRange.start.offset, end: sourceMapRange.end.offset, source });
26336 return node;
26337 }
26338 }
26339 // HACK: Use this in place of `ts.createTemplateMiddle()`.
26340 // Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed.
26341 function createTemplateMiddle(cooked, raw) {
26342 const node = ts$1.createTemplateHead(cooked, raw);
26343 node.kind = ts$1.SyntaxKind.TemplateMiddle;
26344 return node;
26345 }
26346 // HACK: Use this in place of `ts.createTemplateTail()`.
26347 // Revert once https://github.com/microsoft/TypeScript/issues/35374 is fixed.
26348 function createTemplateTail(cooked, raw) {
26349 const node = ts$1.createTemplateHead(cooked, raw);
26350 node.kind = ts$1.SyntaxKind.TemplateTail;
26351 return node;
26352 }
26353 /**
26354 * Attach the given `leadingComments` to the `statement` node.
26355 *
26356 * @param statement The statement that will have comments attached.
26357 * @param leadingComments The comments to attach to the statement.
26358 */
26359 function attachComments(statement, leadingComments) {
26360 for (const comment of leadingComments) {
26361 const commentKind = comment.multiline ? ts$1.SyntaxKind.MultiLineCommentTrivia :
26362 ts$1.SyntaxKind.SingleLineCommentTrivia;
26363 if (comment.multiline) {
26364 ts$1.addSyntheticLeadingComment(statement, commentKind, comment.toString(), comment.trailingNewline);
26365 }
26366 else {
26367 for (const line of comment.toString().split('\n')) {
26368 ts$1.addSyntheticLeadingComment(statement, commentKind, line, comment.trailingNewline);
26369 }
26370 }
26371 }
26372 }
26373
26374 /**
26375 * @license
26376 * Copyright Google LLC All Rights Reserved.
26377 *
26378 * Use of this source code is governed by an MIT-style license that can be
26379 * found in the LICENSE file at https://angular.io/license
26380 */
26381 function translateExpression(expression, imports, options = {}) {
26382 return expression.visitExpression(new ExpressionTranslatorVisitor(new TypeScriptAstFactory(), imports, options), new Context(false));
26383 }
26384 function translateStatement(statement, imports, options = {}) {
26385 return statement.visitStatement(new ExpressionTranslatorVisitor(new TypeScriptAstFactory(), imports, options), new Context(true));
26386 }
26387
26388 /**
26389 * @license
26390 * Copyright Google LLC All Rights Reserved.
26391 *
26392 * Use of this source code is governed by an MIT-style license that can be
26393 * found in the LICENSE file at https://angular.io/license
26394 */
26395 /**
26396 * Adds extra imports in the import manage for this source file, after the existing imports
26397 * and before the module body.
26398 * Can optionally add extra statements (e.g. new constants) before the body as well.
26399 */
26400 function addImports(importManager, sf, extraStatements = []) {
26401 // Generate the import statements to prepend.
26402 const addedImports = importManager.getAllImports(sf.fileName).map(i => {
26403 const qualifier = ts$1.createIdentifier(i.qualifier);
26404 const importClause = ts$1.createImportClause(
26405 /* name */ undefined,
26406 /* namedBindings */ ts$1.createNamespaceImport(qualifier));
26407 return ts$1.createImportDeclaration(
26408 /* decorators */ undefined,
26409 /* modifiers */ undefined,
26410 /* importClause */ importClause,
26411 /* moduleSpecifier */ ts$1.createLiteral(i.specifier));
26412 });
26413 // Filter out the existing imports and the source file body. All new statements
26414 // will be inserted between them.
26415 const existingImports = sf.statements.filter(stmt => isImportStatement(stmt));
26416 const body = sf.statements.filter(stmt => !isImportStatement(stmt));
26417 // Prepend imports if needed.
26418 if (addedImports.length > 0) {
26419 // If we prepend imports, we also prepend NotEmittedStatement to use it as an anchor
26420 // for @fileoverview Closure annotation. If there is no @fileoverview annotations, this
26421 // statement would be a noop.
26422 const fileoverviewAnchorStmt = ts$1.createNotEmittedStatement(sf);
26423 return ts$1.updateSourceFileNode(sf, ts$1.createNodeArray([
26424 fileoverviewAnchorStmt, ...existingImports, ...addedImports, ...extraStatements, ...body
26425 ]));
26426 }
26427 return sf;
26428 }
26429 function isImportStatement(stmt) {
26430 return ts$1.isImportDeclaration(stmt) || ts$1.isImportEqualsDeclaration(stmt) ||
26431 ts$1.isNamespaceImport(stmt);
26432 }
26433
26434 /**
26435 * @license
26436 * Copyright Google LLC All Rights Reserved.
26437 *
26438 * Use of this source code is governed by an MIT-style license that can be
26439 * found in the LICENSE file at https://angular.io/license
26440 */
26441 /**
26442 * Keeps track of `DtsTransform`s per source file, so that it is known which source files need to
26443 * have their declaration file transformed.
26444 */
26445 class DtsTransformRegistry {
26446 constructor() {
26447 this.ivyDeclarationTransforms = new Map();
26448 this.returnTypeTransforms = new Map();
26449 }
26450 getIvyDeclarationTransform(sf) {
26451 if (!this.ivyDeclarationTransforms.has(sf)) {
26452 this.ivyDeclarationTransforms.set(sf, new IvyDeclarationDtsTransform());
26453 }
26454 return this.ivyDeclarationTransforms.get(sf);
26455 }
26456 getReturnTypeTransform(sf) {
26457 if (!this.returnTypeTransforms.has(sf)) {
26458 this.returnTypeTransforms.set(sf, new ReturnTypeTransform());
26459 }
26460 return this.returnTypeTransforms.get(sf);
26461 }
26462 /**
26463 * Gets the dts transforms to be applied for the given source file, or `null` if no transform is
26464 * necessary.
26465 */
26466 getAllTransforms(sf) {
26467 // No need to transform if it's not a declarations file, or if no changes have been requested
26468 // to the input file. Due to the way TypeScript afterDeclarations transformers work, the
26469 // `ts.SourceFile` path is the same as the original .ts. The only way we know it's actually a
26470 // declaration file is via the `isDeclarationFile` property.
26471 if (!sf.isDeclarationFile) {
26472 return null;
26473 }
26474 const originalSf = ts$1.getOriginalNode(sf);
26475 let transforms = null;
26476 if (this.ivyDeclarationTransforms.has(originalSf)) {
26477 transforms = [];
26478 transforms.push(this.ivyDeclarationTransforms.get(originalSf));
26479 }
26480 if (this.returnTypeTransforms.has(originalSf)) {
26481 transforms = transforms || [];
26482 transforms.push(this.returnTypeTransforms.get(originalSf));
26483 }
26484 return transforms;
26485 }
26486 }
26487 function declarationTransformFactory(transformRegistry, importRewriter, importPrefix) {
26488 return (context) => {
26489 const transformer = new DtsTransformer(context, importRewriter, importPrefix);
26490 return (fileOrBundle) => {
26491 if (ts$1.isBundle(fileOrBundle)) {
26492 // Only attempt to transform source files.
26493 return fileOrBundle;
26494 }
26495 const transforms = transformRegistry.getAllTransforms(fileOrBundle);
26496 if (transforms === null) {
26497 return fileOrBundle;
26498 }
26499 return transformer.transform(fileOrBundle, transforms);
26500 };
26501 };
26502 }
26503 /**
26504 * Processes .d.ts file text and adds static field declarations, with types.
26505 */
26506 class DtsTransformer {
26507 constructor(ctx, importRewriter, importPrefix) {
26508 this.ctx = ctx;
26509 this.importRewriter = importRewriter;
26510 this.importPrefix = importPrefix;
26511 }
26512 /**
26513 * Transform the declaration file and add any declarations which were recorded.
26514 */
26515 transform(sf, transforms) {
26516 const imports = new ImportManager(this.importRewriter, this.importPrefix);
26517 const visitor = (node) => {
26518 if (ts$1.isClassDeclaration(node)) {
26519 return this.transformClassDeclaration(node, transforms, imports);
26520 }
26521 else if (ts$1.isFunctionDeclaration(node)) {
26522 return this.transformFunctionDeclaration(node, transforms, imports);
26523 }
26524 else {
26525 // Otherwise return node as is.
26526 return ts$1.visitEachChild(node, visitor, this.ctx);
26527 }
26528 };
26529 // Recursively scan through the AST and process all nodes as desired.
26530 sf = ts$1.visitNode(sf, visitor);
26531 // Add new imports for this file.
26532 return addImports(imports, sf);
26533 }
26534 transformClassDeclaration(clazz, transforms, imports) {
26535 let elements = clazz.members;
26536 let elementsChanged = false;
26537 for (const transform of transforms) {
26538 if (transform.transformClassElement !== undefined) {
26539 for (let i = 0; i < elements.length; i++) {
26540 const res = transform.transformClassElement(elements[i], imports);
26541 if (res !== elements[i]) {
26542 if (!elementsChanged) {
26543 elements = [...elements];
26544 elementsChanged = true;
26545 }
26546 elements[i] = res;
26547 }
26548 }
26549 }
26550 }
26551 let newClazz = clazz;
26552 for (const transform of transforms) {
26553 if (transform.transformClass !== undefined) {
26554 // If no DtsTransform has changed the class yet, then the (possibly mutated) elements have
26555 // not yet been incorporated. Otherwise, `newClazz.members` holds the latest class members.
26556 const inputMembers = (clazz === newClazz ? elements : newClazz.members);
26557 newClazz = transform.transformClass(newClazz, inputMembers, imports);
26558 }
26559 }
26560 // If some elements have been transformed but the class itself has not been transformed, create
26561 // an updated class declaration with the updated elements.
26562 if (elementsChanged && clazz === newClazz) {
26563 newClazz = ts$1.updateClassDeclaration(
26564 /* node */ clazz,
26565 /* decorators */ clazz.decorators,
26566 /* modifiers */ clazz.modifiers,
26567 /* name */ clazz.name,
26568 /* typeParameters */ clazz.typeParameters,
26569 /* heritageClauses */ clazz.heritageClauses,
26570 /* members */ elements);
26571 }
26572 return newClazz;
26573 }
26574 transformFunctionDeclaration(declaration, transforms, imports) {
26575 let newDecl = declaration;
26576 for (const transform of transforms) {
26577 if (transform.transformFunctionDeclaration !== undefined) {
26578 newDecl = transform.transformFunctionDeclaration(newDecl, imports);
26579 }
26580 }
26581 return newDecl;
26582 }
26583 }
26584 class IvyDeclarationDtsTransform {
26585 constructor() {
26586 this.declarationFields = new Map();
26587 }
26588 addFields(decl, fields) {
26589 this.declarationFields.set(decl, fields);
26590 }
26591 transformClass(clazz, members, imports) {
26592 const original = ts$1.getOriginalNode(clazz);
26593 if (!this.declarationFields.has(original)) {
26594 return clazz;
26595 }
26596 const fields = this.declarationFields.get(original);
26597 const newMembers = fields.map(decl => {
26598 const modifiers = [ts$1.createModifier(ts$1.SyntaxKind.StaticKeyword)];
26599 const typeRef = translateType(decl.type, imports);
26600 markForEmitAsSingleLine(typeRef);
26601 return ts$1.createProperty(
26602 /* decorators */ undefined,
26603 /* modifiers */ modifiers,
26604 /* name */ decl.name,
26605 /* questionOrExclamationToken */ undefined,
26606 /* type */ typeRef,
26607 /* initializer */ undefined);
26608 });
26609 return ts$1.updateClassDeclaration(
26610 /* node */ clazz,
26611 /* decorators */ clazz.decorators,
26612 /* modifiers */ clazz.modifiers,
26613 /* name */ clazz.name,
26614 /* typeParameters */ clazz.typeParameters,
26615 /* heritageClauses */ clazz.heritageClauses,
26616 /* members */ [...members, ...newMembers]);
26617 }
26618 }
26619 function markForEmitAsSingleLine(node) {
26620 ts$1.setEmitFlags(node, ts$1.EmitFlags.SingleLine);
26621 ts$1.forEachChild(node, markForEmitAsSingleLine);
26622 }
26623 class ReturnTypeTransform {
26624 constructor() {
26625 this.typeReplacements = new Map();
26626 }
26627 addTypeReplacement(declaration, type) {
26628 this.typeReplacements.set(declaration, type);
26629 }
26630 transformClassElement(element, imports) {
26631 if (ts$1.isMethodDeclaration(element)) {
26632 const original = ts$1.getOriginalNode(element, ts$1.isMethodDeclaration);
26633 if (!this.typeReplacements.has(original)) {
26634 return element;
26635 }
26636 const returnType = this.typeReplacements.get(original);
26637 const tsReturnType = translateType(returnType, imports);
26638 return ts$1.updateMethod(element, element.decorators, element.modifiers, element.asteriskToken, element.name, element.questionToken, element.typeParameters, element.parameters, tsReturnType, element.body);
26639 }
26640 return element;
26641 }
26642 transformFunctionDeclaration(element, imports) {
26643 const original = ts$1.getOriginalNode(element);
26644 if (!this.typeReplacements.has(original)) {
26645 return element;
26646 }
26647 const returnType = this.typeReplacements.get(original);
26648 const tsReturnType = translateType(returnType, imports);
26649 return ts$1.updateFunctionDeclaration(
26650 /* node */ element,
26651 /* decorators */ element.decorators,
26652 /* modifiers */ element.modifiers,
26653 /* asteriskToken */ element.asteriskToken,
26654 /* name */ element.name,
26655 /* typeParameters */ element.typeParameters,
26656 /* parameters */ element.parameters,
26657 /* type */ tsReturnType,
26658 /* body */ element.body);
26659 }
26660 }
26661
26662 /**
26663 * @license
26664 * Copyright Google LLC All Rights Reserved.
26665 *
26666 * Use of this source code is governed by an MIT-style license that can be
26667 * found in the LICENSE file at https://angular.io/license
26668 */
26669 /**
26670 * Visit a node with the given visitor and return a transformed copy.
26671 */
26672 function visit(node, visitor, context) {
26673 return visitor._visit(node, context);
26674 }
26675 /**
26676 * Abstract base class for visitors, which processes certain nodes specially to allow insertion
26677 * of other nodes before them.
26678 */
26679 class Visitor {
26680 constructor() {
26681 /**
26682 * Maps statements to an array of statements that should be inserted before them.
26683 */
26684 this._before = new Map();
26685 /**
26686 * Maps statements to an array of statements that should be inserted after them.
26687 */
26688 this._after = new Map();
26689 }
26690 /**
26691 * Visit a class declaration, returning at least the transformed declaration and optionally other
26692 * nodes to insert before the declaration.
26693 */
26694 visitClassDeclaration(node) {
26695 return { node };
26696 }
26697 _visitListEntryNode(node, visitor) {
26698 const result = visitor(node);
26699 if (result.before !== undefined) {
26700 // Record that some nodes should be inserted before the given declaration. The declaration's
26701 // parent's _visit call is responsible for performing this insertion.
26702 this._before.set(result.node, result.before);
26703 }
26704 if (result.after !== undefined) {
26705 // Same with nodes that should be inserted after.
26706 this._after.set(result.node, result.after);
26707 }
26708 return result.node;
26709 }
26710 /**
26711 * Visit types of nodes which don't have their own explicit visitor.
26712 */
26713 visitOtherNode(node) {
26714 return node;
26715 }
26716 /**
26717 * @internal
26718 */
26719 _visit(node, context) {
26720 // First, visit the node. visitedNode starts off as `null` but should be set after visiting
26721 // is completed.
26722 let visitedNode = null;
26723 node = ts$1.visitEachChild(node, child => this._visit(child, context), context);
26724 if (ts$1.isClassDeclaration(node)) {
26725 visitedNode =
26726 this._visitListEntryNode(node, (node) => this.visitClassDeclaration(node));
26727 }
26728 else {
26729 visitedNode = this.visitOtherNode(node);
26730 }
26731 // If the visited node has a `statements` array then process them, maybe replacing the visited
26732 // node and adding additional statements.
26733 if (hasStatements(visitedNode)) {
26734 visitedNode = this._maybeProcessStatements(visitedNode);
26735 }
26736 return visitedNode;
26737 }
26738 _maybeProcessStatements(node) {
26739 // Shortcut - if every statement doesn't require nodes to be prepended or appended,
26740 // this is a no-op.
26741 if (node.statements.every(stmt => !this._before.has(stmt) && !this._after.has(stmt))) {
26742 return node;
26743 }
26744 // There are statements to prepend, so clone the original node.
26745 const clone = ts$1.getMutableClone(node);
26746 // Build a new list of statements and patch it onto the clone.
26747 const newStatements = [];
26748 clone.statements.forEach(stmt => {
26749 if (this._before.has(stmt)) {
26750 newStatements.push(...this._before.get(stmt));
26751 this._before.delete(stmt);
26752 }
26753 newStatements.push(stmt);
26754 if (this._after.has(stmt)) {
26755 newStatements.push(...this._after.get(stmt));
26756 this._after.delete(stmt);
26757 }
26758 });
26759 clone.statements = ts$1.createNodeArray(newStatements, node.statements.hasTrailingComma);
26760 return clone;
26761 }
26762 }
26763 function hasStatements(node) {
26764 const block = node;
26765 return block.statements !== undefined && Array.isArray(block.statements);
26766 }
26767
26768 /**
26769 * @license
26770 * Copyright Google LLC All Rights Reserved.
26771 *
26772 * Use of this source code is governed by an MIT-style license that can be
26773 * found in the LICENSE file at https://angular.io/license
26774 */
26775 const NO_DECORATORS = new Set();
26776 const CLOSURE_FILE_OVERVIEW_REGEXP = /\s+@fileoverview\s+/i;
26777 function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportRecorder, isCore, isClosureCompilerEnabled) {
26778 const recordWrappedNodeExpr = createRecorderFn(defaultImportRecorder);
26779 return (context) => {
26780 return (file) => {
26781 return transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNodeExpr);
26782 };
26783 };
26784 }
26785 /**
26786 * Visits all classes, performs Ivy compilation where Angular decorators are present and collects
26787 * result in a Map that associates a ts.ClassDeclaration with Ivy compilation results. This visitor
26788 * does NOT perform any TS transformations.
26789 */
26790 class IvyCompilationVisitor extends Visitor {
26791 constructor(compilation, constantPool) {
26792 super();
26793 this.compilation = compilation;
26794 this.constantPool = constantPool;
26795 this.classCompilationMap = new Map();
26796 }
26797 visitClassDeclaration(node) {
26798 // Determine if this class has an Ivy field that needs to be added, and compile the field
26799 // to an expression if so.
26800 const result = this.compilation.compile(node, this.constantPool);
26801 if (result !== null) {
26802 this.classCompilationMap.set(node, result);
26803 }
26804 return { node };
26805 }
26806 }
26807 /**
26808 * Visits all classes and performs transformation of corresponding TS nodes based on the Ivy
26809 * compilation results (provided as an argument).
26810 */
26811 class IvyTransformationVisitor extends Visitor {
26812 constructor(compilation, classCompilationMap, reflector, importManager, recordWrappedNodeExpr, isClosureCompilerEnabled, isCore) {
26813 super();
26814 this.compilation = compilation;
26815 this.classCompilationMap = classCompilationMap;
26816 this.reflector = reflector;
26817 this.importManager = importManager;
26818 this.recordWrappedNodeExpr = recordWrappedNodeExpr;
26819 this.isClosureCompilerEnabled = isClosureCompilerEnabled;
26820 this.isCore = isCore;
26821 }
26822 visitClassDeclaration(node) {
26823 // If this class is not registered in the map, it means that it doesn't have Angular decorators,
26824 // thus no further processing is required.
26825 if (!this.classCompilationMap.has(node)) {
26826 return { node };
26827 }
26828 // There is at least one field to add.
26829 const statements = [];
26830 const members = [...node.members];
26831 for (const field of this.classCompilationMap.get(node)) {
26832 // Translate the initializer for the field into TS nodes.
26833 const exprNode = translateExpression(field.initializer, this.importManager, { recordWrappedNodeExpr: this.recordWrappedNodeExpr });
26834 // Create a static property declaration for the new field.
26835 const property = ts$1.createProperty(undefined, [ts$1.createToken(ts$1.SyntaxKind.StaticKeyword)], field.name, undefined, undefined, exprNode);
26836 if (this.isClosureCompilerEnabled) {
26837 // Closure compiler transforms the form `Service.ɵprov = X` into `Service$ɵprov = X`. To
26838 // prevent this transformation, such assignments need to be annotated with @nocollapse.
26839 // Note that tsickle is typically responsible for adding such annotations, however it
26840 // doesn't yet handle synthetic fields added during other transformations.
26841 ts$1.addSyntheticLeadingComment(property, ts$1.SyntaxKind.MultiLineCommentTrivia, '* @nocollapse ',
26842 /* hasTrailingNewLine */ false);
26843 }
26844 field.statements
26845 .map(stmt => translateStatement(stmt, this.importManager, { recordWrappedNodeExpr: this.recordWrappedNodeExpr }))
26846 .forEach(stmt => statements.push(stmt));
26847 members.push(property);
26848 }
26849 // Replace the class declaration with an updated version.
26850 node = ts$1.updateClassDeclaration(node,
26851 // Remove the decorator which triggered this compilation, leaving the others alone.
26852 maybeFilterDecorator(node.decorators, this.compilation.decoratorsFor(node)), node.modifiers, node.name, node.typeParameters, node.heritageClauses || [],
26853 // Map over the class members and remove any Angular decorators from them.
26854 members.map(member => this._stripAngularDecorators(member)));
26855 return { node, after: statements };
26856 }
26857 /**
26858 * Return all decorators on a `Declaration` which are from @angular/core, or an empty set if none
26859 * are.
26860 */
26861 _angularCoreDecorators(decl) {
26862 const decorators = this.reflector.getDecoratorsOfDeclaration(decl);
26863 if (decorators === null) {
26864 return NO_DECORATORS;
26865 }
26866 const coreDecorators = decorators.filter(dec => this.isCore || isFromAngularCore(dec))
26867 .map(dec => dec.node);
26868 if (coreDecorators.length > 0) {
26869 return new Set(coreDecorators);
26870 }
26871 else {
26872 return NO_DECORATORS;
26873 }
26874 }
26875 /**
26876 * Given a `ts.Node`, filter the decorators array and return a version containing only non-Angular
26877 * decorators.
26878 *
26879 * If all decorators are removed (or none existed in the first place), this method returns
26880 * `undefined`.
26881 */
26882 _nonCoreDecoratorsOnly(node) {
26883 // Shortcut if the node has no decorators.
26884 if (node.decorators === undefined) {
26885 return undefined;
26886 }
26887 // Build a Set of the decorators on this node from @angular/core.
26888 const coreDecorators = this._angularCoreDecorators(node);
26889 if (coreDecorators.size === node.decorators.length) {
26890 // If all decorators are to be removed, return `undefined`.
26891 return undefined;
26892 }
26893 else if (coreDecorators.size === 0) {
26894 // If no decorators need to be removed, return the original decorators array.
26895 return node.decorators;
26896 }
26897 // Filter out the core decorators.
26898 const filtered = node.decorators.filter(dec => !coreDecorators.has(dec));
26899 // If no decorators survive, return `undefined`. This can only happen if a core decorator is
26900 // repeated on the node.
26901 if (filtered.length === 0) {
26902 return undefined;
26903 }
26904 // Create a new `NodeArray` with the filtered decorators that sourcemaps back to the original.
26905 const array = ts$1.createNodeArray(filtered);
26906 array.pos = node.decorators.pos;
26907 array.end = node.decorators.end;
26908 return array;
26909 }
26910 /**
26911 * Remove Angular decorators from a `ts.Node` in a shallow manner.
26912 *
26913 * This will remove decorators from class elements (getters, setters, properties, methods) as well
26914 * as parameters of constructors.
26915 */
26916 _stripAngularDecorators(node) {
26917 if (ts$1.isParameter(node)) {
26918 // Strip decorators from parameters (probably of the constructor).
26919 node = ts$1.updateParameter(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.dotDotDotToken, node.name, node.questionToken, node.type, node.initializer);
26920 }
26921 else if (ts$1.isMethodDeclaration(node) && node.decorators !== undefined) {
26922 // Strip decorators of methods.
26923 node = ts$1.updateMethod(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, node.parameters, node.type, node.body);
26924 }
26925 else if (ts$1.isPropertyDeclaration(node) && node.decorators !== undefined) {
26926 // Strip decorators of properties.
26927 node = ts$1.updateProperty(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name, node.questionToken, node.type, node.initializer);
26928 }
26929 else if (ts$1.isGetAccessor(node)) {
26930 // Strip decorators of getters.
26931 node = ts$1.updateGetAccessor(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name, node.parameters, node.type, node.body);
26932 }
26933 else if (ts$1.isSetAccessor(node)) {
26934 // Strip decorators of setters.
26935 node = ts$1.updateSetAccessor(node, this._nonCoreDecoratorsOnly(node), node.modifiers, node.name, node.parameters, node.body);
26936 }
26937 else if (ts$1.isConstructorDeclaration(node)) {
26938 // For constructors, strip decorators of the parameters.
26939 const parameters = node.parameters.map(param => this._stripAngularDecorators(param));
26940 node =
26941 ts$1.updateConstructor(node, node.decorators, node.modifiers, parameters, node.body);
26942 }
26943 return node;
26944 }
26945 }
26946 /**
26947 * A transformer which operates on ts.SourceFiles and applies changes from an `IvyCompilation`.
26948 */
26949 function transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNodeExpr) {
26950 const constantPool = new ConstantPool(isClosureCompilerEnabled);
26951 const importManager = new ImportManager(importRewriter);
26952 // The transformation process consists of 2 steps:
26953 //
26954 // 1. Visit all classes, perform compilation and collect the results.
26955 // 2. Perform actual transformation of required TS nodes using compilation results from the first
26956 // step.
26957 //
26958 // This is needed to have all `o.Expression`s generated before any TS transforms happen. This
26959 // allows `ConstantPool` to properly identify expressions that can be shared across multiple
26960 // components declared in the same file.
26961 // Step 1. Go though all classes in AST, perform compilation and collect the results.
26962 const compilationVisitor = new IvyCompilationVisitor(compilation, constantPool);
26963 visit(file, compilationVisitor, context);
26964 // Step 2. Scan through the AST again and perform transformations based on Ivy compilation
26965 // results obtained at Step 1.
26966 const transformationVisitor = new IvyTransformationVisitor(compilation, compilationVisitor.classCompilationMap, reflector, importManager, recordWrappedNodeExpr, isClosureCompilerEnabled, isCore);
26967 let sf = visit(file, transformationVisitor, context);
26968 // Generate the constant statements first, as they may involve adding additional imports
26969 // to the ImportManager.
26970 const downlevelTranslatedCode = getLocalizeCompileTarget(context) < ts$1.ScriptTarget.ES2015;
26971 const constants = constantPool.statements.map(stmt => translateStatement(stmt, importManager, {
26972 recordWrappedNodeExpr,
26973 downlevelTaggedTemplates: downlevelTranslatedCode,
26974 downlevelVariableDeclarations: downlevelTranslatedCode,
26975 }));
26976 // Preserve @fileoverview comments required by Closure, since the location might change as a
26977 // result of adding extra imports and constant pool statements.
26978 const fileOverviewMeta = isClosureCompilerEnabled ? getFileOverviewComment(sf.statements) : null;
26979 // Add new imports for this file.
26980 sf = addImports(importManager, sf, constants);
26981 if (fileOverviewMeta !== null) {
26982 setFileOverviewComment(sf, fileOverviewMeta);
26983 }
26984 return sf;
26985 }
26986 /**
26987 * Compute the correct target output for `$localize` messages generated by Angular
26988 *
26989 * In some versions of TypeScript, the transformation of synthetic `$localize` tagged template
26990 * literals is broken. See https://github.com/microsoft/TypeScript/issues/38485
26991 *
26992 * Here we compute what the expected final output target of the compilation will
26993 * be so that we can generate ES5 compliant `$localize` calls instead of relying upon TS to do the
26994 * downleveling for us.
26995 */
26996 function getLocalizeCompileTarget(context) {
26997 const target = context.getCompilerOptions().target || ts$1.ScriptTarget.ES2015;
26998 return target !== ts$1.ScriptTarget.JSON ? target : ts$1.ScriptTarget.ES2015;
26999 }
27000 function getFileOverviewComment(statements) {
27001 if (statements.length > 0) {
27002 const host = statements[0];
27003 let trailing = false;
27004 let comments = ts$1.getSyntheticLeadingComments(host);
27005 // If @fileoverview tag is not found in source file, tsickle produces fake node with trailing
27006 // comment and inject it at the very beginning of the generated file. So we need to check for
27007 // leading as well as trailing comments.
27008 if (!comments || comments.length === 0) {
27009 trailing = true;
27010 comments = ts$1.getSyntheticTrailingComments(host);
27011 }
27012 if (comments && comments.length > 0 && CLOSURE_FILE_OVERVIEW_REGEXP.test(comments[0].text)) {
27013 return { comments, host, trailing };
27014 }
27015 }
27016 return null;
27017 }
27018 function setFileOverviewComment(sf, fileoverview) {
27019 const { comments, host, trailing } = fileoverview;
27020 // If host statement is no longer the first one, it means that extra statements were added at the
27021 // very beginning, so we need to relocate @fileoverview comment and cleanup the original statement
27022 // that hosted it.
27023 if (sf.statements.length > 0 && host !== sf.statements[0]) {
27024 if (trailing) {
27025 ts$1.setSyntheticTrailingComments(host, undefined);
27026 }
27027 else {
27028 ts$1.setSyntheticLeadingComments(host, undefined);
27029 }
27030 ts$1.setSyntheticLeadingComments(sf.statements[0], comments);
27031 }
27032 }
27033 function maybeFilterDecorator(decorators, toRemove) {
27034 if (decorators === undefined) {
27035 return undefined;
27036 }
27037 const filtered = decorators.filter(dec => toRemove.find(decToRemove => ts$1.getOriginalNode(dec) === decToRemove) === undefined);
27038 if (filtered.length === 0) {
27039 return undefined;
27040 }
27041 return ts$1.createNodeArray(filtered);
27042 }
27043 function isFromAngularCore(decorator) {
27044 return decorator.import !== null && decorator.import.from === '@angular/core';
27045 }
27046 function createRecorderFn(defaultImportRecorder) {
27047 return expr => {
27048 if (ts$1.isIdentifier(expr)) {
27049 defaultImportRecorder.recordUsedIdentifier(expr);
27050 }
27051 };
27052 }
27053
27054 /**
27055 * @license
27056 * Copyright Google LLC All Rights Reserved.
27057 *
27058 * Use of this source code is governed by an MIT-style license that can be
27059 * found in the LICENSE file at https://angular.io/license
27060 */
27061 let _tsSourceMapBug29300Fixed;
27062 /**
27063 * Test the current version of TypeScript to see if it has fixed the external SourceMap
27064 * file bug: https://github.com/Microsoft/TypeScript/issues/29300.
27065 *
27066 * The bug is fixed in TS 3.3+ but this check avoid us having to rely upon the version number,
27067 * and allows us to gracefully fail if the TS version still has the bug.
27068 *
27069 * We check for the bug by compiling a very small program `a;` and transforming it to `b;`,
27070 * where we map the new `b` identifier to an external source file, which has different lines to
27071 * the original source file. If the bug is fixed then the output SourceMap should contain
27072 * mappings that correspond ot the correct line/col pairs for this transformed node.
27073 *
27074 * @returns true if the bug is fixed.
27075 */
27076 function tsSourceMapBug29300Fixed() {
27077 if (_tsSourceMapBug29300Fixed === undefined) {
27078 let writtenFiles = {};
27079 const sourceFile = ts$1.createSourceFile('test.ts', 'a;', ts$1.ScriptTarget.ES2015, true, ts$1.ScriptKind.TS);
27080 const host = {
27081 getSourceFile() {
27082 return sourceFile;
27083 },
27084 fileExists() {
27085 return true;
27086 },
27087 readFile() {
27088 return '';
27089 },
27090 writeFile(fileName, data) {
27091 writtenFiles[fileName] = data;
27092 },
27093 getDefaultLibFileName() {
27094 return '';
27095 },
27096 getCurrentDirectory() {
27097 return '';
27098 },
27099 getDirectories() {
27100 return [];
27101 },
27102 getCanonicalFileName() {
27103 return '';
27104 },
27105 useCaseSensitiveFileNames() {
27106 return true;
27107 },
27108 getNewLine() {
27109 return '\n';
27110 },
27111 };
27112 const transform = (context) => {
27113 return (node) => ts$1.visitNode(node, visitor);
27114 function visitor(node) {
27115 if (ts$1.isIdentifier(node) && node.text === 'a') {
27116 const newNode = ts$1.createIdentifier('b');
27117 ts$1.setSourceMapRange(newNode, {
27118 pos: 16,
27119 end: 16,
27120 source: ts$1.createSourceMapSource('test.html', 'abc\ndef\nghi\njkl\nmno\npqr')
27121 });
27122 return newNode;
27123 }
27124 return ts$1.visitEachChild(node, visitor, context);
27125 }
27126 };
27127 const program = ts$1.createProgram(['test.ts'], { sourceMap: true }, host);
27128 program.emit(sourceFile, undefined, undefined, undefined, { after: [transform] });
27129 // The first two mappings in the source map should look like:
27130 // [0,1,4,0] col 0 => source file 1, row 4, column 0)
27131 // [1,0,0,0] col 1 => source file 1, row 4, column 0)
27132 _tsSourceMapBug29300Fixed = /ACIA,CAAA/.test(writtenFiles['test.js.map']);
27133 }
27134 return _tsSourceMapBug29300Fixed;
27135 }
27136
27137 /**
27138 * @license
27139 * Copyright Google LLC All Rights Reserved.
27140 *
27141 * Use of this source code is governed by an MIT-style license that can be
27142 * found in the LICENSE file at https://angular.io/license
27143 */
27144 function getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore) {
27145 const deps = [];
27146 const errors = [];
27147 let ctorParams = reflector.getConstructorParameters(clazz);
27148 if (ctorParams === null) {
27149 if (reflector.hasBaseClass(clazz)) {
27150 return null;
27151 }
27152 else {
27153 ctorParams = [];
27154 }
27155 }
27156 ctorParams.forEach((param, idx) => {
27157 let token = valueReferenceToExpression(param.typeValueReference, defaultImportRecorder);
27158 let attribute = null;
27159 let optional = false, self = false, skipSelf = false, host = false;
27160 let resolved = R3ResolvedDependencyType.Token;
27161 (param.decorators || []).filter(dec => isCore || isAngularCore(dec)).forEach(dec => {
27162 const name = isCore || dec.import === null ? dec.name : dec.import.name;
27163 if (name === 'Inject') {
27164 if (dec.args === null || dec.args.length !== 1) {
27165 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(dec), `Unexpected number of arguments to @Inject().`);
27166 }
27167 token = new WrappedNodeExpr(dec.args[0]);
27168 }
27169 else if (name === 'Optional') {
27170 optional = true;
27171 }
27172 else if (name === 'SkipSelf') {
27173 skipSelf = true;
27174 }
27175 else if (name === 'Self') {
27176 self = true;
27177 }
27178 else if (name === 'Host') {
27179 host = true;
27180 }
27181 else if (name === 'Attribute') {
27182 if (dec.args === null || dec.args.length !== 1) {
27183 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(dec), `Unexpected number of arguments to @Attribute().`);
27184 }
27185 const attributeName = dec.args[0];
27186 token = new WrappedNodeExpr(attributeName);
27187 if (ts$1.isStringLiteralLike(attributeName)) {
27188 attribute = new LiteralExpr(attributeName.text);
27189 }
27190 else {
27191 attribute = new WrappedNodeExpr(ts$1.createKeywordTypeNode(ts$1.SyntaxKind.UnknownKeyword));
27192 }
27193 resolved = R3ResolvedDependencyType.Attribute;
27194 }
27195 else {
27196 throw new FatalDiagnosticError(ErrorCode.DECORATOR_UNEXPECTED, Decorator.nodeForError(dec), `Unexpected decorator ${name} on parameter.`);
27197 }
27198 });
27199 if (token instanceof ExternalExpr && token.value.name === 'ChangeDetectorRef' &&
27200 token.value.moduleName === '@angular/core') {
27201 resolved = R3ResolvedDependencyType.ChangeDetectorRef;
27202 }
27203 if (token === null) {
27204 if (param.typeValueReference.kind !== 2 /* UNAVAILABLE */) {
27205 throw new Error('Illegal state: expected value reference to be unavailable if no token is present');
27206 }
27207 errors.push({
27208 index: idx,
27209 param,
27210 reason: param.typeValueReference.reason,
27211 });
27212 }
27213 else {
27214 deps.push({ token, attribute, optional, self, skipSelf, host, resolved });
27215 }
27216 });
27217 if (errors.length === 0) {
27218 return { deps };
27219 }
27220 else {
27221 return { deps: null, errors };
27222 }
27223 }
27224 function valueReferenceToExpression(valueRef, defaultImportRecorder) {
27225 if (valueRef.kind === 2 /* UNAVAILABLE */) {
27226 return null;
27227 }
27228 else if (valueRef.kind === 0 /* LOCAL */) {
27229 if (defaultImportRecorder !== null && valueRef.defaultImportStatement !== null &&
27230 ts$1.isIdentifier(valueRef.expression)) {
27231 defaultImportRecorder.recordImportedIdentifier(valueRef.expression, valueRef.defaultImportStatement);
27232 }
27233 return new WrappedNodeExpr(valueRef.expression);
27234 }
27235 else {
27236 let importExpr = new ExternalExpr({ moduleName: valueRef.moduleName, name: valueRef.importedName });
27237 if (valueRef.nestedPath !== null) {
27238 for (const property of valueRef.nestedPath) {
27239 importExpr = new ReadPropExpr(importExpr, property);
27240 }
27241 }
27242 return importExpr;
27243 }
27244 }
27245 /**
27246 * Convert `ConstructorDeps` into the `R3DependencyMetadata` array for those deps if they're valid,
27247 * or into an `'invalid'` signal if they're not.
27248 *
27249 * This is a companion function to `validateConstructorDependencies` which accepts invalid deps.
27250 */
27251 function unwrapConstructorDependencies(deps) {
27252 if (deps === null) {
27253 return null;
27254 }
27255 else if (deps.deps !== null) {
27256 // These constructor dependencies are valid.
27257 return deps.deps;
27258 }
27259 else {
27260 // These deps are invalid.
27261 return 'invalid';
27262 }
27263 }
27264 function getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore) {
27265 return validateConstructorDependencies(clazz, getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore));
27266 }
27267 /**
27268 * Validate that `ConstructorDeps` does not have any invalid dependencies and convert them into the
27269 * `R3DependencyMetadata` array if so, or raise a diagnostic if some deps are invalid.
27270 *
27271 * This is a companion function to `unwrapConstructorDependencies` which does not accept invalid
27272 * deps.
27273 */
27274 function validateConstructorDependencies(clazz, deps) {
27275 if (deps === null) {
27276 return null;
27277 }
27278 else if (deps.deps !== null) {
27279 return deps.deps;
27280 }
27281 else {
27282 // TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here.
27283 // There is at least one error.
27284 const error = deps.errors[0];
27285 throw createUnsuitableInjectionTokenError(clazz, error);
27286 }
27287 }
27288 /**
27289 * Creates a fatal error with diagnostic for an invalid injection token.
27290 * @param clazz The class for which the injection token was unavailable.
27291 * @param error The reason why no valid injection token is available.
27292 */
27293 function createUnsuitableInjectionTokenError(clazz, error) {
27294 const { param, index, reason } = error;
27295 let chainMessage = undefined;
27296 let hints = undefined;
27297 switch (reason.kind) {
27298 case 5 /* UNSUPPORTED */:
27299 chainMessage = 'Consider using the @Inject decorator to specify an injection token.';
27300 hints = [
27301 makeRelatedInformation(reason.typeNode, 'This type is not supported as injection token.'),
27302 ];
27303 break;
27304 case 1 /* NO_VALUE_DECLARATION */:
27305 chainMessage = 'Consider using the @Inject decorator to specify an injection token.';
27306 hints = [
27307 makeRelatedInformation(reason.typeNode, 'This type does not have a value, so it cannot be used as injection token.'),
27308 ];
27309 if (reason.decl !== null) {
27310 hints.push(makeRelatedInformation(reason.decl, 'The type is declared here.'));
27311 }
27312 break;
27313 case 2 /* TYPE_ONLY_IMPORT */:
27314 chainMessage =
27315 'Consider changing the type-only import to a regular import, or use the @Inject decorator to specify an injection token.';
27316 hints = [
27317 makeRelatedInformation(reason.typeNode, 'This type is imported using a type-only import, which prevents it from being usable as an injection token.'),
27318 makeRelatedInformation(reason.importClause, 'The type-only import occurs here.'),
27319 ];
27320 break;
27321 case 4 /* NAMESPACE */:
27322 chainMessage = 'Consider using the @Inject decorator to specify an injection token.';
27323 hints = [
27324 makeRelatedInformation(reason.typeNode, 'This type corresponds with a namespace, which cannot be used as injection token.'),
27325 makeRelatedInformation(reason.importClause, 'The namespace import occurs here.'),
27326 ];
27327 break;
27328 case 3 /* UNKNOWN_REFERENCE */:
27329 chainMessage = 'The type should reference a known declaration.';
27330 hints = [makeRelatedInformation(reason.typeNode, 'This type could not be resolved.')];
27331 break;
27332 case 0 /* MISSING_TYPE */:
27333 chainMessage =
27334 'Consider adding a type to the parameter or use the @Inject decorator to specify an injection token.';
27335 break;
27336 }
27337 const chain = {
27338 messageText: `No suitable injection token for parameter '${param.name || index}' of class '${clazz.name.text}'.`,
27339 category: ts$1.DiagnosticCategory.Error,
27340 code: 0,
27341 next: [{
27342 messageText: chainMessage,
27343 category: ts$1.DiagnosticCategory.Message,
27344 code: 0,
27345 }],
27346 };
27347 return new FatalDiagnosticError(ErrorCode.PARAM_MISSING_TOKEN, param.nameNode, chain, hints);
27348 }
27349 function toR3Reference(valueRef, typeRef, valueContext, typeContext, refEmitter) {
27350 const value = refEmitter.emit(valueRef, valueContext);
27351 const type = refEmitter.emit(typeRef, typeContext, ImportFlags.ForceNewImport | ImportFlags.AllowTypeImports);
27352 if (value === null || type === null) {
27353 throw new Error(`Could not refer to ${ts$1.SyntaxKind[valueRef.node.kind]}`);
27354 }
27355 return { value, type };
27356 }
27357 function isAngularCore(decorator) {
27358 return decorator.import !== null && decorator.import.from === '@angular/core';
27359 }
27360 function isAngularCoreReference(reference, symbolName) {
27361 return reference.ownedByModuleGuess === '@angular/core' && reference.debugName === symbolName;
27362 }
27363 function findAngularDecorator(decorators, name, isCore) {
27364 return decorators.find(decorator => isAngularDecorator(decorator, name, isCore));
27365 }
27366 function isAngularDecorator(decorator, name, isCore) {
27367 if (isCore) {
27368 return decorator.name === name;
27369 }
27370 else if (isAngularCore(decorator)) {
27371 return decorator.import.name === name;
27372 }
27373 return false;
27374 }
27375 /**
27376 * Unwrap a `ts.Expression`, removing outer type-casts or parentheses until the expression is in its
27377 * lowest level form.
27378 *
27379 * For example, the expression "(foo as Type)" unwraps to "foo".
27380 */
27381 function unwrapExpression(node) {
27382 while (ts$1.isAsExpression(node) || ts$1.isParenthesizedExpression(node)) {
27383 node = node.expression;
27384 }
27385 return node;
27386 }
27387 function expandForwardRef(arg) {
27388 arg = unwrapExpression(arg);
27389 if (!ts$1.isArrowFunction(arg) && !ts$1.isFunctionExpression(arg)) {
27390 return null;
27391 }
27392 const body = arg.body;
27393 // Either the body is a ts.Expression directly, or a block with a single return statement.
27394 if (ts$1.isBlock(body)) {
27395 // Block body - look for a single return statement.
27396 if (body.statements.length !== 1) {
27397 return null;
27398 }
27399 const stmt = body.statements[0];
27400 if (!ts$1.isReturnStatement(stmt) || stmt.expression === undefined) {
27401 return null;
27402 }
27403 return stmt.expression;
27404 }
27405 else {
27406 // Shorthand body - return as an expression.
27407 return body;
27408 }
27409 }
27410 /**
27411 * Possibly resolve a forwardRef() expression into the inner value.
27412 *
27413 * @param node the forwardRef() expression to resolve
27414 * @param reflector a ReflectionHost
27415 * @returns the resolved expression, if the original expression was a forwardRef(), or the original
27416 * expression otherwise
27417 */
27418 function unwrapForwardRef(node, reflector) {
27419 node = unwrapExpression(node);
27420 if (!ts$1.isCallExpression(node) || node.arguments.length !== 1) {
27421 return node;
27422 }
27423 const fn = ts$1.isPropertyAccessExpression(node.expression) ? node.expression.name : node.expression;
27424 if (!ts$1.isIdentifier(fn)) {
27425 return node;
27426 }
27427 const expr = expandForwardRef(node.arguments[0]);
27428 if (expr === null) {
27429 return node;
27430 }
27431 const imp = reflector.getImportOfIdentifier(fn);
27432 if (imp === null || imp.from !== '@angular/core' || imp.name !== 'forwardRef') {
27433 return node;
27434 }
27435 else {
27436 return expr;
27437 }
27438 }
27439 /**
27440 * A foreign function resolver for `staticallyResolve` which unwraps forwardRef() expressions.
27441 *
27442 * @param ref a Reference to the declaration of the function being called (which might be
27443 * forwardRef)
27444 * @param args the arguments to the invocation of the forwardRef expression
27445 * @returns an unwrapped argument if `ref` pointed to forwardRef, or null otherwise
27446 */
27447 function forwardRefResolver(ref, args) {
27448 if (!isAngularCoreReference(ref, 'forwardRef') || args.length !== 1) {
27449 return null;
27450 }
27451 return expandForwardRef(args[0]);
27452 }
27453 /**
27454 * Combines an array of resolver functions into a one.
27455 * @param resolvers Resolvers to be combined.
27456 */
27457 function combineResolvers(resolvers) {
27458 return (ref, args) => {
27459 for (const resolver of resolvers) {
27460 const resolved = resolver(ref, args);
27461 if (resolved !== null) {
27462 return resolved;
27463 }
27464 }
27465 return null;
27466 };
27467 }
27468 function isExpressionForwardReference(expr, context, contextSource) {
27469 if (isWrappedTsNodeExpr(expr)) {
27470 const node = ts$1.getOriginalNode(expr.node);
27471 return node.getSourceFile() === contextSource && context.pos < node.pos;
27472 }
27473 else {
27474 return false;
27475 }
27476 }
27477 function isWrappedTsNodeExpr(expr) {
27478 return expr instanceof WrappedNodeExpr;
27479 }
27480 function readBaseClass$1(node, reflector, evaluator) {
27481 const baseExpression = reflector.getBaseClassExpression(node);
27482 if (baseExpression !== null) {
27483 const baseClass = evaluator.evaluate(baseExpression);
27484 if (baseClass instanceof Reference$1 && reflector.isClass(baseClass.node)) {
27485 return baseClass;
27486 }
27487 else {
27488 return 'dynamic';
27489 }
27490 }
27491 return null;
27492 }
27493 const parensWrapperTransformerFactory = (context) => {
27494 const visitor = (node) => {
27495 const visited = ts$1.visitEachChild(node, visitor, context);
27496 if (ts$1.isArrowFunction(visited) || ts$1.isFunctionExpression(visited)) {
27497 return ts$1.createParen(visited);
27498 }
27499 return visited;
27500 };
27501 return (node) => ts$1.visitEachChild(node, visitor, context);
27502 };
27503 /**
27504 * Wraps all functions in a given expression in parentheses. This is needed to avoid problems
27505 * where Tsickle annotations added between analyse and transform phases in Angular may trigger
27506 * automatic semicolon insertion, e.g. if a function is the expression in a `return` statement.
27507 * More
27508 * info can be found in Tsickle source code here:
27509 * https://github.com/angular/tsickle/blob/d7974262571c8a17d684e5ba07680e1b1993afdd/src/jsdoc_transformer.ts#L1021
27510 *
27511 * @param expression Expression where functions should be wrapped in parentheses
27512 */
27513 function wrapFunctionExpressionsInParens(expression) {
27514 return ts$1.transform(expression, [parensWrapperTransformerFactory]).transformed[0];
27515 }
27516 /**
27517 * Create a `ts.Diagnostic` which indicates the given class is part of the declarations of two or
27518 * more NgModules.
27519 *
27520 * The resulting `ts.Diagnostic` will have a context entry for each NgModule showing the point where
27521 * the directive/pipe exists in its `declarations` (if possible).
27522 */
27523 function makeDuplicateDeclarationError(node, data, kind) {
27524 const context = [];
27525 for (const decl of data) {
27526 if (decl.rawDeclarations === null) {
27527 continue;
27528 }
27529 // Try to find the reference to the declaration within the declarations array, to hang the
27530 // error there. If it can't be found, fall back on using the NgModule's name.
27531 const contextNode = decl.ref.getOriginForDiagnostics(decl.rawDeclarations, decl.ngModule.name);
27532 context.push(makeRelatedInformation(contextNode, `'${node.name.text}' is listed in the declarations of the NgModule '${decl.ngModule.name.text}'.`));
27533 }
27534 // Finally, produce the diagnostic.
27535 return makeDiagnostic(ErrorCode.NGMODULE_DECLARATION_NOT_UNIQUE, node.name, `The ${kind} '${node.name.text}' is declared by more than one NgModule.`, context);
27536 }
27537 /**
27538 * Resolves the given `rawProviders` into `ClassDeclarations` and returns
27539 * a set containing those that are known to require a factory definition.
27540 * @param rawProviders Expression that declared the providers array in the source.
27541 */
27542 function resolveProvidersRequiringFactory(rawProviders, reflector, evaluator) {
27543 const providers = new Set();
27544 const resolvedProviders = evaluator.evaluate(rawProviders);
27545 if (!Array.isArray(resolvedProviders)) {
27546 return providers;
27547 }
27548 resolvedProviders.forEach(function processProviders(provider) {
27549 let tokenClass = null;
27550 if (Array.isArray(provider)) {
27551 // If we ran into an array, recurse into it until we've resolve all the classes.
27552 provider.forEach(processProviders);
27553 }
27554 else if (provider instanceof Reference$1) {
27555 tokenClass = provider;
27556 }
27557 else if (provider instanceof Map && provider.has('useClass') && !provider.has('deps')) {
27558 const useExisting = provider.get('useClass');
27559 if (useExisting instanceof Reference$1) {
27560 tokenClass = useExisting;
27561 }
27562 }
27563 // TODO(alxhub): there was a bug where `getConstructorParameters` would return `null` for a
27564 // class in a .d.ts file, always, even if the class had a constructor. This was fixed for
27565 // `getConstructorParameters`, but that fix causes more classes to be recognized here as needing
27566 // provider checks, which is a breaking change in g3. Avoid this breakage for now by skipping
27567 // classes from .d.ts files here directly, until g3 can be cleaned up.
27568 if (tokenClass !== null && !tokenClass.node.getSourceFile().isDeclarationFile &&
27569 reflector.isClass(tokenClass.node)) {
27570 const constructorParameters = reflector.getConstructorParameters(tokenClass.node);
27571 // Note that we only want to capture providers with a non-trivial constructor,
27572 // because they're the ones that might be using DI and need to be decorated.
27573 if (constructorParameters !== null && constructorParameters.length > 0) {
27574 providers.add(tokenClass);
27575 }
27576 }
27577 });
27578 return providers;
27579 }
27580 /**
27581 * Create an R3Reference for a class.
27582 *
27583 * The `value` is the exported declaration of the class from its source file.
27584 * The `type` is an expression that would be used by ngcc in the typings (.d.ts) files.
27585 */
27586 function wrapTypeReference(reflector, clazz) {
27587 const dtsClass = reflector.getDtsDeclaration(clazz);
27588 const value = new WrappedNodeExpr(clazz.name);
27589 const type = dtsClass !== null && isNamedClassDeclaration(dtsClass) ?
27590 new WrappedNodeExpr(dtsClass.name) :
27591 value;
27592 return { value, type };
27593 }
27594 /** Creates a ParseSourceSpan for a TypeScript node. */
27595 function createSourceSpan(node) {
27596 const sf = node.getSourceFile();
27597 const [startOffset, endOffset] = [node.getStart(), node.getEnd()];
27598 const { line: startLine, character: startCol } = sf.getLineAndCharacterOfPosition(startOffset);
27599 const { line: endLine, character: endCol } = sf.getLineAndCharacterOfPosition(endOffset);
27600 const parseSf = new ParseSourceFile(sf.getFullText(), sf.fileName);
27601 // +1 because values are zero-indexed.
27602 return new ParseSourceSpan(new ParseLocation(parseSf, startOffset, startLine + 1, startCol + 1), new ParseLocation(parseSf, endOffset, endLine + 1, endCol + 1));
27603 }
27604
27605 /**
27606 * @license
27607 * Copyright Google LLC All Rights Reserved.
27608 *
27609 * Use of this source code is governed by an MIT-style license that can be
27610 * found in the LICENSE file at https://angular.io/license
27611 */
27612 /**
27613 * Creates a `FatalDiagnosticError` for a node that did not evaluate to the expected type. The
27614 * diagnostic that is created will include details on why the value is incorrect, i.e. it includes
27615 * a representation of the actual type that was unsupported, or in the case of a dynamic value the
27616 * trace to the node where the dynamic value originated.
27617 *
27618 * @param node The node for which the diagnostic should be produced.
27619 * @param value The evaluated value that has the wrong type.
27620 * @param messageText The message text of the error.
27621 */
27622 function createValueHasWrongTypeError(node, value, messageText) {
27623 var _a;
27624 let chainedMessage;
27625 let relatedInformation;
27626 if (value instanceof DynamicValue) {
27627 chainedMessage = 'Value could not be determined statically.';
27628 relatedInformation = traceDynamicValue(node, value);
27629 }
27630 else if (value instanceof Reference$1) {
27631 const target = value.debugName !== null ? `'${value.debugName}'` : 'an anonymous declaration';
27632 chainedMessage = `Value is a reference to ${target}.`;
27633 const referenceNode = (_a = identifierOfNode(value.node)) !== null && _a !== void 0 ? _a : value.node;
27634 relatedInformation = [makeRelatedInformation(referenceNode, 'Reference is declared here.')];
27635 }
27636 else {
27637 chainedMessage = `Value is of type '${describeResolvedType(value)}'.`;
27638 }
27639 const chain = {
27640 messageText,
27641 category: ts$1.DiagnosticCategory.Error,
27642 code: 0,
27643 next: [{
27644 messageText: chainedMessage,
27645 category: ts$1.DiagnosticCategory.Message,
27646 code: 0,
27647 }]
27648 };
27649 return new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, node, chain, relatedInformation);
27650 }
27651 /**
27652 * Gets the diagnostics for a set of provider classes.
27653 * @param providerClasses Classes that should be checked.
27654 * @param providersDeclaration Node that declares the providers array.
27655 * @param registry Registry that keeps track of the registered injectable classes.
27656 */
27657 function getProviderDiagnostics(providerClasses, providersDeclaration, registry) {
27658 const diagnostics = [];
27659 for (const provider of providerClasses) {
27660 if (registry.isInjectable(provider.node)) {
27661 continue;
27662 }
27663 const contextNode = provider.getOriginForDiagnostics(providersDeclaration);
27664 diagnostics.push(makeDiagnostic(ErrorCode.UNDECORATED_PROVIDER, contextNode, `The class '${provider.node.name
27665 .text}' cannot be created via dependency injection, as it does not have an Angular decorator. This will result in an error at runtime.
27666
27667Either add the @Injectable() decorator to '${provider.node.name
27668 .text}', or configure a different provider (such as a provider with 'useFactory').
27669`, [makeRelatedInformation(provider.node, `'${provider.node.name.text}' is declared here.`)]));
27670 }
27671 return diagnostics;
27672 }
27673 function getDirectiveDiagnostics(node, reader, evaluator, reflector, scopeRegistry, kind) {
27674 let diagnostics = [];
27675 const addDiagnostics = (more) => {
27676 if (more === null) {
27677 return;
27678 }
27679 else if (diagnostics === null) {
27680 diagnostics = Array.isArray(more) ? more : [more];
27681 }
27682 else if (Array.isArray(more)) {
27683 diagnostics.push(...more);
27684 }
27685 else {
27686 diagnostics.push(more);
27687 }
27688 };
27689 const duplicateDeclarations = scopeRegistry.getDuplicateDeclarations(node);
27690 if (duplicateDeclarations !== null) {
27691 addDiagnostics(makeDuplicateDeclarationError(node, duplicateDeclarations, kind));
27692 }
27693 addDiagnostics(checkInheritanceOfDirective(node, reader, reflector, evaluator));
27694 return diagnostics;
27695 }
27696 function getUndecoratedClassWithAngularFeaturesDiagnostic(node) {
27697 return makeDiagnostic(ErrorCode.UNDECORATED_CLASS_USING_ANGULAR_FEATURES, node.name, `Class is using Angular features but is not decorated. Please add an explicit ` +
27698 `Angular decorator.`);
27699 }
27700 function checkInheritanceOfDirective(node, reader, reflector, evaluator) {
27701 if (!reflector.isClass(node) || reflector.getConstructorParameters(node) !== null) {
27702 // We should skip nodes that aren't classes. If a constructor exists, then no base class
27703 // definition is required on the runtime side - it's legal to inherit from any class.
27704 return null;
27705 }
27706 // The extends clause is an expression which can be as dynamic as the user wants. Try to
27707 // evaluate it, but fall back on ignoring the clause if it can't be understood. This is a View
27708 // Engine compatibility hack: View Engine ignores 'extends' expressions that it cannot understand.
27709 let baseClass = readBaseClass$1(node, reflector, evaluator);
27710 while (baseClass !== null) {
27711 if (baseClass === 'dynamic') {
27712 return null;
27713 }
27714 // We can skip the base class if it has metadata.
27715 const baseClassMeta = reader.getDirectiveMetadata(baseClass);
27716 if (baseClassMeta !== null) {
27717 return null;
27718 }
27719 // If the base class has a blank constructor we can skip it since it can't be using DI.
27720 const baseClassConstructorParams = reflector.getConstructorParameters(baseClass.node);
27721 const newParentClass = readBaseClass$1(baseClass.node, reflector, evaluator);
27722 if (baseClassConstructorParams !== null && baseClassConstructorParams.length > 0) {
27723 // This class has a non-trivial constructor, that's an error!
27724 return getInheritedUndecoratedCtorDiagnostic(node, baseClass, reader);
27725 }
27726 else if (baseClassConstructorParams !== null || newParentClass === null) {
27727 // This class has a trivial constructor, or no constructor + is the
27728 // top of the inheritance chain, so it's okay.
27729 return null;
27730 }
27731 // Go up the chain and continue
27732 baseClass = newParentClass;
27733 }
27734 return null;
27735 }
27736 function getInheritedUndecoratedCtorDiagnostic(node, baseClass, reader) {
27737 const subclassMeta = reader.getDirectiveMetadata(new Reference$1(node));
27738 const dirOrComp = subclassMeta.isComponent ? 'Component' : 'Directive';
27739 const baseClassName = baseClass.debugName;
27740 return makeDiagnostic(ErrorCode.DIRECTIVE_INHERITS_UNDECORATED_CTOR, node.name, `The ${dirOrComp.toLowerCase()} ${node.name.text} inherits its constructor from ${baseClassName}, ` +
27741 `but the latter does not have an Angular decorator of its own. Dependency injection will not be able to ` +
27742 `resolve the parameters of ${baseClassName}'s constructor. Either add a @Directive decorator ` +
27743 `to ${baseClassName}, or add an explicit constructor to ${node.name.text}.`);
27744 }
27745
27746 /**
27747 * @license
27748 * Copyright Google LLC All Rights Reserved.
27749 *
27750 * Use of this source code is governed by an MIT-style license that can be
27751 * found in the LICENSE file at https://angular.io/license
27752 */
27753 function compileNgFactoryDefField(metadata) {
27754 const res = compileFactoryFunction(metadata);
27755 return { name: 'ɵfac', initializer: res.factory, statements: res.statements, type: res.type };
27756 }
27757
27758 /**
27759 * @license
27760 * Copyright Google LLC All Rights Reserved.
27761 *
27762 * Use of this source code is governed by an MIT-style license that can be
27763 * found in the LICENSE file at https://angular.io/license
27764 */
27765 /**
27766 * Given a class declaration, generate a call to `setClassMetadata` with the Angular metadata
27767 * present on the class or its member fields. An ngDevMode guard is used to allow the call to be
27768 * tree-shaken away, as the `setClassMetadata` invocation is only needed for testing purposes.
27769 *
27770 * If no such metadata is present, this function returns `null`. Otherwise, the call is returned
27771 * as a `Statement` for inclusion along with the class.
27772 */
27773 function generateSetClassMetadataCall(clazz, reflection, defaultImportRecorder, isCore, annotateForClosureCompiler) {
27774 if (!reflection.isClass(clazz)) {
27775 return null;
27776 }
27777 const id = reflection.getAdjacentNameOfClass(clazz);
27778 // Reflect over the class decorators. If none are present, or those that are aren't from
27779 // Angular, then return null. Otherwise, turn them into metadata.
27780 const classDecorators = reflection.getDecoratorsOfDeclaration(clazz);
27781 if (classDecorators === null) {
27782 return null;
27783 }
27784 const ngClassDecorators = classDecorators.filter(dec => isAngularDecorator$1(dec, isCore))
27785 .map(decorator => decoratorToMetadata(decorator, annotateForClosureCompiler))
27786 // Since the `setClassMetadata` call is intended to be emitted after the class
27787 // declaration, we have to strip references to the existing identifiers or
27788 // TypeScript might generate invalid code when it emits to JS. In particular
27789 // this can break when emitting a class to ES5 which has a custom decorator
27790 // and is referenced inside of its own metadata (see #39509 for more information).
27791 .map(decorator => removeIdentifierReferences(decorator, id.text));
27792 if (ngClassDecorators.length === 0) {
27793 return null;
27794 }
27795 const metaDecorators = ts$1.createArrayLiteral(ngClassDecorators);
27796 // Convert the constructor parameters to metadata, passing null if none are present.
27797 let metaCtorParameters = new LiteralExpr(null);
27798 const classCtorParameters = reflection.getConstructorParameters(clazz);
27799 if (classCtorParameters !== null) {
27800 const ctorParameters = classCtorParameters.map(param => ctorParameterToMetadata(param, defaultImportRecorder, isCore));
27801 metaCtorParameters = new FunctionExpr([], [
27802 new ReturnStatement(new LiteralArrayExpr(ctorParameters)),
27803 ]);
27804 }
27805 // Do the same for property decorators.
27806 let metaPropDecorators = ts$1.createNull();
27807 const classMembers = reflection.getMembersOfClass(clazz).filter(member => !member.isStatic && member.decorators !== null && member.decorators.length > 0);
27808 const duplicateDecoratedMemberNames = classMembers.map(member => member.name).filter((name, i, arr) => arr.indexOf(name) < i);
27809 if (duplicateDecoratedMemberNames.length > 0) {
27810 // This should theoretically never happen, because the only way to have duplicate instance
27811 // member names is getter/setter pairs and decorators cannot appear in both a getter and the
27812 // corresponding setter.
27813 throw new Error(`Duplicate decorated properties found on class '${clazz.name.text}': ` +
27814 duplicateDecoratedMemberNames.join(', '));
27815 }
27816 const decoratedMembers = classMembers.map(member => { var _a; return classMemberToMetadata((_a = member.nameNode) !== null && _a !== void 0 ? _a : member.name, member.decorators, isCore); });
27817 if (decoratedMembers.length > 0) {
27818 metaPropDecorators = ts$1.createObjectLiteral(decoratedMembers);
27819 }
27820 // Generate a pure call to setClassMetadata with the class identifier and its metadata.
27821 const setClassMetadata = new ExternalExpr(Identifiers.setClassMetadata);
27822 const fnCall = new InvokeFunctionExpr(
27823 /* fn */ setClassMetadata,
27824 /* args */
27825 [
27826 new WrappedNodeExpr(id),
27827 new WrappedNodeExpr(metaDecorators),
27828 metaCtorParameters,
27829 new WrappedNodeExpr(metaPropDecorators),
27830 ]);
27831 const iife = new FunctionExpr([], [devOnlyGuardedExpression(fnCall).toStmt()]);
27832 return iife.callFn([]).toStmt();
27833 }
27834 /**
27835 * Convert a reflected constructor parameter to metadata.
27836 */
27837 function ctorParameterToMetadata(param, defaultImportRecorder, isCore) {
27838 // Parameters sometimes have a type that can be referenced. If so, then use it, otherwise
27839 // its type is undefined.
27840 const type = param.typeValueReference.kind !== 2 /* UNAVAILABLE */ ?
27841 valueReferenceToExpression(param.typeValueReference, defaultImportRecorder) :
27842 new LiteralExpr(undefined);
27843 const mapEntries = [
27844 { key: 'type', value: type, quoted: false },
27845 ];
27846 // If the parameter has decorators, include the ones from Angular.
27847 if (param.decorators !== null) {
27848 const ngDecorators = param.decorators.filter(dec => isAngularDecorator$1(dec, isCore))
27849 .map((decorator) => decoratorToMetadata(decorator));
27850 const value = new WrappedNodeExpr(ts$1.createArrayLiteral(ngDecorators));
27851 mapEntries.push({ key: 'decorators', value, quoted: false });
27852 }
27853 return literalMap(mapEntries);
27854 }
27855 /**
27856 * Convert a reflected class member to metadata.
27857 */
27858 function classMemberToMetadata(name, decorators, isCore) {
27859 const ngDecorators = decorators.filter(dec => isAngularDecorator$1(dec, isCore))
27860 .map((decorator) => decoratorToMetadata(decorator));
27861 const decoratorMeta = ts$1.createArrayLiteral(ngDecorators);
27862 return ts$1.createPropertyAssignment(name, decoratorMeta);
27863 }
27864 /**
27865 * Convert a reflected decorator to metadata.
27866 */
27867 function decoratorToMetadata(decorator, wrapFunctionsInParens) {
27868 if (decorator.identifier === null) {
27869 throw new Error('Illegal state: synthesized decorator cannot be emitted in class metadata.');
27870 }
27871 // Decorators have a type.
27872 const properties = [
27873 ts$1.createPropertyAssignment('type', ts$1.getMutableClone(decorator.identifier)),
27874 ];
27875 // Sometimes they have arguments.
27876 if (decorator.args !== null && decorator.args.length > 0) {
27877 const args = decorator.args.map(arg => {
27878 const expr = ts$1.getMutableClone(arg);
27879 return wrapFunctionsInParens ? wrapFunctionExpressionsInParens(expr) : expr;
27880 });
27881 properties.push(ts$1.createPropertyAssignment('args', ts$1.createArrayLiteral(args)));
27882 }
27883 return ts$1.createObjectLiteral(properties, true);
27884 }
27885 /**
27886 * Whether a given decorator should be treated as an Angular decorator.
27887 *
27888 * Either it's used in @angular/core, or it's imported from there.
27889 */
27890 function isAngularDecorator$1(decorator, isCore) {
27891 return isCore || (decorator.import !== null && decorator.import.from === '@angular/core');
27892 }
27893 /**
27894 * Recursively recreates all of the `Identifier` descendant nodes with a particular name inside
27895 * of an AST node, thus removing any references to them. Useful if a particular node has to be t
27896 * aken from one place any emitted to another one exactly as it has been written.
27897 */
27898 function removeIdentifierReferences(node, name) {
27899 const result = ts$1.transform(node, [context => root => ts$1.visitNode(root, function walk(current) {
27900 return ts$1.isIdentifier(current) && current.text === name ?
27901 ts$1.createIdentifier(current.text) :
27902 ts$1.visitEachChild(current, walk, context);
27903 })]);
27904 return result.transformed[0];
27905 }
27906
27907 /**
27908 * @license
27909 * Copyright Google LLC All Rights Reserved.
27910 *
27911 * Use of this source code is governed by an MIT-style license that can be
27912 * found in the LICENSE file at https://angular.io/license
27913 */
27914 const EMPTY_OBJECT = {};
27915 const FIELD_DECORATORS = [
27916 'Input', 'Output', 'ViewChild', 'ViewChildren', 'ContentChild', 'ContentChildren', 'HostBinding',
27917 'HostListener'
27918 ];
27919 const LIFECYCLE_HOOKS = new Set([
27920 'ngOnChanges', 'ngOnInit', 'ngOnDestroy', 'ngDoCheck', 'ngAfterViewInit', 'ngAfterViewChecked',
27921 'ngAfterContentInit', 'ngAfterContentChecked'
27922 ]);
27923 class DirectiveDecoratorHandler {
27924 constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportRecorder, injectableRegistry, isCore, annotateForClosureCompiler, compileUndecoratedClassesWithAngularFeatures) {
27925 this.reflector = reflector;
27926 this.evaluator = evaluator;
27927 this.metaRegistry = metaRegistry;
27928 this.scopeRegistry = scopeRegistry;
27929 this.metaReader = metaReader;
27930 this.defaultImportRecorder = defaultImportRecorder;
27931 this.injectableRegistry = injectableRegistry;
27932 this.isCore = isCore;
27933 this.annotateForClosureCompiler = annotateForClosureCompiler;
27934 this.compileUndecoratedClassesWithAngularFeatures = compileUndecoratedClassesWithAngularFeatures;
27935 this.precedence = HandlerPrecedence.PRIMARY;
27936 this.name = DirectiveDecoratorHandler.name;
27937 }
27938 detect(node, decorators) {
27939 // If a class is undecorated but uses Angular features, we detect it as an
27940 // abstract directive. This is an unsupported pattern as of v10, but we want
27941 // to still detect these patterns so that we can report diagnostics, or compile
27942 // them for backwards compatibility in ngcc.
27943 if (!decorators) {
27944 const angularField = this.findClassFieldWithAngularFeatures(node);
27945 return angularField ? { trigger: angularField.node, decorator: null, metadata: null } :
27946 undefined;
27947 }
27948 else {
27949 const decorator = findAngularDecorator(decorators, 'Directive', this.isCore);
27950 return decorator ? { trigger: decorator.node, decorator, metadata: decorator } : undefined;
27951 }
27952 }
27953 analyze(node, decorator, flags = HandlerFlags.NONE) {
27954 // Skip processing of the class declaration if compilation of undecorated classes
27955 // with Angular features is disabled. Previously in ngtsc, such classes have always
27956 // been processed, but we want to enforce a consistent decorator mental model.
27957 // See: https://v9.angular.io/guide/migration-undecorated-classes.
27958 if (this.compileUndecoratedClassesWithAngularFeatures === false && decorator === null) {
27959 return { diagnostics: [getUndecoratedClassWithAngularFeaturesDiagnostic(node)] };
27960 }
27961 const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, flags, this.annotateForClosureCompiler);
27962 if (directiveResult === undefined) {
27963 return {};
27964 }
27965 const analysis = directiveResult.metadata;
27966 let providersRequiringFactory = null;
27967 if (directiveResult !== undefined && directiveResult.decorator.has('providers')) {
27968 providersRequiringFactory = resolveProvidersRequiringFactory(directiveResult.decorator.get('providers'), this.reflector, this.evaluator);
27969 }
27970 return {
27971 analysis: {
27972 inputs: directiveResult.inputs,
27973 outputs: directiveResult.outputs,
27974 meta: analysis,
27975 metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
27976 baseClass: readBaseClass$1(node, this.reflector, this.evaluator),
27977 typeCheckMeta: extractDirectiveTypeCheckMeta(node, directiveResult.inputs, this.reflector),
27978 providersRequiringFactory,
27979 isPoisoned: false,
27980 isStructural: directiveResult.isStructural,
27981 }
27982 };
27983 }
27984 register(node, analysis) {
27985 // Register this directive's information with the `MetadataRegistry`. This ensures that
27986 // the information about the directive is available during the compile() phase.
27987 const ref = new Reference$1(node);
27988 this.metaRegistry.registerDirectiveMetadata(Object.assign(Object.assign({ ref, name: node.name.text, selector: analysis.meta.selector, exportAs: analysis.meta.exportAs, inputs: analysis.inputs, outputs: analysis.outputs, queries: analysis.meta.queries.map(query => query.propertyName), isComponent: false, baseClass: analysis.baseClass }, analysis.typeCheckMeta), { isPoisoned: analysis.isPoisoned, isStructural: analysis.isStructural }));
27989 this.injectableRegistry.registerInjectable(node);
27990 }
27991 resolve(node, analysis) {
27992 const diagnostics = [];
27993 if (analysis.providersRequiringFactory !== null &&
27994 analysis.meta.providers instanceof WrappedNodeExpr) {
27995 const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.meta.providers.node, this.injectableRegistry);
27996 diagnostics.push(...providerDiagnostics);
27997 }
27998 const directiveDiagnostics = getDirectiveDiagnostics(node, this.metaReader, this.evaluator, this.reflector, this.scopeRegistry, 'Directive');
27999 if (directiveDiagnostics !== null) {
28000 diagnostics.push(...directiveDiagnostics);
28001 }
28002 return { diagnostics: diagnostics.length > 0 ? diagnostics : undefined };
28003 }
28004 compileFull(node, analysis, resolution, pool) {
28005 const def = compileDirectiveFromMetadata(analysis.meta, pool, makeBindingParser());
28006 return this.compileDirective(analysis, def);
28007 }
28008 compilePartial(node, analysis, resolution) {
28009 const def = compileDeclareDirectiveFromMetadata(analysis.meta);
28010 return this.compileDirective(analysis, def);
28011 }
28012 compileDirective(analysis, { expression: initializer, type }) {
28013 const factoryRes = compileNgFactoryDefField(Object.assign(Object.assign({}, analysis.meta), { injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Directive }));
28014 if (analysis.metadataStmt !== null) {
28015 factoryRes.statements.push(analysis.metadataStmt);
28016 }
28017 return [
28018 factoryRes, {
28019 name: 'ɵdir',
28020 initializer,
28021 statements: [],
28022 type,
28023 }
28024 ];
28025 }
28026 /**
28027 * Checks if a given class uses Angular features and returns the TypeScript node
28028 * that indicated the usage. Classes are considered using Angular features if they
28029 * contain class members that are either decorated with a known Angular decorator,
28030 * or if they correspond to a known Angular lifecycle hook.
28031 */
28032 findClassFieldWithAngularFeatures(node) {
28033 return this.reflector.getMembersOfClass(node).find(member => {
28034 if (!member.isStatic && member.kind === ClassMemberKind.Method &&
28035 LIFECYCLE_HOOKS.has(member.name)) {
28036 return true;
28037 }
28038 if (member.decorators) {
28039 return member.decorators.some(decorator => FIELD_DECORATORS.some(decoratorName => isAngularDecorator(decorator, decoratorName, this.isCore)));
28040 }
28041 return false;
28042 });
28043 }
28044 }
28045 /**
28046 * Helper function to extract metadata from a `Directive` or `Component`. `Directive`s without a
28047 * selector are allowed to be used for abstract base classes. These abstract directives should not
28048 * appear in the declarations of an `NgModule` and additional verification is done when processing
28049 * the module.
28050 */
28051 function extractDirectiveMetadata(clazz, decorator, reflector, evaluator, defaultImportRecorder, isCore, flags, annotateForClosureCompiler, defaultSelector = null) {
28052 let directive;
28053 if (decorator === null || decorator.args === null || decorator.args.length === 0) {
28054 directive = new Map();
28055 }
28056 else if (decorator.args.length !== 1) {
28057 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @${decorator.name} decorator`);
28058 }
28059 else {
28060 const meta = unwrapExpression(decorator.args[0]);
28061 if (!ts$1.isObjectLiteralExpression(meta)) {
28062 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, `@${decorator.name} argument must be an object literal`);
28063 }
28064 directive = reflectObjectLiteral(meta);
28065 }
28066 if (directive.has('jit')) {
28067 // The only allowed value is true, so there's no need to expand further.
28068 return undefined;
28069 }
28070 const members = reflector.getMembersOfClass(clazz);
28071 // Precompute a list of ts.ClassElements that have decorators. This includes things like @Input,
28072 // @Output, @HostBinding, etc.
28073 const decoratedElements = members.filter(member => !member.isStatic && member.decorators !== null);
28074 const coreModule = isCore ? undefined : '@angular/core';
28075 // Construct the map of inputs both from the @Directive/@Component
28076 // decorator, and the decorated
28077 // fields.
28078 const inputsFromMeta = parseFieldToPropertyMapping(directive, 'inputs', evaluator);
28079 const inputsFromFields = parseDecoratedFields(filterToMembersWithDecorator(decoratedElements, 'Input', coreModule), evaluator, resolveInput);
28080 // And outputs.
28081 const outputsFromMeta = parseFieldToPropertyMapping(directive, 'outputs', evaluator);
28082 const outputsFromFields = parseDecoratedFields(filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), evaluator, resolveOutput);
28083 // Construct the list of queries.
28084 const contentChildFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ContentChild', coreModule), reflector, evaluator);
28085 const contentChildrenFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ContentChildren', coreModule), reflector, evaluator);
28086 const queries = [...contentChildFromFields, ...contentChildrenFromFields];
28087 // Construct the list of view queries.
28088 const viewChildFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ViewChild', coreModule), reflector, evaluator);
28089 const viewChildrenFromFields = queriesFromFields(filterToMembersWithDecorator(decoratedElements, 'ViewChildren', coreModule), reflector, evaluator);
28090 const viewQueries = [...viewChildFromFields, ...viewChildrenFromFields];
28091 if (directive.has('queries')) {
28092 const queriesFromDecorator = extractQueriesFromDecorator(directive.get('queries'), reflector, evaluator, isCore);
28093 queries.push(...queriesFromDecorator.content);
28094 viewQueries.push(...queriesFromDecorator.view);
28095 }
28096 // Parse the selector.
28097 let selector = defaultSelector;
28098 if (directive.has('selector')) {
28099 const expr = directive.get('selector');
28100 const resolved = evaluator.evaluate(expr);
28101 if (typeof resolved !== 'string') {
28102 throw createValueHasWrongTypeError(expr, resolved, `selector must be a string`);
28103 }
28104 // use default selector in case selector is an empty string
28105 selector = resolved === '' ? defaultSelector : resolved;
28106 if (!selector) {
28107 throw new FatalDiagnosticError(ErrorCode.DIRECTIVE_MISSING_SELECTOR, expr, `Directive ${clazz.name.text} has no selector, please add it!`);
28108 }
28109 }
28110 const host = extractHostBindings$1(decoratedElements, evaluator, coreModule, directive);
28111 const providers = directive.has('providers') ?
28112 new WrappedNodeExpr(annotateForClosureCompiler ?
28113 wrapFunctionExpressionsInParens(directive.get('providers')) :
28114 directive.get('providers')) :
28115 null;
28116 // Determine if `ngOnChanges` is a lifecycle hook defined on the component.
28117 const usesOnChanges = members.some(member => !member.isStatic && member.kind === ClassMemberKind.Method &&
28118 member.name === 'ngOnChanges');
28119 // Parse exportAs.
28120 let exportAs = null;
28121 if (directive.has('exportAs')) {
28122 const expr = directive.get('exportAs');
28123 const resolved = evaluator.evaluate(expr);
28124 if (typeof resolved !== 'string') {
28125 throw createValueHasWrongTypeError(expr, resolved, `exportAs must be a string`);
28126 }
28127 exportAs = resolved.split(',').map(part => part.trim());
28128 }
28129 const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
28130 let ctorDeps;
28131 // Non-abstract directives (those with a selector) require valid constructor dependencies, whereas
28132 // abstract directives are allowed to have invalid dependencies, given that a subclass may call
28133 // the constructor explicitly.
28134 if (selector !== null) {
28135 ctorDeps = validateConstructorDependencies(clazz, rawCtorDeps);
28136 }
28137 else {
28138 ctorDeps = unwrapConstructorDependencies(rawCtorDeps);
28139 }
28140 const isStructural = ctorDeps !== null && ctorDeps !== 'invalid' && ctorDeps.some(dep => {
28141 if (dep.resolved !== R3ResolvedDependencyType.Token || !(dep.token instanceof ExternalExpr)) {
28142 return false;
28143 }
28144 if (dep.token.value.moduleName !== '@angular/core' || dep.token.value.name !== 'TemplateRef') {
28145 return false;
28146 }
28147 return true;
28148 });
28149 // Detect if the component inherits from another class
28150 const usesInheritance = reflector.hasBaseClass(clazz);
28151 const type = wrapTypeReference(reflector, clazz);
28152 const internalType = new WrappedNodeExpr(reflector.getInternalNameOfClass(clazz));
28153 const inputs = ClassPropertyMapping.fromMappedObject(Object.assign(Object.assign({}, inputsFromMeta), inputsFromFields));
28154 const outputs = ClassPropertyMapping.fromMappedObject(Object.assign(Object.assign({}, outputsFromMeta), outputsFromFields));
28155 const metadata = {
28156 name: clazz.name.text,
28157 deps: ctorDeps,
28158 host,
28159 lifecycle: {
28160 usesOnChanges,
28161 },
28162 inputs: inputs.toJointMappedObject(),
28163 outputs: outputs.toDirectMappedObject(),
28164 queries,
28165 viewQueries,
28166 selector,
28167 fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE),
28168 type,
28169 internalType,
28170 typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
28171 typeSourceSpan: createSourceSpan(clazz.name),
28172 usesInheritance,
28173 exportAs,
28174 providers
28175 };
28176 return {
28177 decorator: directive,
28178 metadata,
28179 inputs,
28180 outputs,
28181 isStructural,
28182 };
28183 }
28184 function extractQueryMetadata(exprNode, name, args, propertyName, reflector, evaluator) {
28185 if (args.length === 0) {
28186 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, exprNode, `@${name} must have arguments`);
28187 }
28188 const first = name === 'ViewChild' || name === 'ContentChild';
28189 const node = unwrapForwardRef(args[0], reflector);
28190 const arg = evaluator.evaluate(node);
28191 /** Whether or not this query should collect only static results (see view/api.ts) */
28192 let isStatic = false;
28193 // Extract the predicate
28194 let predicate = null;
28195 if (arg instanceof Reference$1 || arg instanceof DynamicValue) {
28196 // References and predicates that could not be evaluated statically are emitted as is.
28197 predicate = new WrappedNodeExpr(node);
28198 }
28199 else if (typeof arg === 'string') {
28200 predicate = [arg];
28201 }
28202 else if (isStringArrayOrDie(arg, `@${name} predicate`, node)) {
28203 predicate = arg;
28204 }
28205 else {
28206 throw createValueHasWrongTypeError(node, arg, `@${name} predicate cannot be interpreted`);
28207 }
28208 // Extract the read and descendants options.
28209 let read = null;
28210 // The default value for descendants is true for every decorator except @ContentChildren.
28211 let descendants = name !== 'ContentChildren';
28212 let emitDistinctChangesOnly = emitDistinctChangesOnlyDefaultValue;
28213 if (args.length === 2) {
28214 const optionsExpr = unwrapExpression(args[1]);
28215 if (!ts$1.isObjectLiteralExpression(optionsExpr)) {
28216 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, optionsExpr, `@${name} options must be an object literal`);
28217 }
28218 const options = reflectObjectLiteral(optionsExpr);
28219 if (options.has('read')) {
28220 read = new WrappedNodeExpr(options.get('read'));
28221 }
28222 if (options.has('descendants')) {
28223 const descendantsExpr = options.get('descendants');
28224 const descendantsValue = evaluator.evaluate(descendantsExpr);
28225 if (typeof descendantsValue !== 'boolean') {
28226 throw createValueHasWrongTypeError(descendantsExpr, descendantsValue, `@${name} options.descendants must be a boolean`);
28227 }
28228 descendants = descendantsValue;
28229 }
28230 if (options.has('emitDistinctChangesOnly')) {
28231 const emitDistinctChangesOnlyExpr = options.get('emitDistinctChangesOnly');
28232 const emitDistinctChangesOnlyValue = evaluator.evaluate(emitDistinctChangesOnlyExpr);
28233 if (typeof emitDistinctChangesOnlyValue !== 'boolean') {
28234 throw createValueHasWrongTypeError(emitDistinctChangesOnlyExpr, emitDistinctChangesOnlyValue, `@${name} options.emitDistinctChangesOnlys must be a boolean`);
28235 }
28236 emitDistinctChangesOnly = emitDistinctChangesOnlyValue;
28237 }
28238 if (options.has('static')) {
28239 const staticValue = evaluator.evaluate(options.get('static'));
28240 if (typeof staticValue !== 'boolean') {
28241 throw createValueHasWrongTypeError(node, staticValue, `@${name} options.static must be a boolean`);
28242 }
28243 isStatic = staticValue;
28244 }
28245 }
28246 else if (args.length > 2) {
28247 // Too many arguments.
28248 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, node, `@${name} has too many arguments`);
28249 }
28250 return {
28251 propertyName,
28252 predicate,
28253 first,
28254 descendants,
28255 read,
28256 static: isStatic,
28257 emitDistinctChangesOnly,
28258 };
28259 }
28260 function extractQueriesFromDecorator(queryData, reflector, evaluator, isCore) {
28261 const content = [], view = [];
28262 if (!ts$1.isObjectLiteralExpression(queryData)) {
28263 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator queries metadata must be an object literal');
28264 }
28265 reflectObjectLiteral(queryData).forEach((queryExpr, propertyName) => {
28266 queryExpr = unwrapExpression(queryExpr);
28267 if (!ts$1.isNewExpression(queryExpr)) {
28268 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator query metadata must be an instance of a query type');
28269 }
28270 const queryType = ts$1.isPropertyAccessExpression(queryExpr.expression) ?
28271 queryExpr.expression.name :
28272 queryExpr.expression;
28273 if (!ts$1.isIdentifier(queryType)) {
28274 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator query metadata must be an instance of a query type');
28275 }
28276 const type = reflector.getImportOfIdentifier(queryType);
28277 if (type === null || (!isCore && type.from !== '@angular/core') ||
28278 !QUERY_TYPES.has(type.name)) {
28279 throw new FatalDiagnosticError(ErrorCode.VALUE_HAS_WRONG_TYPE, queryData, 'Decorator query metadata must be an instance of a query type');
28280 }
28281 const query = extractQueryMetadata(queryExpr, type.name, queryExpr.arguments || [], propertyName, reflector, evaluator);
28282 if (type.name.startsWith('Content')) {
28283 content.push(query);
28284 }
28285 else {
28286 view.push(query);
28287 }
28288 });
28289 return { content, view };
28290 }
28291 function isStringArrayOrDie(value, name, node) {
28292 if (!Array.isArray(value)) {
28293 return false;
28294 }
28295 for (let i = 0; i < value.length; i++) {
28296 if (typeof value[i] !== 'string') {
28297 throw createValueHasWrongTypeError(node, value[i], `Failed to resolve ${name} at position ${i} to a string`);
28298 }
28299 }
28300 return true;
28301 }
28302 function parseFieldArrayValue(directive, field, evaluator) {
28303 if (!directive.has(field)) {
28304 return null;
28305 }
28306 // Resolve the field of interest from the directive metadata to a string[].
28307 const expression = directive.get(field);
28308 const value = evaluator.evaluate(expression);
28309 if (!isStringArrayOrDie(value, field, expression)) {
28310 throw createValueHasWrongTypeError(expression, value, `Failed to resolve @Directive.${field} to a string array`);
28311 }
28312 return value;
28313 }
28314 /**
28315 * Interpret property mapping fields on the decorator (e.g. inputs or outputs) and return the
28316 * correctly shaped metadata object.
28317 */
28318 function parseFieldToPropertyMapping(directive, field, evaluator) {
28319 const metaValues = parseFieldArrayValue(directive, field, evaluator);
28320 if (!metaValues) {
28321 return EMPTY_OBJECT;
28322 }
28323 return metaValues.reduce((results, value) => {
28324 // Either the value is 'field' or 'field: property'. In the first case, `property` will
28325 // be undefined, in which case the field name should also be used as the property name.
28326 const [field, property] = value.split(':', 2).map(str => str.trim());
28327 results[field] = property || field;
28328 return results;
28329 }, {});
28330 }
28331 /**
28332 * Parse property decorators (e.g. `Input` or `Output`) and return the correctly shaped metadata
28333 * object.
28334 */
28335 function parseDecoratedFields(fields, evaluator, mapValueResolver) {
28336 return fields.reduce((results, field) => {
28337 const fieldName = field.member.name;
28338 field.decorators.forEach(decorator => {
28339 // The decorator either doesn't have an argument (@Input()) in which case the property
28340 // name is used, or it has one argument (@Output('named')).
28341 if (decorator.args == null || decorator.args.length === 0) {
28342 results[fieldName] = fieldName;
28343 }
28344 else if (decorator.args.length === 1) {
28345 const property = evaluator.evaluate(decorator.args[0]);
28346 if (typeof property !== 'string') {
28347 throw createValueHasWrongTypeError(Decorator.nodeForError(decorator), property, `@${decorator.name} decorator argument must resolve to a string`);
28348 }
28349 results[fieldName] = mapValueResolver(property, fieldName);
28350 }
28351 else {
28352 // Too many arguments.
28353 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `@${decorator.name} can have at most one argument, got ${decorator.args.length} argument(s)`);
28354 }
28355 });
28356 return results;
28357 }, {});
28358 }
28359 function resolveInput(publicName, internalName) {
28360 return [publicName, internalName];
28361 }
28362 function resolveOutput(publicName, internalName) {
28363 return publicName;
28364 }
28365 function queriesFromFields(fields, reflector, evaluator) {
28366 return fields.map(({ member, decorators }) => {
28367 const decorator = decorators[0];
28368 const node = member.node || Decorator.nodeForError(decorator);
28369 // Throw in case of `@Input() @ContentChild('foo') foo: any`, which is not supported in Ivy
28370 if (member.decorators.some(v => v.name === 'Input')) {
28371 throw new FatalDiagnosticError(ErrorCode.DECORATOR_COLLISION, node, 'Cannot combine @Input decorators with query decorators');
28372 }
28373 if (decorators.length !== 1) {
28374 throw new FatalDiagnosticError(ErrorCode.DECORATOR_COLLISION, node, 'Cannot have multiple query decorators on the same class member');
28375 }
28376 else if (!isPropertyTypeMember(member)) {
28377 throw new FatalDiagnosticError(ErrorCode.DECORATOR_UNEXPECTED, node, 'Query decorator must go on a property-type member');
28378 }
28379 return extractQueryMetadata(node, decorator.name, decorator.args || [], member.name, reflector, evaluator);
28380 });
28381 }
28382 function isPropertyTypeMember(member) {
28383 return member.kind === ClassMemberKind.Getter || member.kind === ClassMemberKind.Setter ||
28384 member.kind === ClassMemberKind.Property;
28385 }
28386 function evaluateHostExpressionBindings(hostExpr, evaluator) {
28387 const hostMetaMap = evaluator.evaluate(hostExpr);
28388 if (!(hostMetaMap instanceof Map)) {
28389 throw createValueHasWrongTypeError(hostExpr, hostMetaMap, `Decorator host metadata must be an object`);
28390 }
28391 const hostMetadata = {};
28392 hostMetaMap.forEach((value, key) => {
28393 // Resolve Enum references to their declared value.
28394 if (value instanceof EnumValue) {
28395 value = value.resolved;
28396 }
28397 if (typeof key !== 'string') {
28398 throw createValueHasWrongTypeError(hostExpr, key, `Decorator host metadata must be a string -> string object, but found unparseable key`);
28399 }
28400 if (typeof value == 'string') {
28401 hostMetadata[key] = value;
28402 }
28403 else if (value instanceof DynamicValue) {
28404 hostMetadata[key] = new WrappedNodeExpr(value.node);
28405 }
28406 else {
28407 throw createValueHasWrongTypeError(hostExpr, value, `Decorator host metadata must be a string -> string object, but found unparseable value`);
28408 }
28409 });
28410 const bindings = parseHostBindings(hostMetadata);
28411 const errors = verifyHostBindings(bindings, createSourceSpan(hostExpr));
28412 if (errors.length > 0) {
28413 throw new FatalDiagnosticError(
28414 // TODO: provide more granular diagnostic and output specific host expression that
28415 // triggered an error instead of the whole host object.
28416 ErrorCode.HOST_BINDING_PARSE_ERROR, hostExpr, errors.map((error) => error.msg).join('\n'));
28417 }
28418 return bindings;
28419 }
28420 function extractHostBindings$1(members, evaluator, coreModule, metadata) {
28421 let bindings;
28422 if (metadata && metadata.has('host')) {
28423 bindings = evaluateHostExpressionBindings(metadata.get('host'), evaluator);
28424 }
28425 else {
28426 bindings = parseHostBindings({});
28427 }
28428 filterToMembersWithDecorator(members, 'HostBinding', coreModule)
28429 .forEach(({ member, decorators }) => {
28430 decorators.forEach(decorator => {
28431 let hostPropertyName = member.name;
28432 if (decorator.args !== null && decorator.args.length > 0) {
28433 if (decorator.args.length !== 1) {
28434 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `@HostBinding can have at most one argument, got ${decorator.args.length} argument(s)`);
28435 }
28436 const resolved = evaluator.evaluate(decorator.args[0]);
28437 if (typeof resolved !== 'string') {
28438 throw createValueHasWrongTypeError(Decorator.nodeForError(decorator), resolved, `@HostBinding's argument must be a string`);
28439 }
28440 hostPropertyName = resolved;
28441 }
28442 // Since this is a decorator, we know that the value is a class member. Always access it
28443 // through `this` so that further down the line it can't be confused for a literal value
28444 // (e.g. if there's a property called `true`). There is no size penalty, because all
28445 // values (except literals) are converted to `ctx.propName` eventually.
28446 bindings.properties[hostPropertyName] = getSafePropertyAccessString('this', member.name);
28447 });
28448 });
28449 filterToMembersWithDecorator(members, 'HostListener', coreModule)
28450 .forEach(({ member, decorators }) => {
28451 decorators.forEach(decorator => {
28452 let eventName = member.name;
28453 let args = [];
28454 if (decorator.args !== null && decorator.args.length > 0) {
28455 if (decorator.args.length > 2) {
28456 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, decorator.args[2], `@HostListener can have at most two arguments`);
28457 }
28458 const resolved = evaluator.evaluate(decorator.args[0]);
28459 if (typeof resolved !== 'string') {
28460 throw createValueHasWrongTypeError(decorator.args[0], resolved, `@HostListener's event name argument must be a string`);
28461 }
28462 eventName = resolved;
28463 if (decorator.args.length === 2) {
28464 const expression = decorator.args[1];
28465 const resolvedArgs = evaluator.evaluate(decorator.args[1]);
28466 if (!isStringArrayOrDie(resolvedArgs, '@HostListener.args', expression)) {
28467 throw createValueHasWrongTypeError(decorator.args[1], resolvedArgs, `@HostListener's second argument must be a string array`);
28468 }
28469 args = resolvedArgs;
28470 }
28471 }
28472 bindings.listeners[eventName] = `${member.name}(${args.join(',')})`;
28473 });
28474 });
28475 return bindings;
28476 }
28477 const QUERY_TYPES = new Set([
28478 'ContentChild',
28479 'ContentChildren',
28480 'ViewChild',
28481 'ViewChildren',
28482 ]);
28483
28484 /**
28485 * @license
28486 * Copyright Google LLC All Rights Reserved.
28487 *
28488 * Use of this source code is governed by an MIT-style license that can be
28489 * found in the LICENSE file at https://angular.io/license
28490 */
28491 const EMPTY_MAP = new Map();
28492 const EMPTY_ARRAY = [];
28493 /**
28494 * `DecoratorHandler` which handles the `@Component` annotation.
28495 */
28496 class ComponentDecoratorHandler {
28497 constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, refEmitter, defaultImportRecorder, depTracker, injectableRegistry, annotateForClosureCompiler) {
28498 this.reflector = reflector;
28499 this.evaluator = evaluator;
28500 this.metaRegistry = metaRegistry;
28501 this.metaReader = metaReader;
28502 this.scopeReader = scopeReader;
28503 this.scopeRegistry = scopeRegistry;
28504 this.typeCheckScopeRegistry = typeCheckScopeRegistry;
28505 this.resourceRegistry = resourceRegistry;
28506 this.isCore = isCore;
28507 this.resourceLoader = resourceLoader;
28508 this.rootDirs = rootDirs;
28509 this.defaultPreserveWhitespaces = defaultPreserveWhitespaces;
28510 this.i18nUseExternalIds = i18nUseExternalIds;
28511 this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
28512 this.usePoisonedData = usePoisonedData;
28513 this.i18nNormalizeLineEndingsInICUs = i18nNormalizeLineEndingsInICUs;
28514 this.moduleResolver = moduleResolver;
28515 this.cycleAnalyzer = cycleAnalyzer;
28516 this.refEmitter = refEmitter;
28517 this.defaultImportRecorder = defaultImportRecorder;
28518 this.depTracker = depTracker;
28519 this.injectableRegistry = injectableRegistry;
28520 this.annotateForClosureCompiler = annotateForClosureCompiler;
28521 this.literalCache = new Map();
28522 this.elementSchemaRegistry = new DomElementSchemaRegistry();
28523 /**
28524 * During the asynchronous preanalyze phase, it's necessary to parse the template to extract
28525 * any potential <link> tags which might need to be loaded. This cache ensures that work is not
28526 * thrown away, and the parsed template is reused during the analyze phase.
28527 */
28528 this.preanalyzeTemplateCache = new Map();
28529 this.precedence = HandlerPrecedence.PRIMARY;
28530 this.name = ComponentDecoratorHandler.name;
28531 }
28532 detect(node, decorators) {
28533 if (!decorators) {
28534 return undefined;
28535 }
28536 const decorator = findAngularDecorator(decorators, 'Component', this.isCore);
28537 if (decorator !== undefined) {
28538 return {
28539 trigger: decorator.node,
28540 decorator,
28541 metadata: decorator,
28542 };
28543 }
28544 else {
28545 return undefined;
28546 }
28547 }
28548 preanalyze(node, decorator) {
28549 // In preanalyze, resource URLs associated with the component are asynchronously preloaded via
28550 // the resourceLoader. This is the only time async operations are allowed for a component.
28551 // These resources are:
28552 //
28553 // - the templateUrl, if there is one
28554 // - any styleUrls if present
28555 // - any stylesheets referenced from <link> tags in the template itself
28556 //
28557 // As a result of the last one, the template must be parsed as part of preanalysis to extract
28558 // <link> tags, which may involve waiting for the templateUrl to be resolved first.
28559 // If preloading isn't possible, then skip this step.
28560 if (!this.resourceLoader.canPreload) {
28561 return undefined;
28562 }
28563 const meta = this._resolveLiteral(decorator);
28564 const component = reflectObjectLiteral(meta);
28565 const containingFile = node.getSourceFile().fileName;
28566 const resolveStyleUrl = (styleUrl, nodeForError, resourceType) => {
28567 const resourceUrl = this._resolveResourceOrThrow(styleUrl, containingFile, nodeForError, resourceType);
28568 return this.resourceLoader.preload(resourceUrl);
28569 };
28570 // A Promise that waits for the template and all <link>ed styles within it to be preloaded.
28571 const templateAndTemplateStyleResources = this._preloadAndParseTemplate(node, decorator, component, containingFile)
28572 .then((template) => {
28573 if (template === null) {
28574 return undefined;
28575 }
28576 const nodeForError = getTemplateDeclarationNodeForError(template.declaration);
28577 return Promise
28578 .all(template.styleUrls.map(styleUrl => resolveStyleUrl(styleUrl, nodeForError, 1 /* StylesheetFromTemplate */)))
28579 .then(() => undefined);
28580 });
28581 // Extract all the styleUrls in the decorator.
28582 const componentStyleUrls = this._extractComponentStyleUrls(component);
28583 if (componentStyleUrls === null) {
28584 // A fast path exists if there are no styleUrls, to just wait for
28585 // templateAndTemplateStyleResources.
28586 return templateAndTemplateStyleResources;
28587 }
28588 else {
28589 // Wait for both the template and all styleUrl resources to resolve.
28590 return Promise
28591 .all([
28592 templateAndTemplateStyleResources,
28593 ...componentStyleUrls.map(styleUrl => resolveStyleUrl(styleUrl.url, styleUrl.nodeForError, 2 /* StylesheetFromDecorator */))
28594 ])
28595 .then(() => undefined);
28596 }
28597 }
28598 analyze(node, decorator, flags = HandlerFlags.NONE) {
28599 var _a;
28600 const containingFile = node.getSourceFile().fileName;
28601 this.literalCache.delete(decorator);
28602 // @Component inherits @Directive, so begin by extracting the @Directive metadata and building
28603 // on it.
28604 const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, flags, this.annotateForClosureCompiler, this.elementSchemaRegistry.getDefaultComponentElementName());
28605 if (directiveResult === undefined) {
28606 // `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
28607 // case, compilation of the decorator is skipped. Returning an empty object signifies
28608 // that no analysis was produced.
28609 return {};
28610 }
28611 // Next, read the `@Component`-specific fields.
28612 const { decorator: component, metadata, inputs, outputs } = directiveResult;
28613 // Go through the root directories for this project, and select the one with the smallest
28614 // relative path representation.
28615 const relativeContextFilePath = this.rootDirs.reduce((previous, rootDir) => {
28616 const candidate = relative(absoluteFrom(rootDir), absoluteFrom(containingFile));
28617 if (previous === undefined || candidate.length < previous.length) {
28618 return candidate;
28619 }
28620 else {
28621 return previous;
28622 }
28623 }, undefined);
28624 // Note that we could technically combine the `viewProvidersRequiringFactory` and
28625 // `providersRequiringFactory` into a single set, but we keep the separate so that
28626 // we can distinguish where an error is coming from when logging the diagnostics in `resolve`.
28627 let viewProvidersRequiringFactory = null;
28628 let providersRequiringFactory = null;
28629 let wrappedViewProviders = null;
28630 if (component.has('viewProviders')) {
28631 const viewProviders = component.get('viewProviders');
28632 viewProvidersRequiringFactory =
28633 resolveProvidersRequiringFactory(viewProviders, this.reflector, this.evaluator);
28634 wrappedViewProviders = new WrappedNodeExpr(this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(viewProviders) :
28635 viewProviders);
28636 }
28637 if (component.has('providers')) {
28638 providersRequiringFactory = resolveProvidersRequiringFactory(component.get('providers'), this.reflector, this.evaluator);
28639 }
28640 // Parse the template.
28641 // If a preanalyze phase was executed, the template may already exist in parsed form, so check
28642 // the preanalyzeTemplateCache.
28643 // Extract a closure of the template parsing code so that it can be reparsed with different
28644 // options if needed, like in the indexing pipeline.
28645 let template;
28646 if (this.preanalyzeTemplateCache.has(node)) {
28647 // The template was parsed in preanalyze. Use it and delete it to save memory.
28648 const preanalyzed = this.preanalyzeTemplateCache.get(node);
28649 this.preanalyzeTemplateCache.delete(node);
28650 template = preanalyzed;
28651 }
28652 else {
28653 const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
28654 template = this.extractTemplate(node, templateDecl);
28655 }
28656 const templateResource = template.isInline ? { path: null, expression: component.get('template') } : {
28657 path: absoluteFrom(template.declaration.resolvedTemplateUrl),
28658 expression: template.sourceMapping.node
28659 };
28660 // Figure out the set of styles. The ordering here is important: external resources (styleUrls)
28661 // precede inline styles, and styles defined in the template override styles defined in the
28662 // component.
28663 let styles = [];
28664 const styleResources = this._extractStyleResources(component, containingFile);
28665 const styleUrls = [
28666 ...this._extractComponentStyleUrls(component), ...this._extractTemplateStyleUrls(template)
28667 ];
28668 for (const styleUrl of styleUrls) {
28669 const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
28670 2 /* StylesheetFromDecorator */ :
28671 1 /* StylesheetFromTemplate */;
28672 const resourceUrl = this._resolveResourceOrThrow(styleUrl.url, containingFile, styleUrl.nodeForError, resourceType);
28673 const resourceStr = this.resourceLoader.load(resourceUrl);
28674 styles.push(resourceStr);
28675 if (this.depTracker !== null) {
28676 this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl));
28677 }
28678 }
28679 let inlineStyles = null;
28680 if (component.has('styles')) {
28681 const litStyles = parseFieldArrayValue(component, 'styles', this.evaluator);
28682 if (litStyles !== null) {
28683 inlineStyles = [...litStyles];
28684 styles.push(...litStyles);
28685 }
28686 }
28687 if (template.styles.length > 0) {
28688 styles.push(...template.styles);
28689 }
28690 const encapsulation = this._resolveEnumValue(component, 'encapsulation', 'ViewEncapsulation') || 0;
28691 const changeDetection = this._resolveEnumValue(component, 'changeDetection', 'ChangeDetectionStrategy');
28692 let animations = null;
28693 if (component.has('animations')) {
28694 animations = new WrappedNodeExpr(component.get('animations'));
28695 }
28696 const output = {
28697 analysis: {
28698 baseClass: readBaseClass$1(node, this.reflector, this.evaluator),
28699 inputs,
28700 outputs,
28701 meta: Object.assign(Object.assign({}, metadata), { template: {
28702 nodes: template.nodes,
28703 ngContentSelectors: template.ngContentSelectors,
28704 }, encapsulation, interpolation: (_a = template.interpolationConfig) !== null && _a !== void 0 ? _a : DEFAULT_INTERPOLATION_CONFIG, styles,
28705 // These will be replaced during the compilation step, after all `NgModule`s have been
28706 // analyzed and the full compilation scope for the component can be realized.
28707 animations, viewProviders: wrappedViewProviders, i18nUseExternalIds: this.i18nUseExternalIds, relativeContextFilePath }),
28708 typeCheckMeta: extractDirectiveTypeCheckMeta(node, inputs, this.reflector),
28709 metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
28710 template,
28711 providersRequiringFactory,
28712 viewProvidersRequiringFactory,
28713 inlineStyles,
28714 styleUrls,
28715 resources: {
28716 styles: styleResources,
28717 template: templateResource,
28718 },
28719 isPoisoned: false,
28720 },
28721 };
28722 if (changeDetection !== null) {
28723 output.analysis.meta.changeDetection = changeDetection;
28724 }
28725 return output;
28726 }
28727 register(node, analysis) {
28728 // Register this component's information with the `MetadataRegistry`. This ensures that
28729 // the information about the component is available during the compile() phase.
28730 const ref = new Reference$1(node);
28731 this.metaRegistry.registerDirectiveMetadata(Object.assign(Object.assign({ ref, name: node.name.text, selector: analysis.meta.selector, exportAs: analysis.meta.exportAs, inputs: analysis.inputs, outputs: analysis.outputs, queries: analysis.meta.queries.map(query => query.propertyName), isComponent: true, baseClass: analysis.baseClass }, analysis.typeCheckMeta), { isPoisoned: analysis.isPoisoned, isStructural: false }));
28732 this.resourceRegistry.registerResources(analysis.resources, node);
28733 this.injectableRegistry.registerInjectable(node);
28734 }
28735 index(context, node, analysis) {
28736 if (analysis.isPoisoned && !this.usePoisonedData) {
28737 return null;
28738 }
28739 const scope = this.scopeReader.getScopeForComponent(node);
28740 const selector = analysis.meta.selector;
28741 const matcher = new SelectorMatcher();
28742 if (scope !== null) {
28743 if ((scope.compilation.isPoisoned || scope.exported.isPoisoned) && !this.usePoisonedData) {
28744 // Don't bother indexing components which had erroneous scopes, unless specifically
28745 // requested.
28746 return null;
28747 }
28748 for (const directive of scope.compilation.directives) {
28749 if (directive.selector !== null) {
28750 matcher.addSelectables(CssSelector.parse(directive.selector), directive);
28751 }
28752 }
28753 }
28754 const binder = new R3TargetBinder(matcher);
28755 const boundTemplate = binder.bind({ template: analysis.template.diagNodes });
28756 context.addComponent({
28757 declaration: node,
28758 selector,
28759 boundTemplate,
28760 templateMeta: {
28761 isInline: analysis.template.isInline,
28762 file: analysis.template.file,
28763 },
28764 });
28765 }
28766 typeCheck(ctx, node, meta) {
28767 if (this.typeCheckScopeRegistry === null || !ts$1.isClassDeclaration(node)) {
28768 return;
28769 }
28770 if (meta.isPoisoned && !this.usePoisonedData) {
28771 return;
28772 }
28773 const scope = this.typeCheckScopeRegistry.getTypeCheckScope(node);
28774 if (scope.isPoisoned && !this.usePoisonedData) {
28775 // Don't type-check components that had errors in their scopes, unless requested.
28776 return;
28777 }
28778 const binder = new R3TargetBinder(scope.matcher);
28779 ctx.addTemplate(new Reference$1(node), binder, meta.template.diagNodes, scope.pipes, scope.schemas, meta.template.sourceMapping, meta.template.file, meta.template.errors);
28780 }
28781 resolve(node, analysis) {
28782 if (analysis.isPoisoned && !this.usePoisonedData) {
28783 return {};
28784 }
28785 const context = node.getSourceFile();
28786 // Check whether this component was registered with an NgModule. If so, it should be compiled
28787 // under that module's compilation scope.
28788 const scope = this.scopeReader.getScopeForComponent(node);
28789 let metadata = analysis.meta;
28790 const data = {
28791 directives: EMPTY_ARRAY,
28792 pipes: EMPTY_MAP,
28793 declarationListEmitMode: 0 /* Direct */,
28794 };
28795 if (scope !== null && (!scope.compilation.isPoisoned || this.usePoisonedData)) {
28796 const matcher = new SelectorMatcher();
28797 for (const dir of scope.compilation.directives) {
28798 if (dir.selector !== null) {
28799 matcher.addSelectables(CssSelector.parse(dir.selector), dir);
28800 }
28801 }
28802 const pipes = new Map();
28803 for (const pipe of scope.compilation.pipes) {
28804 pipes.set(pipe.name, pipe.ref);
28805 }
28806 // Next, the component template AST is bound using the R3TargetBinder. This produces a
28807 // BoundTarget, which is similar to a ts.TypeChecker.
28808 const binder = new R3TargetBinder(matcher);
28809 const bound = binder.bind({ template: metadata.template.nodes });
28810 const usedDirectives = bound.getUsedDirectives().map(directive => {
28811 return {
28812 ref: directive.ref,
28813 type: this.refEmitter.emit(directive.ref, context),
28814 selector: directive.selector,
28815 inputs: directive.inputs.propertyNames,
28816 outputs: directive.outputs.propertyNames,
28817 exportAs: directive.exportAs,
28818 };
28819 });
28820 const usedPipes = [];
28821 for (const pipeName of bound.getUsedPipes()) {
28822 if (!pipes.has(pipeName)) {
28823 continue;
28824 }
28825 const pipe = pipes.get(pipeName);
28826 usedPipes.push({
28827 ref: pipe,
28828 pipeName,
28829 expression: this.refEmitter.emit(pipe, context),
28830 });
28831 }
28832 // Scan through the directives/pipes actually used in the template and check whether any
28833 // import which needs to be generated would create a cycle.
28834 const cycleDetected = usedDirectives.some(dir => this._isCyclicImport(dir.type, context)) ||
28835 usedPipes.some(pipe => this._isCyclicImport(pipe.expression, context));
28836 if (!cycleDetected) {
28837 // No cycle was detected. Record the imports that need to be created in the cycle detector
28838 // so that future cyclic import checks consider their production.
28839 for (const { type } of usedDirectives) {
28840 this._recordSyntheticImport(type, context);
28841 }
28842 for (const { expression } of usedPipes) {
28843 this._recordSyntheticImport(expression, context);
28844 }
28845 // Check whether the directive/pipe arrays in ɵcmp need to be wrapped in closures.
28846 // This is required if any directive/pipe reference is to a declaration in the same file
28847 // but declared after this component.
28848 const wrapDirectivesAndPipesInClosure = usedDirectives.some(dir => isExpressionForwardReference(dir.type, node.name, context)) ||
28849 usedPipes.some(pipe => isExpressionForwardReference(pipe.expression, node.name, context));
28850 data.directives = usedDirectives;
28851 data.pipes = new Map(usedPipes.map(pipe => [pipe.pipeName, pipe.expression]));
28852 data.declarationListEmitMode = wrapDirectivesAndPipesInClosure ?
28853 1 /* Closure */ :
28854 0 /* Direct */;
28855 }
28856 else {
28857 // Declaring the directiveDefs/pipeDefs arrays directly would require imports that would
28858 // create a cycle. Instead, mark this component as requiring remote scoping, so that the
28859 // NgModule file will take care of setting the directives for the component.
28860 this.scopeRegistry.setComponentRemoteScope(node, usedDirectives.map(dir => dir.ref), usedPipes.map(pipe => pipe.ref));
28861 }
28862 }
28863 const diagnostics = [];
28864 if (analysis.providersRequiringFactory !== null &&
28865 analysis.meta.providers instanceof WrappedNodeExpr) {
28866 const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.meta.providers.node, this.injectableRegistry);
28867 diagnostics.push(...providerDiagnostics);
28868 }
28869 if (analysis.viewProvidersRequiringFactory !== null &&
28870 analysis.meta.viewProviders instanceof WrappedNodeExpr) {
28871 const viewProviderDiagnostics = getProviderDiagnostics(analysis.viewProvidersRequiringFactory, analysis.meta.viewProviders.node, this.injectableRegistry);
28872 diagnostics.push(...viewProviderDiagnostics);
28873 }
28874 const directiveDiagnostics = getDirectiveDiagnostics(node, this.metaReader, this.evaluator, this.reflector, this.scopeRegistry, 'Component');
28875 if (directiveDiagnostics !== null) {
28876 diagnostics.push(...directiveDiagnostics);
28877 }
28878 if (diagnostics.length > 0) {
28879 return { diagnostics };
28880 }
28881 return { data };
28882 }
28883 updateResources(node, analysis) {
28884 const containingFile = node.getSourceFile().fileName;
28885 // If the template is external, re-parse it.
28886 const templateDecl = analysis.template.declaration;
28887 if (!templateDecl.isInline) {
28888 analysis.template = this.extractTemplate(node, templateDecl);
28889 }
28890 // Update any external stylesheets and rebuild the combined 'styles' list.
28891 // TODO(alxhub): write tests for styles when the primary compiler uses the updateResources path
28892 let styles = [];
28893 if (analysis.styleUrls !== null) {
28894 for (const styleUrl of analysis.styleUrls) {
28895 const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
28896 2 /* StylesheetFromDecorator */ :
28897 1 /* StylesheetFromTemplate */;
28898 const resolvedStyleUrl = this._resolveResourceOrThrow(styleUrl.url, containingFile, styleUrl.nodeForError, resourceType);
28899 const styleText = this.resourceLoader.load(resolvedStyleUrl);
28900 styles.push(styleText);
28901 }
28902 }
28903 if (analysis.inlineStyles !== null) {
28904 for (const styleText of analysis.inlineStyles) {
28905 styles.push(styleText);
28906 }
28907 }
28908 for (const styleText of analysis.template.styles) {
28909 styles.push(styleText);
28910 }
28911 analysis.meta.styles = styles;
28912 }
28913 compileFull(node, analysis, resolution, pool) {
28914 if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
28915 return [];
28916 }
28917 const meta = Object.assign(Object.assign({}, analysis.meta), resolution);
28918 const def = compileComponentFromMetadata(meta, pool, makeBindingParser());
28919 return this.compileComponent(analysis, def);
28920 }
28921 compilePartial(node, analysis, resolution) {
28922 if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
28923 return [];
28924 }
28925 const meta = Object.assign(Object.assign({}, analysis.meta), resolution);
28926 const def = compileDeclareComponentFromMetadata(meta, analysis.template);
28927 return this.compileComponent(analysis, def);
28928 }
28929 compileComponent(analysis, { expression: initializer, type }) {
28930 const factoryRes = compileNgFactoryDefField(Object.assign(Object.assign({}, analysis.meta), { injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Component }));
28931 if (analysis.metadataStmt !== null) {
28932 factoryRes.statements.push(analysis.metadataStmt);
28933 }
28934 return [
28935 factoryRes, {
28936 name: 'ɵcmp',
28937 initializer,
28938 statements: [],
28939 type,
28940 }
28941 ];
28942 }
28943 _resolveLiteral(decorator) {
28944 if (this.literalCache.has(decorator)) {
28945 return this.literalCache.get(decorator);
28946 }
28947 if (decorator.args === null || decorator.args.length !== 1) {
28948 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @Component decorator`);
28949 }
28950 const meta = unwrapExpression(decorator.args[0]);
28951 if (!ts$1.isObjectLiteralExpression(meta)) {
28952 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, `Decorator argument must be literal.`);
28953 }
28954 this.literalCache.set(decorator, meta);
28955 return meta;
28956 }
28957 _resolveEnumValue(component, field, enumSymbolName) {
28958 let resolved = null;
28959 if (component.has(field)) {
28960 const expr = component.get(field);
28961 const value = this.evaluator.evaluate(expr);
28962 if (value instanceof EnumValue && isAngularCoreReference(value.enumRef, enumSymbolName)) {
28963 resolved = value.resolved;
28964 }
28965 else {
28966 throw createValueHasWrongTypeError(expr, value, `${field} must be a member of ${enumSymbolName} enum from @angular/core`);
28967 }
28968 }
28969 return resolved;
28970 }
28971 _extractComponentStyleUrls(component) {
28972 if (!component.has('styleUrls')) {
28973 return [];
28974 }
28975 return this._extractStyleUrlsFromExpression(component.get('styleUrls'));
28976 }
28977 _extractStyleUrlsFromExpression(styleUrlsExpr) {
28978 const styleUrls = [];
28979 if (ts$1.isArrayLiteralExpression(styleUrlsExpr)) {
28980 for (const styleUrlExpr of styleUrlsExpr.elements) {
28981 if (ts$1.isSpreadElement(styleUrlExpr)) {
28982 styleUrls.push(...this._extractStyleUrlsFromExpression(styleUrlExpr.expression));
28983 }
28984 else {
28985 const styleUrl = this.evaluator.evaluate(styleUrlExpr);
28986 if (typeof styleUrl !== 'string') {
28987 throw createValueHasWrongTypeError(styleUrlExpr, styleUrl, 'styleUrl must be a string');
28988 }
28989 styleUrls.push({
28990 url: styleUrl,
28991 source: 2 /* StylesheetFromDecorator */,
28992 nodeForError: styleUrlExpr,
28993 });
28994 }
28995 }
28996 }
28997 else {
28998 const evaluatedStyleUrls = this.evaluator.evaluate(styleUrlsExpr);
28999 if (!isStringArray(evaluatedStyleUrls)) {
29000 throw createValueHasWrongTypeError(styleUrlsExpr, evaluatedStyleUrls, 'styleUrls must be an array of strings');
29001 }
29002 for (const styleUrl of evaluatedStyleUrls) {
29003 styleUrls.push({
29004 url: styleUrl,
29005 source: 2 /* StylesheetFromDecorator */,
29006 nodeForError: styleUrlsExpr,
29007 });
29008 }
29009 }
29010 return styleUrls;
29011 }
29012 _extractStyleResources(component, containingFile) {
29013 const styles = new Set();
29014 function stringLiteralElements(array) {
29015 return array.elements.filter((e) => ts$1.isStringLiteralLike(e));
29016 }
29017 // If styleUrls is a literal array, process each resource url individually and
29018 // register ones that are string literals.
29019 const styleUrlsExpr = component.get('styleUrls');
29020 if (styleUrlsExpr !== undefined && ts$1.isArrayLiteralExpression(styleUrlsExpr)) {
29021 for (const expression of stringLiteralElements(styleUrlsExpr)) {
29022 const resourceUrl = this._resolveResourceOrThrow(expression.text, containingFile, expression, 2 /* StylesheetFromDecorator */);
29023 styles.add({ path: absoluteFrom(resourceUrl), expression });
29024 }
29025 }
29026 const stylesExpr = component.get('styles');
29027 if (stylesExpr !== undefined && ts$1.isArrayLiteralExpression(stylesExpr)) {
29028 for (const expression of stringLiteralElements(stylesExpr)) {
29029 styles.add({ path: null, expression });
29030 }
29031 }
29032 return styles;
29033 }
29034 _preloadAndParseTemplate(node, decorator, component, containingFile) {
29035 if (component.has('templateUrl')) {
29036 // Extract the templateUrl and preload it.
29037 const templateUrlExpr = component.get('templateUrl');
29038 const templateUrl = this.evaluator.evaluate(templateUrlExpr);
29039 if (typeof templateUrl !== 'string') {
29040 throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
29041 }
29042 const resourceUrl = this._resolveResourceOrThrow(templateUrl, containingFile, templateUrlExpr, 0 /* Template */);
29043 const templatePromise = this.resourceLoader.preload(resourceUrl);
29044 // If the preload worked, then actually load and parse the template, and wait for any style
29045 // URLs to resolve.
29046 if (templatePromise !== undefined) {
29047 return templatePromise.then(() => {
29048 const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
29049 const template = this.extractTemplate(node, templateDecl);
29050 this.preanalyzeTemplateCache.set(node, template);
29051 return template;
29052 });
29053 }
29054 else {
29055 return Promise.resolve(null);
29056 }
29057 }
29058 else {
29059 const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
29060 const template = this.extractTemplate(node, templateDecl);
29061 this.preanalyzeTemplateCache.set(node, template);
29062 return Promise.resolve(template);
29063 }
29064 }
29065 extractTemplate(node, template) {
29066 if (template.isInline) {
29067 let templateStr;
29068 let templateLiteral = null;
29069 let templateUrl = '';
29070 let templateRange = null;
29071 let sourceMapping;
29072 let escapedString = false;
29073 // We only support SourceMaps for inline templates that are simple string literals.
29074 if (ts$1.isStringLiteral(template.expression) ||
29075 ts$1.isNoSubstitutionTemplateLiteral(template.expression)) {
29076 // the start and end of the `templateExpr` node includes the quotation marks, which we must
29077 // strip
29078 templateRange = getTemplateRange(template.expression);
29079 templateStr = template.expression.getSourceFile().text;
29080 templateLiteral = template.expression;
29081 templateUrl = template.templateUrl;
29082 escapedString = true;
29083 sourceMapping = {
29084 type: 'direct',
29085 node: template.expression,
29086 };
29087 }
29088 else {
29089 const resolvedTemplate = this.evaluator.evaluate(template.expression);
29090 if (typeof resolvedTemplate !== 'string') {
29091 throw createValueHasWrongTypeError(template.expression, resolvedTemplate, 'template must be a string');
29092 }
29093 templateStr = resolvedTemplate;
29094 sourceMapping = {
29095 type: 'indirect',
29096 node: template.expression,
29097 componentClass: node,
29098 template: templateStr,
29099 };
29100 }
29101 return Object.assign(Object.assign({}, this._parseTemplate(template, templateStr, templateRange, escapedString)), { sourceMapping, declaration: template });
29102 }
29103 else {
29104 const templateStr = this.resourceLoader.load(template.resolvedTemplateUrl);
29105 if (this.depTracker !== null) {
29106 this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(template.resolvedTemplateUrl));
29107 }
29108 return Object.assign(Object.assign({}, this._parseTemplate(template, templateStr, /* templateRange */ null,
29109 /* escapedString */ false)), { sourceMapping: {
29110 type: 'external',
29111 componentClass: node,
29112 // TODO(alxhub): TS in g3 is unable to make this inference on its own, so cast it here
29113 // until g3 is able to figure this out.
29114 node: template.templateUrlExpression,
29115 template: templateStr,
29116 templateUrl: template.resolvedTemplateUrl,
29117 }, declaration: template });
29118 }
29119 }
29120 _parseTemplate(template, templateStr, templateRange, escapedString) {
29121 // We always normalize line endings if the template has been escaped (i.e. is inline).
29122 const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs;
29123 const parsedTemplate = parseTemplate(templateStr, template.sourceMapUrl, {
29124 preserveWhitespaces: template.preserveWhitespaces,
29125 interpolationConfig: template.interpolationConfig,
29126 range: templateRange !== null && templateRange !== void 0 ? templateRange : undefined,
29127 escapedString,
29128 enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
29129 i18nNormalizeLineEndingsInICUs,
29130 isInline: template.isInline,
29131 });
29132 // Unfortunately, the primary parse of the template above may not contain accurate source map
29133 // information. If used directly, it would result in incorrect code locations in template
29134 // errors, etc. There are three main problems:
29135 //
29136 // 1. `preserveWhitespaces: false` annihilates the correctness of template source mapping, as
29137 // the whitespace transformation changes the contents of HTML text nodes before they're
29138 // parsed into Angular expressions.
29139 // 2. `preserveLineEndings: false` causes growing misalignments in templates that use '\r\n'
29140 // line endings, by normalizing them to '\n'.
29141 // 3. By default, the template parser strips leading trivia characters (like spaces, tabs, and
29142 // newlines). This also destroys source mapping information.
29143 //
29144 // In order to guarantee the correctness of diagnostics, templates are parsed a second time
29145 // with the above options set to preserve source mappings.
29146 const { nodes: diagNodes } = parseTemplate(templateStr, template.sourceMapUrl, {
29147 preserveWhitespaces: true,
29148 preserveLineEndings: true,
29149 interpolationConfig: template.interpolationConfig,
29150 range: templateRange !== null && templateRange !== void 0 ? templateRange : undefined,
29151 escapedString,
29152 enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
29153 i18nNormalizeLineEndingsInICUs,
29154 leadingTriviaChars: [],
29155 isInline: template.isInline,
29156 });
29157 return Object.assign(Object.assign({}, parsedTemplate), { diagNodes, template: template.isInline ? new WrappedNodeExpr(template.expression) : templateStr, templateUrl: template.resolvedTemplateUrl, isInline: template.isInline, file: new ParseSourceFile(templateStr, template.resolvedTemplateUrl) });
29158 }
29159 parseTemplateDeclaration(decorator, component, containingFile) {
29160 let preserveWhitespaces = this.defaultPreserveWhitespaces;
29161 if (component.has('preserveWhitespaces')) {
29162 const expr = component.get('preserveWhitespaces');
29163 const value = this.evaluator.evaluate(expr);
29164 if (typeof value !== 'boolean') {
29165 throw createValueHasWrongTypeError(expr, value, 'preserveWhitespaces must be a boolean');
29166 }
29167 preserveWhitespaces = value;
29168 }
29169 let interpolationConfig = DEFAULT_INTERPOLATION_CONFIG;
29170 if (component.has('interpolation')) {
29171 const expr = component.get('interpolation');
29172 const value = this.evaluator.evaluate(expr);
29173 if (!Array.isArray(value) || value.length !== 2 ||
29174 !value.every(element => typeof element === 'string')) {
29175 throw createValueHasWrongTypeError(expr, value, 'interpolation must be an array with 2 elements of string type');
29176 }
29177 interpolationConfig = InterpolationConfig.fromArray(value);
29178 }
29179 if (component.has('templateUrl')) {
29180 const templateUrlExpr = component.get('templateUrl');
29181 const templateUrl = this.evaluator.evaluate(templateUrlExpr);
29182 if (typeof templateUrl !== 'string') {
29183 throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
29184 }
29185 const resourceUrl = this._resolveResourceOrThrow(templateUrl, containingFile, templateUrlExpr, 0 /* Template */);
29186 return {
29187 isInline: false,
29188 interpolationConfig,
29189 preserveWhitespaces,
29190 templateUrl,
29191 templateUrlExpression: templateUrlExpr,
29192 resolvedTemplateUrl: resourceUrl,
29193 sourceMapUrl: sourceMapUrl(resourceUrl),
29194 };
29195 }
29196 else if (component.has('template')) {
29197 return {
29198 isInline: true,
29199 interpolationConfig,
29200 preserveWhitespaces,
29201 expression: component.get('template'),
29202 templateUrl: containingFile,
29203 resolvedTemplateUrl: containingFile,
29204 sourceMapUrl: containingFile,
29205 };
29206 }
29207 else {
29208 throw new FatalDiagnosticError(ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), 'component is missing a template');
29209 }
29210 }
29211 _expressionToImportedFile(expr, origin) {
29212 if (!(expr instanceof ExternalExpr)) {
29213 return null;
29214 }
29215 // Figure out what file is being imported.
29216 return this.moduleResolver.resolveModule(expr.value.moduleName, origin.fileName);
29217 }
29218 _isCyclicImport(expr, origin) {
29219 const imported = this._expressionToImportedFile(expr, origin);
29220 if (imported === null) {
29221 return false;
29222 }
29223 // Check whether the import is legal.
29224 return this.cycleAnalyzer.wouldCreateCycle(origin, imported);
29225 }
29226 _recordSyntheticImport(expr, origin) {
29227 const imported = this._expressionToImportedFile(expr, origin);
29228 if (imported === null) {
29229 return;
29230 }
29231 this.cycleAnalyzer.recordSyntheticImport(origin, imported);
29232 }
29233 /**
29234 * Resolve the url of a resource relative to the file that contains the reference to it.
29235 *
29236 * Throws a FatalDiagnosticError when unable to resolve the file.
29237 */
29238 _resolveResourceOrThrow(file, basePath, nodeForError, resourceType) {
29239 try {
29240 return this.resourceLoader.resolve(file, basePath);
29241 }
29242 catch (e) {
29243 let errorText;
29244 switch (resourceType) {
29245 case 0 /* Template */:
29246 errorText = `Could not find template file '${file}'.`;
29247 break;
29248 case 1 /* StylesheetFromTemplate */:
29249 errorText = `Could not find stylesheet file '${file}' linked from the template.`;
29250 break;
29251 case 2 /* StylesheetFromDecorator */:
29252 errorText = `Could not find stylesheet file '${file}'.`;
29253 break;
29254 }
29255 throw new FatalDiagnosticError(ErrorCode.COMPONENT_RESOURCE_NOT_FOUND, nodeForError, errorText);
29256 }
29257 }
29258 _extractTemplateStyleUrls(template) {
29259 if (template.styleUrls === null) {
29260 return [];
29261 }
29262 const nodeForError = getTemplateDeclarationNodeForError(template.declaration);
29263 return template.styleUrls.map(url => ({ url, source: 1 /* StylesheetFromTemplate */, nodeForError }));
29264 }
29265 }
29266 function getTemplateRange(templateExpr) {
29267 const startPos = templateExpr.getStart() + 1;
29268 const { line, character } = ts$1.getLineAndCharacterOfPosition(templateExpr.getSourceFile(), startPos);
29269 return {
29270 startPos,
29271 startLine: line,
29272 startCol: character,
29273 endPos: templateExpr.getEnd() - 1,
29274 };
29275 }
29276 function sourceMapUrl(resourceUrl) {
29277 if (!tsSourceMapBug29300Fixed()) {
29278 // By removing the template URL we are telling the translator not to try to
29279 // map the external source file to the generated code, since the version
29280 // of TS that is running does not support it.
29281 return '';
29282 }
29283 else {
29284 return resourceUrl;
29285 }
29286 }
29287 /** Determines if the result of an evaluation is a string array. */
29288 function isStringArray(resolvedValue) {
29289 return Array.isArray(resolvedValue) && resolvedValue.every(elem => typeof elem === 'string');
29290 }
29291 /** Determines the node to use for debugging purposes for the given TemplateDeclaration. */
29292 function getTemplateDeclarationNodeForError(declaration) {
29293 // TODO(zarend): Change this to if/else when that is compatible with g3. This uses a switch
29294 // because if/else fails to compile on g3. That is because g3 compiles this in non-strict mode
29295 // where type inference does not work correctly.
29296 switch (declaration.isInline) {
29297 case true:
29298 return declaration.expression;
29299 case false:
29300 return declaration.templateUrlExpression;
29301 }
29302 }
29303
29304 /**
29305 * @license
29306 * Copyright Google LLC All Rights Reserved.
29307 *
29308 * Use of this source code is governed by an MIT-style license that can be
29309 * found in the LICENSE file at https://angular.io/license
29310 */
29311 /**
29312 * Adapts the `compileIvyInjectable` compiler for `@Injectable` decorators to the Ivy compiler.
29313 */
29314 class InjectableDecoratorHandler {
29315 constructor(reflector, defaultImportRecorder, isCore, strictCtorDeps, injectableRegistry,
29316 /**
29317 * What to do if the injectable already contains a ɵprov property.
29318 *
29319 * If true then an error diagnostic is reported.
29320 * If false then there is no error and a new ɵprov property is not added.
29321 */
29322 errorOnDuplicateProv = true) {
29323 this.reflector = reflector;
29324 this.defaultImportRecorder = defaultImportRecorder;
29325 this.isCore = isCore;
29326 this.strictCtorDeps = strictCtorDeps;
29327 this.injectableRegistry = injectableRegistry;
29328 this.errorOnDuplicateProv = errorOnDuplicateProv;
29329 this.precedence = HandlerPrecedence.SHARED;
29330 this.name = InjectableDecoratorHandler.name;
29331 }
29332 detect(node, decorators) {
29333 if (!decorators) {
29334 return undefined;
29335 }
29336 const decorator = findAngularDecorator(decorators, 'Injectable', this.isCore);
29337 if (decorator !== undefined) {
29338 return {
29339 trigger: decorator.node,
29340 decorator: decorator,
29341 metadata: decorator,
29342 };
29343 }
29344 else {
29345 return undefined;
29346 }
29347 }
29348 analyze(node, decorator) {
29349 const meta = extractInjectableMetadata(node, decorator, this.reflector);
29350 const decorators = this.reflector.getDecoratorsOfDeclaration(node);
29351 return {
29352 analysis: {
29353 meta,
29354 ctorDeps: extractInjectableCtorDeps(node, meta, decorator, this.reflector, this.defaultImportRecorder, this.isCore, this.strictCtorDeps),
29355 metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore),
29356 // Avoid generating multiple factories if a class has
29357 // more Angular decorators, apart from Injectable.
29358 needsFactory: !decorators ||
29359 decorators.every(current => !isAngularCore(current) || current.name === 'Injectable')
29360 },
29361 };
29362 }
29363 register(node) {
29364 this.injectableRegistry.registerInjectable(node);
29365 }
29366 compileFull(node, analysis) {
29367 const res = compileInjectable(analysis.meta);
29368 const statements = res.statements;
29369 const results = [];
29370 if (analysis.needsFactory) {
29371 const meta = analysis.meta;
29372 const factoryRes = compileNgFactoryDefField({
29373 name: meta.name,
29374 type: meta.type,
29375 internalType: meta.internalType,
29376 typeArgumentCount: meta.typeArgumentCount,
29377 deps: analysis.ctorDeps,
29378 injectFn: Identifiers.inject,
29379 target: R3FactoryTarget.Injectable,
29380 });
29381 if (analysis.metadataStmt !== null) {
29382 factoryRes.statements.push(analysis.metadataStmt);
29383 }
29384 results.push(factoryRes);
29385 }
29386 const ɵprov = this.reflector.getMembersOfClass(node).find(member => member.name === 'ɵprov');
29387 if (ɵprov !== undefined && this.errorOnDuplicateProv) {
29388 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.');
29389 }
29390 if (ɵprov === undefined) {
29391 // Only add a new ɵprov if there is not one already
29392 results.push({ name: 'ɵprov', initializer: res.expression, statements, type: res.type });
29393 }
29394 return results;
29395 }
29396 }
29397 /**
29398 * Read metadata from the `@Injectable` decorator and produce the `IvyInjectableMetadata`, the
29399 * input metadata needed to run `compileIvyInjectable`.
29400 *
29401 * A `null` return value indicates this is @Injectable has invalid data.
29402 */
29403 function extractInjectableMetadata(clazz, decorator, reflector) {
29404 const name = clazz.name.text;
29405 const type = wrapTypeReference(reflector, clazz);
29406 const internalType = new WrappedNodeExpr(reflector.getInternalNameOfClass(clazz));
29407 const typeArgumentCount = reflector.getGenericArityOfClass(clazz) || 0;
29408 if (decorator.args === null) {
29409 throw new FatalDiagnosticError(ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), '@Injectable must be called');
29410 }
29411 if (decorator.args.length === 0) {
29412 return {
29413 name,
29414 type,
29415 typeArgumentCount,
29416 internalType,
29417 providedIn: new LiteralExpr(null),
29418 };
29419 }
29420 else if (decorator.args.length === 1) {
29421 const metaNode = decorator.args[0];
29422 // Firstly make sure the decorator argument is an inline literal - if not, it's illegal to
29423 // transport references from one location to another. This is the problem that lowering
29424 // used to solve - if this restriction proves too undesirable we can re-implement lowering.
29425 if (!ts$1.isObjectLiteralExpression(metaNode)) {
29426 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, metaNode, `@Injectable argument must be an object literal`);
29427 }
29428 // Resolve the fields of the literal into a map of field name to expression.
29429 const meta = reflectObjectLiteral(metaNode);
29430 let providedIn = new LiteralExpr(null);
29431 if (meta.has('providedIn')) {
29432 providedIn = new WrappedNodeExpr(meta.get('providedIn'));
29433 }
29434 let userDeps = undefined;
29435 if ((meta.has('useClass') || meta.has('useFactory')) && meta.has('deps')) {
29436 const depsExpr = meta.get('deps');
29437 if (!ts$1.isArrayLiteralExpression(depsExpr)) {
29438 throw new FatalDiagnosticError(ErrorCode.VALUE_NOT_LITERAL, depsExpr, `@Injectable deps metadata must be an inline array`);
29439 }
29440 userDeps = depsExpr.elements.map(dep => getDep(dep, reflector));
29441 }
29442 if (meta.has('useValue')) {
29443 return {
29444 name,
29445 type,
29446 typeArgumentCount,
29447 internalType,
29448 providedIn,
29449 useValue: new WrappedNodeExpr(unwrapForwardRef(meta.get('useValue'), reflector)),
29450 };
29451 }
29452 else if (meta.has('useExisting')) {
29453 return {
29454 name,
29455 type,
29456 typeArgumentCount,
29457 internalType,
29458 providedIn,
29459 useExisting: new WrappedNodeExpr(unwrapForwardRef(meta.get('useExisting'), reflector)),
29460 };
29461 }
29462 else if (meta.has('useClass')) {
29463 return {
29464 name,
29465 type,
29466 typeArgumentCount,
29467 internalType,
29468 providedIn,
29469 useClass: new WrappedNodeExpr(unwrapForwardRef(meta.get('useClass'), reflector)),
29470 userDeps,
29471 };
29472 }
29473 else if (meta.has('useFactory')) {
29474 // useFactory is special - the 'deps' property must be analyzed.
29475 const factory = new WrappedNodeExpr(meta.get('useFactory'));
29476 return {
29477 name,
29478 type,
29479 typeArgumentCount,
29480 internalType,
29481 providedIn,
29482 useFactory: factory,
29483 userDeps,
29484 };
29485 }
29486 else {
29487 return { name, type, typeArgumentCount, internalType, providedIn };
29488 }
29489 }
29490 else {
29491 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, decorator.args[2], 'Too many arguments to @Injectable');
29492 }
29493 }
29494 function extractInjectableCtorDeps(clazz, meta, decorator, reflector, defaultImportRecorder, isCore, strictCtorDeps) {
29495 if (decorator.args === null) {
29496 throw new FatalDiagnosticError(ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), '@Injectable must be called');
29497 }
29498 let ctorDeps = null;
29499 if (decorator.args.length === 0) {
29500 // Ideally, using @Injectable() would have the same effect as using @Injectable({...}), and be
29501 // subject to the same validation. However, existing Angular code abuses @Injectable, applying
29502 // it to things like abstract classes with constructors that were never meant for use with
29503 // Angular's DI.
29504 //
29505 // To deal with this, @Injectable() without an argument is more lenient, and if the
29506 // constructor signature does not work for DI then a factory definition (ɵfac) that throws is
29507 // generated.
29508 if (strictCtorDeps) {
29509 ctorDeps = getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
29510 }
29511 else {
29512 ctorDeps = unwrapConstructorDependencies(getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore));
29513 }
29514 return ctorDeps;
29515 }
29516 else if (decorator.args.length === 1) {
29517 const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
29518 if (strictCtorDeps && meta.useValue === undefined && meta.useExisting === undefined &&
29519 meta.useClass === undefined && meta.useFactory === undefined) {
29520 // Since use* was not provided, validate the deps according to strictCtorDeps.
29521 ctorDeps = validateConstructorDependencies(clazz, rawCtorDeps);
29522 }
29523 else {
29524 ctorDeps = unwrapConstructorDependencies(rawCtorDeps);
29525 }
29526 }
29527 return ctorDeps;
29528 }
29529 function getDep(dep, reflector) {
29530 const meta = {
29531 token: new WrappedNodeExpr(dep),
29532 attribute: null,
29533 host: false,
29534 resolved: R3ResolvedDependencyType.Token,
29535 optional: false,
29536 self: false,
29537 skipSelf: false,
29538 };
29539 function maybeUpdateDecorator(dec, reflector, token) {
29540 const source = reflector.getImportOfIdentifier(dec);
29541 if (source === null || source.from !== '@angular/core') {
29542 return;
29543 }
29544 switch (source.name) {
29545 case 'Inject':
29546 if (token !== undefined) {
29547 meta.token = new WrappedNodeExpr(token);
29548 }
29549 break;
29550 case 'Optional':
29551 meta.optional = true;
29552 break;
29553 case 'SkipSelf':
29554 meta.skipSelf = true;
29555 break;
29556 case 'Self':
29557 meta.self = true;
29558 break;
29559 }
29560 }
29561 if (ts$1.isArrayLiteralExpression(dep)) {
29562 dep.elements.forEach(el => {
29563 if (ts$1.isIdentifier(el)) {
29564 maybeUpdateDecorator(el, reflector);
29565 }
29566 else if (ts$1.isNewExpression(el) && ts$1.isIdentifier(el.expression)) {
29567 const token = el.arguments && el.arguments.length > 0 && el.arguments[0] || undefined;
29568 maybeUpdateDecorator(el.expression, reflector, token);
29569 }
29570 });
29571 }
29572 return meta;
29573 }
29574
29575 /**
29576 * @license
29577 * Copyright Google LLC All Rights Reserved.
29578 *
29579 * Use of this source code is governed by an MIT-style license that can be
29580 * found in the LICENSE file at https://angular.io/license
29581 */
29582 /**
29583 * Compiles @NgModule annotations to ngModuleDef fields.
29584 *
29585 * TODO(alxhub): handle injector side of things as well.
29586 */
29587 class NgModuleDecoratorHandler {
29588 constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, factoryTracker, defaultImportRecorder, annotateForClosureCompiler, injectableRegistry, localeId) {
29589 this.reflector = reflector;
29590 this.evaluator = evaluator;
29591 this.metaReader = metaReader;
29592 this.metaRegistry = metaRegistry;
29593 this.scopeRegistry = scopeRegistry;
29594 this.referencesRegistry = referencesRegistry;
29595 this.isCore = isCore;
29596 this.routeAnalyzer = routeAnalyzer;
29597 this.refEmitter = refEmitter;
29598 this.factoryTracker = factoryTracker;
29599 this.defaultImportRecorder = defaultImportRecorder;
29600 this.annotateForClosureCompiler = annotateForClosureCompiler;
29601 this.injectableRegistry = injectableRegistry;
29602 this.localeId = localeId;
29603 this.precedence = HandlerPrecedence.PRIMARY;
29604 this.name = NgModuleDecoratorHandler.name;
29605 }
29606 detect(node, decorators) {
29607 if (!decorators) {
29608 return undefined;
29609 }
29610 const decorator = findAngularDecorator(decorators, 'NgModule', this.isCore);
29611 if (decorator !== undefined) {
29612 return {
29613 trigger: decorator.node,
29614 decorator: decorator,
29615 metadata: decorator,
29616 };
29617 }
29618 else {
29619 return undefined;
29620 }
29621 }
29622 analyze(node, decorator) {
29623 const name = node.name.text;
29624 if (decorator.args === null || decorator.args.length > 1) {
29625 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @NgModule decorator`);
29626 }
29627 // @NgModule can be invoked without arguments. In case it is, pretend as if a blank object
29628 // literal was specified. This simplifies the code below.
29629 const meta = decorator.args.length === 1 ? unwrapExpression(decorator.args[0]) :
29630 ts$1.createObjectLiteral([]);
29631 if (!ts$1.isObjectLiteralExpression(meta)) {
29632 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@NgModule argument must be an object literal');
29633 }
29634 const ngModule = reflectObjectLiteral(meta);
29635 if (ngModule.has('jit')) {
29636 // The only allowed value is true, so there's no need to expand further.
29637 return {};
29638 }
29639 const moduleResolvers = combineResolvers([
29640 ref => this._extractModuleFromModuleWithProvidersFn(ref.node),
29641 forwardRefResolver,
29642 ]);
29643 const diagnostics = [];
29644 // Extract the module declarations, imports, and exports.
29645 let declarationRefs = [];
29646 let rawDeclarations = null;
29647 if (ngModule.has('declarations')) {
29648 rawDeclarations = ngModule.get('declarations');
29649 const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver);
29650 declarationRefs =
29651 this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations');
29652 // Look through the declarations to make sure they're all a part of the current compilation.
29653 for (const ref of declarationRefs) {
29654 if (ref.node.getSourceFile().isDeclarationFile) {
29655 const errorNode = ref.getOriginForDiagnostics(rawDeclarations);
29656 diagnostics.push(makeDiagnostic(ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, `Cannot declare '${ref.node.name
29657 .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.`)]));
29658 }
29659 }
29660 }
29661 if (diagnostics.length > 0) {
29662 return { diagnostics };
29663 }
29664 let importRefs = [];
29665 let rawImports = null;
29666 if (ngModule.has('imports')) {
29667 rawImports = ngModule.get('imports');
29668 const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers);
29669 importRefs = this.resolveTypeList(rawImports, importsMeta, name, 'imports');
29670 }
29671 let exportRefs = [];
29672 let rawExports = null;
29673 if (ngModule.has('exports')) {
29674 rawExports = ngModule.get('exports');
29675 const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers);
29676 exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports');
29677 this.referencesRegistry.add(node, ...exportRefs);
29678 }
29679 let bootstrapRefs = [];
29680 if (ngModule.has('bootstrap')) {
29681 const expr = ngModule.get('bootstrap');
29682 const bootstrapMeta = this.evaluator.evaluate(expr, forwardRefResolver);
29683 bootstrapRefs = this.resolveTypeList(expr, bootstrapMeta, name, 'bootstrap');
29684 }
29685 const schemas = [];
29686 if (ngModule.has('schemas')) {
29687 const rawExpr = ngModule.get('schemas');
29688 const result = this.evaluator.evaluate(rawExpr);
29689 if (!Array.isArray(result)) {
29690 throw createValueHasWrongTypeError(rawExpr, result, `NgModule.schemas must be an array`);
29691 }
29692 for (const schemaRef of result) {
29693 if (!(schemaRef instanceof Reference$1)) {
29694 throw createValueHasWrongTypeError(rawExpr, result, 'NgModule.schemas must be an array of schemas');
29695 }
29696 const id = schemaRef.getIdentityIn(schemaRef.node.getSourceFile());
29697 if (id === null || schemaRef.ownedByModuleGuess !== '@angular/core') {
29698 throw createValueHasWrongTypeError(rawExpr, result, 'NgModule.schemas must be an array of schemas');
29699 }
29700 // Since `id` is the `ts.Identifer` within the schema ref's declaration file, it's safe to
29701 // use `id.text` here to figure out which schema is in use. Even if the actual reference was
29702 // renamed when the user imported it, these names will match.
29703 switch (id.text) {
29704 case 'CUSTOM_ELEMENTS_SCHEMA':
29705 schemas.push(CUSTOM_ELEMENTS_SCHEMA);
29706 break;
29707 case 'NO_ERRORS_SCHEMA':
29708 schemas.push(NO_ERRORS_SCHEMA);
29709 break;
29710 default:
29711 throw createValueHasWrongTypeError(rawExpr, schemaRef, `'${schemaRef.debugName}' is not a valid NgModule schema`);
29712 }
29713 }
29714 }
29715 const id = ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id')) : null;
29716 const valueContext = node.getSourceFile();
29717 let typeContext = valueContext;
29718 const typeNode = this.reflector.getDtsDeclaration(node);
29719 if (typeNode !== null) {
29720 typeContext = typeNode.getSourceFile();
29721 }
29722 const bootstrap = bootstrapRefs.map(bootstrap => this._toR3Reference(bootstrap, valueContext, typeContext));
29723 const declarations = declarationRefs.map(decl => this._toR3Reference(decl, valueContext, typeContext));
29724 const imports = importRefs.map(imp => this._toR3Reference(imp, valueContext, typeContext));
29725 const exports = exportRefs.map(exp => this._toR3Reference(exp, valueContext, typeContext));
29726 const isForwardReference = (ref) => isExpressionForwardReference(ref.value, node.name, valueContext);
29727 const containsForwardDecls = bootstrap.some(isForwardReference) ||
29728 declarations.some(isForwardReference) || imports.some(isForwardReference) ||
29729 exports.some(isForwardReference);
29730 const type = wrapTypeReference(this.reflector, node);
29731 const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(node));
29732 const adjacentType = new WrappedNodeExpr(this.reflector.getAdjacentNameOfClass(node));
29733 const ngModuleDef = {
29734 type,
29735 internalType,
29736 adjacentType,
29737 bootstrap,
29738 declarations,
29739 exports,
29740 imports,
29741 containsForwardDecls,
29742 id,
29743 emitInline: false,
29744 // TODO: to be implemented as a part of FW-1004.
29745 schemas: [],
29746 };
29747 const rawProviders = ngModule.has('providers') ? ngModule.get('providers') : null;
29748 const wrapperProviders = rawProviders !== null ?
29749 new WrappedNodeExpr(this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) :
29750 rawProviders) :
29751 null;
29752 // At this point, only add the module's imports as the injectors' imports. Any exported modules
29753 // are added during `resolve`, as we need scope information to be able to filter out directives
29754 // and pipes from the module exports.
29755 const injectorImports = [];
29756 if (ngModule.has('imports')) {
29757 injectorImports.push(new WrappedNodeExpr(ngModule.get('imports')));
29758 }
29759 if (this.routeAnalyzer !== null) {
29760 this.routeAnalyzer.add(node.getSourceFile(), name, rawImports, rawExports, rawProviders);
29761 }
29762 const ngInjectorDef = {
29763 name,
29764 type,
29765 internalType,
29766 deps: getValidConstructorDependencies(node, this.reflector, this.defaultImportRecorder, this.isCore),
29767 providers: wrapperProviders,
29768 imports: injectorImports,
29769 };
29770 return {
29771 analysis: {
29772 id,
29773 schemas: schemas,
29774 mod: ngModuleDef,
29775 inj: ngInjectorDef,
29776 declarations: declarationRefs,
29777 rawDeclarations,
29778 imports: importRefs,
29779 exports: exportRefs,
29780 providers: rawProviders,
29781 providersRequiringFactory: rawProviders ?
29782 resolveProvidersRequiringFactory(rawProviders, this.reflector, this.evaluator) :
29783 null,
29784 metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
29785 factorySymbolName: node.name.text,
29786 },
29787 };
29788 }
29789 register(node, analysis) {
29790 // Register this module's information with the LocalModuleScopeRegistry. This ensures that
29791 // during the compile() phase, the module's metadata is available for selector scope
29792 // computation.
29793 this.metaRegistry.registerNgModuleMetadata({
29794 ref: new Reference$1(node),
29795 schemas: analysis.schemas,
29796 declarations: analysis.declarations,
29797 imports: analysis.imports,
29798 exports: analysis.exports,
29799 rawDeclarations: analysis.rawDeclarations,
29800 });
29801 if (this.factoryTracker !== null) {
29802 this.factoryTracker.track(node.getSourceFile(), {
29803 name: analysis.factorySymbolName,
29804 hasId: analysis.id !== null,
29805 });
29806 }
29807 this.injectableRegistry.registerInjectable(node);
29808 }
29809 resolve(node, analysis) {
29810 const scope = this.scopeRegistry.getScopeOfModule(node);
29811 const diagnostics = [];
29812 const scopeDiagnostics = this.scopeRegistry.getDiagnosticsOfModule(node);
29813 if (scopeDiagnostics !== null) {
29814 diagnostics.push(...scopeDiagnostics);
29815 }
29816 if (analysis.providersRequiringFactory !== null) {
29817 const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.providers, this.injectableRegistry);
29818 diagnostics.push(...providerDiagnostics);
29819 }
29820 const data = {
29821 injectorImports: [],
29822 };
29823 if (scope !== null && !scope.compilation.isPoisoned) {
29824 // Using the scope information, extend the injector's imports using the modules that are
29825 // specified as module exports.
29826 const context = getSourceFile(node);
29827 for (const exportRef of analysis.exports) {
29828 if (isNgModule(exportRef.node, scope.compilation)) {
29829 data.injectorImports.push(this.refEmitter.emit(exportRef, context));
29830 }
29831 }
29832 for (const decl of analysis.declarations) {
29833 const metadata = this.metaReader.getDirectiveMetadata(decl);
29834 if (metadata !== null && metadata.selector === null) {
29835 throw new FatalDiagnosticError(ErrorCode.DIRECTIVE_MISSING_SELECTOR, decl.node, `Directive ${decl.node.name.text} has no selector, please add it!`);
29836 }
29837 }
29838 }
29839 if (diagnostics.length > 0) {
29840 return { diagnostics };
29841 }
29842 if (scope === null || scope.compilation.isPoisoned || scope.exported.isPoisoned ||
29843 scope.reexports === null) {
29844 return { data };
29845 }
29846 else {
29847 return {
29848 data,
29849 reexports: scope.reexports,
29850 };
29851 }
29852 }
29853 compileFull(node, analysis, resolution) {
29854 // Merge the injector imports (which are 'exports' that were later found to be NgModules)
29855 // computed during resolution with the ones from analysis.
29856 const ngInjectorDef = compileInjector(Object.assign(Object.assign({}, analysis.inj), { imports: [...analysis.inj.imports, ...resolution.injectorImports] }));
29857 const ngModuleDef = compileNgModule(analysis.mod);
29858 const ngModuleStatements = ngModuleDef.additionalStatements;
29859 if (analysis.metadataStmt !== null) {
29860 ngModuleStatements.push(analysis.metadataStmt);
29861 }
29862 const context = getSourceFile(node);
29863 for (const decl of analysis.declarations) {
29864 const remoteScope = this.scopeRegistry.getRemoteScope(decl.node);
29865 if (remoteScope !== null) {
29866 const directives = remoteScope.directives.map(directive => this.refEmitter.emit(directive, context));
29867 const pipes = remoteScope.pipes.map(pipe => this.refEmitter.emit(pipe, context));
29868 const directiveArray = new LiteralArrayExpr(directives);
29869 const pipesArray = new LiteralArrayExpr(pipes);
29870 const declExpr = this.refEmitter.emit(decl, context);
29871 const setComponentScope = new ExternalExpr(Identifiers$1.setComponentScope);
29872 const callExpr = new InvokeFunctionExpr(setComponentScope, [declExpr, directiveArray, pipesArray]);
29873 ngModuleStatements.push(callExpr.toStmt());
29874 }
29875 }
29876 const res = [
29877 {
29878 name: 'ɵmod',
29879 initializer: ngModuleDef.expression,
29880 statements: ngModuleStatements,
29881 type: ngModuleDef.type,
29882 },
29883 {
29884 name: 'ɵinj',
29885 initializer: ngInjectorDef.expression,
29886 statements: ngInjectorDef.statements,
29887 type: ngInjectorDef.type,
29888 }
29889 ];
29890 if (this.localeId) {
29891 res.push({
29892 name: 'ɵloc',
29893 initializer: new LiteralExpr(this.localeId),
29894 statements: [],
29895 type: STRING_TYPE
29896 });
29897 }
29898 return res;
29899 }
29900 _toR3Reference(valueRef, valueContext, typeContext) {
29901 if (valueRef.hasOwningModuleGuess) {
29902 return toR3Reference(valueRef, valueRef, valueContext, valueContext, this.refEmitter);
29903 }
29904 else {
29905 let typeRef = valueRef;
29906 let typeNode = this.reflector.getDtsDeclaration(typeRef.node);
29907 if (typeNode !== null && isNamedClassDeclaration(typeNode)) {
29908 typeRef = new Reference$1(typeNode);
29909 }
29910 return toR3Reference(valueRef, typeRef, valueContext, typeContext, this.refEmitter);
29911 }
29912 }
29913 /**
29914 * Given a `FunctionDeclaration`, `MethodDeclaration` or `FunctionExpression`, check if it is
29915 * typed as a `ModuleWithProviders` and return an expression referencing the module if available.
29916 */
29917 _extractModuleFromModuleWithProvidersFn(node) {
29918 const type = node.type || null;
29919 return type &&
29920 (this._reflectModuleFromTypeParam(type, node) || this._reflectModuleFromLiteralType(type));
29921 }
29922 /**
29923 * Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
29924 * `ModuleWithProviders<T>`
29925 * @param type The type to reflect on.
29926 * @returns the identifier of the NgModule type if found, or null otherwise.
29927 */
29928 _reflectModuleFromTypeParam(type, node) {
29929 // Examine the type of the function to see if it's a ModuleWithProviders reference.
29930 if (!ts$1.isTypeReferenceNode(type)) {
29931 return null;
29932 }
29933 const typeName = type &&
29934 (ts$1.isIdentifier(type.typeName) && type.typeName ||
29935 ts$1.isQualifiedName(type.typeName) && type.typeName.right) ||
29936 null;
29937 if (typeName === null) {
29938 return null;
29939 }
29940 // Look at the type itself to see where it comes from.
29941 const id = this.reflector.getImportOfIdentifier(typeName);
29942 // If it's not named ModuleWithProviders, bail.
29943 if (id === null || id.name !== 'ModuleWithProviders') {
29944 return null;
29945 }
29946 // If it's not from @angular/core, bail.
29947 if (!this.isCore && id.from !== '@angular/core') {
29948 return null;
29949 }
29950 // If there's no type parameter specified, bail.
29951 if (type.typeArguments === undefined || type.typeArguments.length !== 1) {
29952 const parent = ts$1.isMethodDeclaration(node) && ts$1.isClassDeclaration(node.parent) ? node.parent : null;
29953 const symbolName = (parent && parent.name ? parent.name.getText() + '.' : '') +
29954 (node.name ? node.name.getText() : 'anonymous');
29955 throw new FatalDiagnosticError(ErrorCode.NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC, type, `${symbolName} returns a ModuleWithProviders type without a generic type argument. ` +
29956 `Please add a generic type argument to the ModuleWithProviders type. If this ` +
29957 `occurrence is in library code you don't control, please contact the library authors.`);
29958 }
29959 const arg = type.typeArguments[0];
29960 return typeNodeToValueExpr(arg);
29961 }
29962 /**
29963 * Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
29964 * `A|B|{ngModule: T}|C`.
29965 * @param type The type to reflect on.
29966 * @returns the identifier of the NgModule type if found, or null otherwise.
29967 */
29968 _reflectModuleFromLiteralType(type) {
29969 if (!ts$1.isIntersectionTypeNode(type)) {
29970 return null;
29971 }
29972 for (const t of type.types) {
29973 if (ts$1.isTypeLiteralNode(t)) {
29974 for (const m of t.members) {
29975 const ngModuleType = ts$1.isPropertySignature(m) && ts$1.isIdentifier(m.name) &&
29976 m.name.text === 'ngModule' && m.type ||
29977 null;
29978 const ngModuleExpression = ngModuleType && typeNodeToValueExpr(ngModuleType);
29979 if (ngModuleExpression) {
29980 return ngModuleExpression;
29981 }
29982 }
29983 }
29984 }
29985 return null;
29986 }
29987 // Verify that a "Declaration" reference is a `ClassDeclaration` reference.
29988 isClassDeclarationReference(ref) {
29989 return this.reflector.isClass(ref.node);
29990 }
29991 /**
29992 * Compute a list of `Reference`s from a resolved metadata value.
29993 */
29994 resolveTypeList(expr, resolvedList, className, arrayName) {
29995 const refList = [];
29996 if (!Array.isArray(resolvedList)) {
29997 throw createValueHasWrongTypeError(expr, resolvedList, `Expected array when reading the NgModule.${arrayName} of ${className}`);
29998 }
29999 resolvedList.forEach((entry, idx) => {
30000 // Unwrap ModuleWithProviders for modules that are locally declared (and thus static
30001 // resolution was able to descend into the function and return an object literal, a Map).
30002 if (entry instanceof Map && entry.has('ngModule')) {
30003 entry = entry.get('ngModule');
30004 }
30005 if (Array.isArray(entry)) {
30006 // Recurse into nested arrays.
30007 refList.push(...this.resolveTypeList(expr, entry, className, arrayName));
30008 }
30009 else if (entry instanceof Reference$1) {
30010 if (!this.isClassDeclarationReference(entry)) {
30011 throw createValueHasWrongTypeError(entry.node, entry, `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a class`);
30012 }
30013 refList.push(entry);
30014 }
30015 else {
30016 // TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array.
30017 throw createValueHasWrongTypeError(expr, entry, `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a reference`);
30018 }
30019 });
30020 return refList;
30021 }
30022 }
30023 function isNgModule(node, compilation) {
30024 return !compilation.directives.some(directive => directive.ref.node === node) &&
30025 !compilation.pipes.some(pipe => pipe.ref.node === node);
30026 }
30027
30028 /**
30029 * @license
30030 * Copyright Google LLC All Rights Reserved.
30031 *
30032 * Use of this source code is governed by an MIT-style license that can be
30033 * found in the LICENSE file at https://angular.io/license
30034 */
30035 class PipeDecoratorHandler {
30036 constructor(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportRecorder, injectableRegistry, isCore) {
30037 this.reflector = reflector;
30038 this.evaluator = evaluator;
30039 this.metaRegistry = metaRegistry;
30040 this.scopeRegistry = scopeRegistry;
30041 this.defaultImportRecorder = defaultImportRecorder;
30042 this.injectableRegistry = injectableRegistry;
30043 this.isCore = isCore;
30044 this.precedence = HandlerPrecedence.PRIMARY;
30045 this.name = PipeDecoratorHandler.name;
30046 }
30047 detect(node, decorators) {
30048 if (!decorators) {
30049 return undefined;
30050 }
30051 const decorator = findAngularDecorator(decorators, 'Pipe', this.isCore);
30052 if (decorator !== undefined) {
30053 return {
30054 trigger: decorator.node,
30055 decorator: decorator,
30056 metadata: decorator,
30057 };
30058 }
30059 else {
30060 return undefined;
30061 }
30062 }
30063 analyze(clazz, decorator) {
30064 const name = clazz.name.text;
30065 const type = wrapTypeReference(this.reflector, clazz);
30066 const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(clazz));
30067 if (decorator.args === null) {
30068 throw new FatalDiagnosticError(ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), `@Pipe must be called`);
30069 }
30070 if (decorator.args.length !== 1) {
30071 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), '@Pipe must have exactly one argument');
30072 }
30073 const meta = unwrapExpression(decorator.args[0]);
30074 if (!ts$1.isObjectLiteralExpression(meta)) {
30075 throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@Pipe must have a literal argument');
30076 }
30077 const pipe = reflectObjectLiteral(meta);
30078 if (!pipe.has('name')) {
30079 throw new FatalDiagnosticError(ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`);
30080 }
30081 const pipeNameExpr = pipe.get('name');
30082 const pipeName = this.evaluator.evaluate(pipeNameExpr);
30083 if (typeof pipeName !== 'string') {
30084 throw createValueHasWrongTypeError(pipeNameExpr, pipeName, `@Pipe.name must be a string`);
30085 }
30086 let pure = true;
30087 if (pipe.has('pure')) {
30088 const expr = pipe.get('pure');
30089 const pureValue = this.evaluator.evaluate(expr);
30090 if (typeof pureValue !== 'boolean') {
30091 throw createValueHasWrongTypeError(expr, pureValue, `@Pipe.pure must be a boolean`);
30092 }
30093 pure = pureValue;
30094 }
30095 return {
30096 analysis: {
30097 meta: {
30098 name,
30099 type,
30100 internalType,
30101 typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0,
30102 pipeName,
30103 deps: getValidConstructorDependencies(clazz, this.reflector, this.defaultImportRecorder, this.isCore),
30104 pure,
30105 },
30106 metadataStmt: generateSetClassMetadataCall(clazz, this.reflector, this.defaultImportRecorder, this.isCore),
30107 },
30108 };
30109 }
30110 register(node, analysis) {
30111 const ref = new Reference$1(node);
30112 this.metaRegistry.registerPipeMetadata({ ref, name: analysis.meta.pipeName });
30113 this.injectableRegistry.registerInjectable(node);
30114 }
30115 resolve(node) {
30116 const duplicateDeclData = this.scopeRegistry.getDuplicateDeclarations(node);
30117 if (duplicateDeclData !== null) {
30118 // This pipe was declared twice (or more).
30119 return {
30120 diagnostics: [makeDuplicateDeclarationError(node, duplicateDeclData, 'Pipe')],
30121 };
30122 }
30123 return {};
30124 }
30125 compileFull(node, analysis) {
30126 const meta = analysis.meta;
30127 const res = compilePipeFromMetadata(meta);
30128 const factoryRes = compileNgFactoryDefField(Object.assign(Object.assign({}, meta), { injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Pipe }));
30129 if (analysis.metadataStmt !== null) {
30130 factoryRes.statements.push(analysis.metadataStmt);
30131 }
30132 return [
30133 factoryRes, {
30134 name: 'ɵpipe',
30135 initializer: res.expression,
30136 statements: [],
30137 type: res.type,
30138 }
30139 ];
30140 }
30141 }
30142
30143 /**
30144 * @license
30145 * Copyright Google LLC All Rights Reserved.
30146 *
30147 * Use of this source code is governed by an MIT-style license that can be
30148 * found in the LICENSE file at https://angular.io/license
30149 */
30150 /**
30151 * This registry does nothing, since ngtsc does not currently need
30152 * this functionality.
30153 * The ngcc tool implements a working version for its purposes.
30154 */
30155 class NoopReferencesRegistry {
30156 add(source, ...references) { }
30157 }
30158
30159 /**
30160 * @license
30161 * Copyright Google LLC All Rights Reserved.
30162 *
30163 * Use of this source code is governed by an MIT-style license that can be
30164 * found in the LICENSE file at https://angular.io/license
30165 */
30166 /**
30167 * Analyzes a `ts.Program` for cycles.
30168 */
30169 class CycleAnalyzer {
30170 constructor(importGraph) {
30171 this.importGraph = importGraph;
30172 }
30173 /**
30174 * Check whether adding an import from `from` to `to` would create a cycle in the `ts.Program`.
30175 */
30176 wouldCreateCycle(from, to) {
30177 // Import of 'from' -> 'to' is illegal if an edge 'to' -> 'from' already exists.
30178 return this.importGraph.transitiveImportsOf(to).has(from);
30179 }
30180 /**
30181 * Record a synthetic import from `from` to `to`.
30182 *
30183 * This is an import that doesn't exist in the `ts.Program` but will be considered as part of the
30184 * import graph for cycle creation.
30185 */
30186 recordSyntheticImport(from, to) {
30187 this.importGraph.addSyntheticImport(from, to);
30188 }
30189 }
30190
30191 /**
30192 * @license
30193 * Copyright Google LLC All Rights Reserved.
30194 *
30195 * Use of this source code is governed by an MIT-style license that can be
30196 * found in the LICENSE file at https://angular.io/license
30197 */
30198 /**
30199 * A cached graph of imports in the `ts.Program`.
30200 *
30201 * The `ImportGraph` keeps track of dependencies (imports) of individual `ts.SourceFile`s. Only
30202 * dependencies within the same program are tracked; imports into packages on NPM are not.
30203 */
30204 class ImportGraph {
30205 constructor(resolver) {
30206 this.resolver = resolver;
30207 this.map = new Map();
30208 }
30209 /**
30210 * List the direct (not transitive) imports of a given `ts.SourceFile`.
30211 *
30212 * This operation is cached.
30213 */
30214 importsOf(sf) {
30215 if (!this.map.has(sf)) {
30216 this.map.set(sf, this.scanImports(sf));
30217 }
30218 return this.map.get(sf);
30219 }
30220 /**
30221 * Lists the transitive imports of a given `ts.SourceFile`.
30222 */
30223 transitiveImportsOf(sf) {
30224 const imports = new Set();
30225 this.transitiveImportsOfHelper(sf, imports);
30226 return imports;
30227 }
30228 transitiveImportsOfHelper(sf, results) {
30229 if (results.has(sf)) {
30230 return;
30231 }
30232 results.add(sf);
30233 this.importsOf(sf).forEach(imported => {
30234 this.transitiveImportsOfHelper(imported, results);
30235 });
30236 }
30237 /**
30238 * Add a record of an import from `sf` to `imported`, that's not present in the original
30239 * `ts.Program` but will be remembered by the `ImportGraph`.
30240 */
30241 addSyntheticImport(sf, imported) {
30242 if (isLocalFile(imported)) {
30243 this.importsOf(sf).add(imported);
30244 }
30245 }
30246 scanImports(sf) {
30247 const imports = new Set();
30248 // Look through the source file for import statements.
30249 sf.statements.forEach(stmt => {
30250 if ((ts$1.isImportDeclaration(stmt) || ts$1.isExportDeclaration(stmt)) &&
30251 stmt.moduleSpecifier !== undefined && ts$1.isStringLiteral(stmt.moduleSpecifier)) {
30252 // Resolve the module to a file, and check whether that file is in the ts.Program.
30253 const moduleName = stmt.moduleSpecifier.text;
30254 const moduleFile = this.resolver.resolveModule(moduleName, sf.fileName);
30255 if (moduleFile !== null && isLocalFile(moduleFile)) {
30256 // Record this local import.
30257 imports.add(moduleFile);
30258 }
30259 }
30260 });
30261 return imports;
30262 }
30263 }
30264 function isLocalFile(sf) {
30265 return !sf.fileName.endsWith('.d.ts');
30266 }
30267
30268 /**
30269 * @license
30270 * Copyright Google LLC All Rights Reserved.
30271 *
30272 * Use of this source code is governed by an MIT-style license that can be
30273 * found in the LICENSE file at https://angular.io/license
30274 */
30275 /**
30276 * Produce `ts.Diagnostic`s for classes that are visible from exported types (e.g. directives
30277 * exposed by exported `NgModule`s) that are not themselves exported.
30278 *
30279 * This function reconciles two concepts:
30280 *
30281 * A class is Exported if it's exported from the main library `entryPoint` file.
30282 * A class is Visible if, via Angular semantics, a downstream consumer can import an Exported class
30283 * and be affected by the class in question. For example, an Exported NgModule may expose a
30284 * directive class to its consumers. Consumers that import the NgModule may have the directive
30285 * applied to elements in their templates. In this case, the directive is considered Visible.
30286 *
30287 * `checkForPrivateExports` attempts to verify that all Visible classes are Exported, and report
30288 * `ts.Diagnostic`s for those that aren't.
30289 *
30290 * @param entryPoint `ts.SourceFile` of the library's entrypoint, which should export the library's
30291 * public API.
30292 * @param checker `ts.TypeChecker` for the current program.
30293 * @param refGraph `ReferenceGraph` tracking the visibility of Angular types.
30294 * @returns an array of `ts.Diagnostic`s representing errors when visible classes are not exported
30295 * properly.
30296 */
30297 function checkForPrivateExports(entryPoint, checker, refGraph) {
30298 const diagnostics = [];
30299 // Firstly, compute the exports of the entry point. These are all the Exported classes.
30300 const topLevelExports = new Set();
30301 // Do this via `ts.TypeChecker.getExportsOfModule`.
30302 const moduleSymbol = checker.getSymbolAtLocation(entryPoint);
30303 if (moduleSymbol === undefined) {
30304 throw new Error(`Internal error: failed to get symbol for entrypoint`);
30305 }
30306 const exportedSymbols = checker.getExportsOfModule(moduleSymbol);
30307 // Loop through the exported symbols, de-alias if needed, and add them to `topLevelExports`.
30308 // TODO(alxhub): use proper iteration when build.sh is removed. (#27762)
30309 exportedSymbols.forEach(symbol => {
30310 if (symbol.flags & ts$1.SymbolFlags.Alias) {
30311 symbol = checker.getAliasedSymbol(symbol);
30312 }
30313 const decl = symbol.valueDeclaration;
30314 if (decl !== undefined) {
30315 topLevelExports.add(decl);
30316 }
30317 });
30318 // Next, go through each exported class and expand it to the set of classes it makes Visible,
30319 // using the `ReferenceGraph`. For each Visible class, verify that it's also Exported, and queue
30320 // an error if it isn't. `checkedSet` ensures only one error is queued per class.
30321 const checkedSet = new Set();
30322 // Loop through each Exported class.
30323 // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
30324 topLevelExports.forEach(mainExport => {
30325 // Loop through each class made Visible by the Exported class.
30326 refGraph.transitiveReferencesOf(mainExport).forEach(transitiveReference => {
30327 // Skip classes which have already been checked.
30328 if (checkedSet.has(transitiveReference)) {
30329 return;
30330 }
30331 checkedSet.add(transitiveReference);
30332 // Verify that the Visible class is also Exported.
30333 if (!topLevelExports.has(transitiveReference)) {
30334 // This is an error, `mainExport` makes `transitiveReference` Visible, but
30335 // `transitiveReference` is not Exported from the entrypoint. Construct a diagnostic to
30336 // give to the user explaining the situation.
30337 const descriptor = getDescriptorOfDeclaration(transitiveReference);
30338 const name = getNameOfDeclaration(transitiveReference);
30339 // Construct the path of visibility, from `mainExport` to `transitiveReference`.
30340 let visibleVia = 'NgModule exports';
30341 const transitivePath = refGraph.pathFrom(mainExport, transitiveReference);
30342 if (transitivePath !== null) {
30343 visibleVia = transitivePath.map(seg => getNameOfDeclaration(seg)).join(' -> ');
30344 }
30345 const diagnostic = Object.assign(Object.assign({ category: ts$1.DiagnosticCategory.Error, code: ngErrorCode(ErrorCode.SYMBOL_NOT_EXPORTED), file: transitiveReference.getSourceFile() }, getPosOfDeclaration(transitiveReference)), { messageText: `Unsupported private ${descriptor} ${name}. This ${descriptor} is visible to consumers via ${visibleVia}, but is not exported from the top-level library entrypoint.` });
30346 diagnostics.push(diagnostic);
30347 }
30348 });
30349 });
30350 return diagnostics;
30351 }
30352 function getPosOfDeclaration(decl) {
30353 const node = getIdentifierOfDeclaration(decl) || decl;
30354 return {
30355 start: node.getStart(),
30356 length: node.getEnd() + 1 - node.getStart(),
30357 };
30358 }
30359 function getIdentifierOfDeclaration(decl) {
30360 if ((ts$1.isClassDeclaration(decl) || ts$1.isVariableDeclaration(decl) ||
30361 ts$1.isFunctionDeclaration(decl)) &&
30362 decl.name !== undefined && ts$1.isIdentifier(decl.name)) {
30363 return decl.name;
30364 }
30365 else {
30366 return null;
30367 }
30368 }
30369 function getNameOfDeclaration(decl) {
30370 const id = getIdentifierOfDeclaration(decl);
30371 return id !== null ? id.text : '(unnamed)';
30372 }
30373 function getDescriptorOfDeclaration(decl) {
30374 switch (decl.kind) {
30375 case ts$1.SyntaxKind.ClassDeclaration:
30376 return 'class';
30377 case ts$1.SyntaxKind.FunctionDeclaration:
30378 return 'function';
30379 case ts$1.SyntaxKind.VariableDeclaration:
30380 return 'variable';
30381 case ts$1.SyntaxKind.EnumDeclaration:
30382 return 'enum';
30383 default:
30384 return 'declaration';
30385 }
30386 }
30387
30388 /**
30389 * @license
30390 * Copyright Google LLC All Rights Reserved.
30391 *
30392 * Use of this source code is governed by an MIT-style license that can be
30393 * found in the LICENSE file at https://angular.io/license
30394 */
30395 class ReferenceGraph {
30396 constructor() {
30397 this.references = new Map();
30398 }
30399 add(from, to) {
30400 if (!this.references.has(from)) {
30401 this.references.set(from, new Set());
30402 }
30403 this.references.get(from).add(to);
30404 }
30405 transitiveReferencesOf(target) {
30406 const set = new Set();
30407 this.collectTransitiveReferences(set, target);
30408 return set;
30409 }
30410 pathFrom(source, target) {
30411 return this.collectPathFrom(source, target, new Set());
30412 }
30413 collectPathFrom(source, target, seen) {
30414 if (source === target) {
30415 // Looking for a path from the target to itself - that path is just the target. This is the
30416 // "base case" of the search.
30417 return [target];
30418 }
30419 else if (seen.has(source)) {
30420 // The search has already looked through this source before.
30421 return null;
30422 }
30423 // Consider outgoing edges from `source`.
30424 seen.add(source);
30425 if (!this.references.has(source)) {
30426 // There are no outgoing edges from `source`.
30427 return null;
30428 }
30429 else {
30430 // Look through the outgoing edges of `source`.
30431 // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
30432 let candidatePath = null;
30433 this.references.get(source).forEach(edge => {
30434 // Early exit if a path has already been found.
30435 if (candidatePath !== null) {
30436 return;
30437 }
30438 // Look for a path from this outgoing edge to `target`.
30439 const partialPath = this.collectPathFrom(edge, target, seen);
30440 if (partialPath !== null) {
30441 // A path exists from `edge` to `target`. Insert `source` at the beginning.
30442 candidatePath = [source, ...partialPath];
30443 }
30444 });
30445 return candidatePath;
30446 }
30447 }
30448 collectTransitiveReferences(set, decl) {
30449 if (this.references.has(decl)) {
30450 // TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
30451 this.references.get(decl).forEach(ref => {
30452 if (!set.has(ref)) {
30453 set.add(ref);
30454 this.collectTransitiveReferences(set, ref);
30455 }
30456 });
30457 }
30458 }
30459 }
30460
30461 /**
30462 * @license
30463 * Copyright Google LLC All Rights Reserved.
30464 *
30465 * Use of this source code is governed by an MIT-style license that can be
30466 * found in the LICENSE file at https://angular.io/license
30467 */
30468 /**
30469 * An implementation of the `DependencyTracker` dependency graph API.
30470 *
30471 * The `FileDependencyGraph`'s primary job is to determine whether a given file has "logically"
30472 * changed, given the set of physical changes (direct changes to files on disk).
30473 *
30474 * A file is logically changed if at least one of three conditions is met:
30475 *
30476 * 1. The file itself has physically changed.
30477 * 2. One of its dependencies has physically changed.
30478 * 3. One of its resource dependencies has physically changed.
30479 */
30480 class FileDependencyGraph {
30481 constructor() {
30482 this.nodes = new Map();
30483 }
30484 addDependency(from, on) {
30485 this.nodeFor(from).dependsOn.add(on.fileName);
30486 }
30487 addResourceDependency(from, resource) {
30488 this.nodeFor(from).usesResources.add(resource);
30489 }
30490 addTransitiveDependency(from, on) {
30491 const nodeFrom = this.nodeFor(from);
30492 nodeFrom.dependsOn.add(on.fileName);
30493 const nodeOn = this.nodeFor(on);
30494 for (const dep of nodeOn.dependsOn) {
30495 nodeFrom.dependsOn.add(dep);
30496 }
30497 }
30498 addTransitiveResources(from, resourcesOf) {
30499 const nodeFrom = this.nodeFor(from);
30500 const nodeOn = this.nodeFor(resourcesOf);
30501 for (const dep of nodeOn.usesResources) {
30502 nodeFrom.usesResources.add(dep);
30503 }
30504 }
30505 recordDependencyAnalysisFailure(file) {
30506 this.nodeFor(file).failedAnalysis = true;
30507 }
30508 getResourceDependencies(from) {
30509 const node = this.nodes.get(from);
30510 return node ? [...node.usesResources] : [];
30511 }
30512 isStale(sf, changedTsPaths, changedResources) {
30513 return isLogicallyChanged(sf, this.nodeFor(sf), changedTsPaths, EMPTY_SET, changedResources);
30514 }
30515 /**
30516 * Update the current dependency graph from a previous one, incorporating a set of physical
30517 * changes.
30518 *
30519 * This method performs two tasks:
30520 *
30521 * 1. For files which have not logically changed, their dependencies from `previous` are added to
30522 * `this` graph.
30523 * 2. For files which have logically changed, they're added to a set of logically changed files
30524 * which is eventually returned.
30525 *
30526 * In essence, for build `n`, this method performs:
30527 *
30528 * G(n) + L(n) = G(n - 1) + P(n)
30529 *
30530 * where:
30531 *
30532 * G(n) = the dependency graph of build `n`
30533 * L(n) = the logically changed files from build n - 1 to build n.
30534 * P(n) = the physically changed files from build n - 1 to build n.
30535 */
30536 updateWithPhysicalChanges(previous, changedTsPaths, deletedTsPaths, changedResources) {
30537 const logicallyChanged = new Set();
30538 for (const sf of previous.nodes.keys()) {
30539 const node = previous.nodeFor(sf);
30540 if (isLogicallyChanged(sf, node, changedTsPaths, deletedTsPaths, changedResources)) {
30541 logicallyChanged.add(sf.fileName);
30542 }
30543 else if (!deletedTsPaths.has(sf.fileName)) {
30544 this.nodes.set(sf, {
30545 dependsOn: new Set(node.dependsOn),
30546 usesResources: new Set(node.usesResources),
30547 failedAnalysis: false,
30548 });
30549 }
30550 }
30551 return logicallyChanged;
30552 }
30553 nodeFor(sf) {
30554 if (!this.nodes.has(sf)) {
30555 this.nodes.set(sf, {
30556 dependsOn: new Set(),
30557 usesResources: new Set(),
30558 failedAnalysis: false,
30559 });
30560 }
30561 return this.nodes.get(sf);
30562 }
30563 }
30564 /**
30565 * Determine whether `sf` has logically changed, given its dependencies and the set of physically
30566 * changed files and resources.
30567 */
30568 function isLogicallyChanged(sf, node, changedTsPaths, deletedTsPaths, changedResources) {
30569 // A file is assumed to have logically changed if its dependencies could not be determined
30570 // accurately.
30571 if (node.failedAnalysis) {
30572 return true;
30573 }
30574 // A file is logically changed if it has physically changed itself (including being deleted).
30575 if (changedTsPaths.has(sf.fileName) || deletedTsPaths.has(sf.fileName)) {
30576 return true;
30577 }
30578 // A file is logically changed if one of its dependencies has physically changed.
30579 for (const dep of node.dependsOn) {
30580 if (changedTsPaths.has(dep) || deletedTsPaths.has(dep)) {
30581 return true;
30582 }
30583 }
30584 // A file is logically changed if one of its resources has physically changed.
30585 for (const dep of node.usesResources) {
30586 if (changedResources.has(dep)) {
30587 return true;
30588 }
30589 }
30590 return false;
30591 }
30592 const EMPTY_SET = new Set();
30593
30594 /**
30595 * @license
30596 * Copyright Google LLC All Rights Reserved.
30597 *
30598 * Use of this source code is governed by an MIT-style license that can be
30599 * found in the LICENSE file at https://angular.io/license
30600 */
30601 /**
30602 * Drives an incremental build, by tracking changes and determining which files need to be emitted.
30603 */
30604 class IncrementalDriver {
30605 constructor(state, allTsFiles, depGraph, logicalChanges) {
30606 this.allTsFiles = allTsFiles;
30607 this.depGraph = depGraph;
30608 this.logicalChanges = logicalChanges;
30609 this.state = state;
30610 }
30611 /**
30612 * Construct an `IncrementalDriver` with a starting state that incorporates the results of a
30613 * previous build.
30614 *
30615 * The previous build's `BuildState` is reconciled with the new program's changes, and the results
30616 * are merged into the new build's `PendingBuildState`.
30617 */
30618 static reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles) {
30619 // Initialize the state of the current build based on the previous one.
30620 let state;
30621 if (oldDriver.state.kind === BuildStateKind.Pending) {
30622 // The previous build never made it past the pending state. Reuse it as the starting state for
30623 // this build.
30624 state = oldDriver.state;
30625 }
30626 else {
30627 // The previous build was successfully analyzed. `pendingEmit` is the only state carried
30628 // forward into this build.
30629 state = {
30630 kind: BuildStateKind.Pending,
30631 pendingEmit: oldDriver.state.pendingEmit,
30632 changedResourcePaths: new Set(),
30633 changedTsPaths: new Set(),
30634 lastGood: oldDriver.state.lastGood,
30635 };
30636 }
30637 // Merge the freshly modified resource files with any prior ones.
30638 if (modifiedResourceFiles !== null) {
30639 for (const resFile of modifiedResourceFiles) {
30640 state.changedResourcePaths.add(absoluteFrom(resFile));
30641 }
30642 }
30643 // Next, process the files in the new program, with a couple of goals:
30644 // 1) Determine which TS files have changed, if any, and merge them into `changedTsFiles`.
30645 // 2) Produce a list of TS files which no longer exist in the program (they've been deleted
30646 // since the previous compilation). These need to be removed from the state tracking to avoid
30647 // leaking memory.
30648 // All files in the old program, for easy detection of changes.
30649 const oldFiles = new Set(oldProgram.getSourceFiles());
30650 // Assume all the old files were deleted to begin with. Only TS files are tracked.
30651 const deletedTsPaths = new Set(tsOnlyFiles(oldProgram).map(sf => sf.fileName));
30652 for (const newFile of newProgram.getSourceFiles()) {
30653 if (!newFile.isDeclarationFile) {
30654 // This file exists in the new program, so remove it from `deletedTsPaths`.
30655 deletedTsPaths.delete(newFile.fileName);
30656 }
30657 if (oldFiles.has(newFile)) {
30658 // This file hasn't changed; no need to look at it further.
30659 continue;
30660 }
30661 // The file has changed since the last successful build. The appropriate reaction depends on
30662 // what kind of file it is.
30663 if (!newFile.isDeclarationFile) {
30664 // It's a .ts file, so track it as a change.
30665 state.changedTsPaths.add(newFile.fileName);
30666 }
30667 else {
30668 // It's a .d.ts file. Currently the compiler does not do a great job of tracking
30669 // dependencies on .d.ts files, so bail out of incremental builds here and do a full build.
30670 // This usually only happens if something in node_modules changes.
30671 return IncrementalDriver.fresh(newProgram);
30672 }
30673 }
30674 // The next step is to remove any deleted files from the state.
30675 for (const filePath of deletedTsPaths) {
30676 state.pendingEmit.delete(filePath);
30677 // Even if the file doesn't exist in the current compilation, it still might have been changed
30678 // in a previous one, so delete it from the set of changed TS files, just in case.
30679 state.changedTsPaths.delete(filePath);
30680 }
30681 // Now, changedTsPaths contains physically changed TS paths. Use the previous program's logical
30682 // dependency graph to determine logically changed files.
30683 const depGraph = new FileDependencyGraph();
30684 // If a previous compilation exists, use its dependency graph to determine the set of logically
30685 // changed files.
30686 let logicalChanges = null;
30687 if (state.lastGood !== null) {
30688 // Extract the set of logically changed files. At the same time, this operation populates the
30689 // current (fresh) dependency graph with information about those files which have not
30690 // logically changed.
30691 logicalChanges = depGraph.updateWithPhysicalChanges(state.lastGood.depGraph, state.changedTsPaths, deletedTsPaths, state.changedResourcePaths);
30692 for (const fileName of state.changedTsPaths) {
30693 logicalChanges.add(fileName);
30694 }
30695 // Any logically changed files need to be re-emitted. Most of the time this would happen
30696 // regardless because the new dependency graph would _also_ identify the file as stale.
30697 // However there are edge cases such as removing a component from an NgModule without adding
30698 // it to another one, where the previous graph identifies the file as logically changed, but
30699 // the new graph (which does not have that edge) fails to identify that the file should be
30700 // re-emitted.
30701 for (const change of logicalChanges) {
30702 state.pendingEmit.add(change);
30703 }
30704 }
30705 // `state` now reflects the initial pending state of the current compilation.
30706 return new IncrementalDriver(state, new Set(tsOnlyFiles(newProgram)), depGraph, logicalChanges);
30707 }
30708 static fresh(program) {
30709 // Initialize the set of files which need to be emitted to the set of all TS files in the
30710 // program.
30711 const tsFiles = tsOnlyFiles(program);
30712 const state = {
30713 kind: BuildStateKind.Pending,
30714 pendingEmit: new Set(tsFiles.map(sf => sf.fileName)),
30715 changedResourcePaths: new Set(),
30716 changedTsPaths: new Set(),
30717 lastGood: null,
30718 };
30719 return new IncrementalDriver(state, new Set(tsFiles), new FileDependencyGraph(), /* logicalChanges */ null);
30720 }
30721 recordSuccessfulAnalysis(traitCompiler) {
30722 if (this.state.kind !== BuildStateKind.Pending) {
30723 // Changes have already been incorporated.
30724 return;
30725 }
30726 const pendingEmit = this.state.pendingEmit;
30727 const state = this.state;
30728 for (const sf of this.allTsFiles) {
30729 if (this.depGraph.isStale(sf, state.changedTsPaths, state.changedResourcePaths)) {
30730 // Something has changed which requires this file be re-emitted.
30731 pendingEmit.add(sf.fileName);
30732 }
30733 }
30734 // Update the state to an `AnalyzedBuildState`.
30735 this.state = {
30736 kind: BuildStateKind.Analyzed,
30737 pendingEmit,
30738 // Since this compilation was successfully analyzed, update the "last good" artifacts to the
30739 // ones from the current compilation.
30740 lastGood: {
30741 depGraph: this.depGraph,
30742 traitCompiler: traitCompiler,
30743 typeCheckingResults: null,
30744 },
30745 priorTypeCheckingResults: this.state.lastGood !== null ? this.state.lastGood.typeCheckingResults : null,
30746 };
30747 }
30748 recordSuccessfulTypeCheck(results) {
30749 if (this.state.lastGood === null || this.state.kind !== BuildStateKind.Analyzed) {
30750 return;
30751 }
30752 this.state.lastGood.typeCheckingResults = results;
30753 }
30754 recordSuccessfulEmit(sf) {
30755 this.state.pendingEmit.delete(sf.fileName);
30756 }
30757 safeToSkipEmit(sf) {
30758 return !this.state.pendingEmit.has(sf.fileName);
30759 }
30760 priorWorkFor(sf) {
30761 if (this.state.lastGood === null || this.logicalChanges === null) {
30762 // There is no previous good build, so no prior work exists.
30763 return null;
30764 }
30765 else if (this.logicalChanges.has(sf.fileName)) {
30766 // Prior work might exist, but would be stale as the file in question has logically changed.
30767 return null;
30768 }
30769 else {
30770 // Prior work might exist, and if it does then it's usable!
30771 return this.state.lastGood.traitCompiler.recordsFor(sf);
30772 }
30773 }
30774 priorTypeCheckingResultsFor(sf) {
30775 if (this.state.kind !== BuildStateKind.Analyzed ||
30776 this.state.priorTypeCheckingResults === null || this.logicalChanges === null) {
30777 return null;
30778 }
30779 if (this.logicalChanges.has(sf.fileName)) {
30780 return null;
30781 }
30782 const fileName = absoluteFromSourceFile(sf);
30783 if (!this.state.priorTypeCheckingResults.has(fileName)) {
30784 return null;
30785 }
30786 const data = this.state.priorTypeCheckingResults.get(fileName);
30787 if (data.hasInlines) {
30788 return null;
30789 }
30790 return data;
30791 }
30792 }
30793 var BuildStateKind;
30794 (function (BuildStateKind) {
30795 BuildStateKind[BuildStateKind["Pending"] = 0] = "Pending";
30796 BuildStateKind[BuildStateKind["Analyzed"] = 1] = "Analyzed";
30797 })(BuildStateKind || (BuildStateKind = {}));
30798 function tsOnlyFiles(program) {
30799 return program.getSourceFiles().filter(sf => !sf.isDeclarationFile);
30800 }
30801
30802 /**
30803 * @license
30804 * Copyright Google LLC All Rights Reserved.
30805 *
30806 * Use of this source code is governed by an MIT-style license that can be
30807 * found in the LICENSE file at https://angular.io/license
30808 */
30809 /**
30810 * Tracks an `IncrementalDriver` within the strategy itself.
30811 */
30812 class TrackedIncrementalBuildStrategy {
30813 constructor() {
30814 this.driver = null;
30815 this.isSet = false;
30816 }
30817 getIncrementalDriver() {
30818 return this.driver;
30819 }
30820 setIncrementalDriver(driver) {
30821 this.driver = driver;
30822 this.isSet = true;
30823 }
30824 toNextBuildStrategy() {
30825 const strategy = new TrackedIncrementalBuildStrategy();
30826 // Only reuse a driver that was explicitly set via `setIncrementalDriver`.
30827 strategy.driver = this.isSet ? this.driver : null;
30828 return strategy;
30829 }
30830 }
30831
30832 /**
30833 * @license
30834 * Copyright Google LLC All Rights Reserved.
30835 *
30836 * Use of this source code is governed by an MIT-style license that can be
30837 * found in the LICENSE file at https://angular.io/license
30838 */
30839 /**
30840 * Describes the kind of identifier found in a template.
30841 */
30842 var IdentifierKind;
30843 (function (IdentifierKind) {
30844 IdentifierKind[IdentifierKind["Property"] = 0] = "Property";
30845 IdentifierKind[IdentifierKind["Method"] = 1] = "Method";
30846 IdentifierKind[IdentifierKind["Element"] = 2] = "Element";
30847 IdentifierKind[IdentifierKind["Template"] = 3] = "Template";
30848 IdentifierKind[IdentifierKind["Attribute"] = 4] = "Attribute";
30849 IdentifierKind[IdentifierKind["Reference"] = 5] = "Reference";
30850 IdentifierKind[IdentifierKind["Variable"] = 6] = "Variable";
30851 })(IdentifierKind || (IdentifierKind = {}));
30852 /**
30853 * Describes the absolute byte offsets of a text anchor in a source code.
30854 */
30855 class AbsoluteSourceSpan$1 {
30856 constructor(start, end) {
30857 this.start = start;
30858 this.end = end;
30859 }
30860 }
30861
30862 /**
30863 * @license
30864 * Copyright Google LLC All Rights Reserved.
30865 *
30866 * Use of this source code is governed by an MIT-style license that can be
30867 * found in the LICENSE file at https://angular.io/license
30868 */
30869 /**
30870 * A context for storing indexing infromation about components of a program.
30871 *
30872 * An `IndexingContext` collects component and template analysis information from
30873 * `DecoratorHandler`s and exposes them to be indexed.
30874 */
30875 class IndexingContext {
30876 constructor() {
30877 this.components = new Set();
30878 }
30879 /**
30880 * Adds a component to the context.
30881 */
30882 addComponent(info) {
30883 this.components.add(info);
30884 }
30885 }
30886
30887 /**
30888 * @license
30889 * Copyright Google LLC All Rights Reserved.
30890 *
30891 * Use of this source code is governed by an MIT-style license that can be
30892 * found in the LICENSE file at https://angular.io/license
30893 */
30894 /**
30895 * Visits the AST of an Angular template syntax expression, finding interesting
30896 * entities (variable references, etc.). Creates an array of Entities found in
30897 * the expression, with the location of the Entities being relative to the
30898 * expression.
30899 *
30900 * Visiting `text {{prop}}` will return
30901 * `[TopLevelIdentifier {name: 'prop', span: {start: 7, end: 11}}]`.
30902 */
30903 class ExpressionVisitor extends RecursiveAstVisitor {
30904 constructor(expressionStr, absoluteOffset, boundTemplate, targetToIdentifier) {
30905 super();
30906 this.expressionStr = expressionStr;
30907 this.absoluteOffset = absoluteOffset;
30908 this.boundTemplate = boundTemplate;
30909 this.targetToIdentifier = targetToIdentifier;
30910 this.identifiers = [];
30911 }
30912 /**
30913 * Returns identifiers discovered in an expression.
30914 *
30915 * @param ast expression AST to visit
30916 * @param source expression AST source code
30917 * @param absoluteOffset absolute byte offset from start of the file to the start of the AST
30918 * source code.
30919 * @param boundTemplate bound target of the entire template, which can be used to query for the
30920 * entities expressions target.
30921 * @param targetToIdentifier closure converting a template target node to its identifier.
30922 */
30923 static getIdentifiers(ast, source, absoluteOffset, boundTemplate, targetToIdentifier) {
30924 const visitor = new ExpressionVisitor(source, absoluteOffset, boundTemplate, targetToIdentifier);
30925 visitor.visit(ast);
30926 return visitor.identifiers;
30927 }
30928 visit(ast) {
30929 ast.visit(this);
30930 }
30931 visitMethodCall(ast, context) {
30932 this.visitIdentifier(ast, IdentifierKind.Method);
30933 super.visitMethodCall(ast, context);
30934 }
30935 visitPropertyRead(ast, context) {
30936 this.visitIdentifier(ast, IdentifierKind.Property);
30937 super.visitPropertyRead(ast, context);
30938 }
30939 visitPropertyWrite(ast, context) {
30940 this.visitIdentifier(ast, IdentifierKind.Property);
30941 super.visitPropertyWrite(ast, context);
30942 }
30943 /**
30944 * Visits an identifier, adding it to the identifier store if it is useful for indexing.
30945 *
30946 * @param ast expression AST the identifier is in
30947 * @param kind identifier kind
30948 */
30949 visitIdentifier(ast, kind) {
30950 // The definition of a non-top-level property such as `bar` in `{{foo.bar}}` is currently
30951 // impossible to determine by an indexer and unsupported by the indexing module.
30952 // The indexing module also does not currently support references to identifiers declared in the
30953 // template itself, which have a non-null expression target.
30954 if (!(ast.receiver instanceof ImplicitReceiver)) {
30955 return;
30956 }
30957 // The source span of the requested AST starts at a location that is offset from the expression.
30958 const identifierStart = ast.sourceSpan.start - this.absoluteOffset;
30959 if (!this.expressionStr.substring(identifierStart).startsWith(ast.name)) {
30960 throw new Error(`Impossible state: "${ast.name}" not found in "${this.expressionStr}" at location ${identifierStart}`);
30961 }
30962 // Join the relative position of the expression within a node with the absolute position
30963 // of the node to get the absolute position of the expression in the source code.
30964 const absoluteStart = this.absoluteOffset + identifierStart;
30965 const span = new AbsoluteSourceSpan$1(absoluteStart, absoluteStart + ast.name.length);
30966 const targetAst = this.boundTemplate.getExpressionTarget(ast);
30967 const target = targetAst ? this.targetToIdentifier(targetAst) : null;
30968 const identifier = {
30969 name: ast.name,
30970 span,
30971 kind,
30972 target,
30973 };
30974 this.identifiers.push(identifier);
30975 }
30976 }
30977 /**
30978 * Visits the AST of a parsed Angular template. Discovers and stores
30979 * identifiers of interest, deferring to an `ExpressionVisitor` as needed.
30980 */
30981 class TemplateVisitor extends RecursiveVisitor {
30982 /**
30983 * Creates a template visitor for a bound template target. The bound target can be used when
30984 * deferred to the expression visitor to get information about the target of an expression.
30985 *
30986 * @param boundTemplate bound template target
30987 */
30988 constructor(boundTemplate) {
30989 super();
30990 this.boundTemplate = boundTemplate;
30991 // Identifiers of interest found in the template.
30992 this.identifiers = new Set();
30993 // Map of targets in a template to their identifiers.
30994 this.targetIdentifierCache = new Map();
30995 // Map of elements and templates to their identifiers.
30996 this.elementAndTemplateIdentifierCache = new Map();
30997 }
30998 /**
30999 * Visits a node in the template.
31000 *
31001 * @param node node to visit
31002 */
31003 visit(node) {
31004 node.visit(this);
31005 }
31006 visitAll(nodes) {
31007 nodes.forEach(node => this.visit(node));
31008 }
31009 /**
31010 * Add an identifier for an HTML element and visit its children recursively.
31011 *
31012 * @param element
31013 */
31014 visitElement(element) {
31015 const elementIdentifier = this.elementOrTemplateToIdentifier(element);
31016 this.identifiers.add(elementIdentifier);
31017 this.visitAll(element.references);
31018 this.visitAll(element.inputs);
31019 this.visitAll(element.attributes);
31020 this.visitAll(element.children);
31021 this.visitAll(element.outputs);
31022 }
31023 visitTemplate(template) {
31024 const templateIdentifier = this.elementOrTemplateToIdentifier(template);
31025 this.identifiers.add(templateIdentifier);
31026 this.visitAll(template.variables);
31027 this.visitAll(template.attributes);
31028 this.visitAll(template.templateAttrs);
31029 this.visitAll(template.children);
31030 this.visitAll(template.references);
31031 }
31032 visitBoundAttribute(attribute) {
31033 // If the bound attribute has no value, it cannot have any identifiers in the value expression.
31034 if (attribute.valueSpan === undefined) {
31035 return;
31036 }
31037 const identifiers = ExpressionVisitor.getIdentifiers(attribute.value, attribute.valueSpan.toString(), attribute.valueSpan.start.offset, this.boundTemplate, this.targetToIdentifier.bind(this));
31038 identifiers.forEach(id => this.identifiers.add(id));
31039 }
31040 visitBoundEvent(attribute) {
31041 this.visitExpression(attribute.handler);
31042 }
31043 visitBoundText(text) {
31044 this.visitExpression(text.value);
31045 }
31046 visitReference(reference) {
31047 const referenceIdentifer = this.targetToIdentifier(reference);
31048 this.identifiers.add(referenceIdentifer);
31049 }
31050 visitVariable(variable) {
31051 const variableIdentifier = this.targetToIdentifier(variable);
31052 this.identifiers.add(variableIdentifier);
31053 }
31054 /** Creates an identifier for a template element or template node. */
31055 elementOrTemplateToIdentifier(node) {
31056 // If this node has already been seen, return the cached result.
31057 if (this.elementAndTemplateIdentifierCache.has(node)) {
31058 return this.elementAndTemplateIdentifierCache.get(node);
31059 }
31060 let name;
31061 let kind;
31062 if (node instanceof Template) {
31063 name = node.tagName;
31064 kind = IdentifierKind.Template;
31065 }
31066 else {
31067 name = node.name;
31068 kind = IdentifierKind.Element;
31069 }
31070 const sourceSpan = node.startSourceSpan;
31071 // An element's or template's source span can be of the form `<element>`, `<element />`, or
31072 // `<element></element>`. Only the selector is interesting to the indexer, so the source is
31073 // searched for the first occurrence of the element (selector) name.
31074 const start = this.getStartLocation(name, sourceSpan);
31075 const absoluteSpan = new AbsoluteSourceSpan$1(start, start + name.length);
31076 // Record the nodes's attributes, which an indexer can later traverse to see if any of them
31077 // specify a used directive on the node.
31078 const attributes = node.attributes.map(({ name, sourceSpan }) => {
31079 return {
31080 name,
31081 span: new AbsoluteSourceSpan$1(sourceSpan.start.offset, sourceSpan.end.offset),
31082 kind: IdentifierKind.Attribute,
31083 };
31084 });
31085 const usedDirectives = this.boundTemplate.getDirectivesOfNode(node) || [];
31086 const identifier = {
31087 name,
31088 span: absoluteSpan,
31089 kind,
31090 attributes: new Set(attributes),
31091 usedDirectives: new Set(usedDirectives.map(dir => {
31092 return {
31093 node: dir.ref.node,
31094 selector: dir.selector,
31095 };
31096 })),
31097 };
31098 this.elementAndTemplateIdentifierCache.set(node, identifier);
31099 return identifier;
31100 }
31101 /** Creates an identifier for a template reference or template variable target. */
31102 targetToIdentifier(node) {
31103 // If this node has already been seen, return the cached result.
31104 if (this.targetIdentifierCache.has(node)) {
31105 return this.targetIdentifierCache.get(node);
31106 }
31107 const { name, sourceSpan } = node;
31108 const start = this.getStartLocation(name, sourceSpan);
31109 const span = new AbsoluteSourceSpan$1(start, start + name.length);
31110 let identifier;
31111 if (node instanceof Reference) {
31112 // If the node is a reference, we care about its target. The target can be an element, a
31113 // template, a directive applied on a template or element (in which case the directive field
31114 // is non-null), or nothing at all.
31115 const refTarget = this.boundTemplate.getReferenceTarget(node);
31116 let target = null;
31117 if (refTarget) {
31118 if (refTarget instanceof Element || refTarget instanceof Template) {
31119 target = {
31120 node: this.elementOrTemplateToIdentifier(refTarget),
31121 directive: null,
31122 };
31123 }
31124 else {
31125 target = {
31126 node: this.elementOrTemplateToIdentifier(refTarget.node),
31127 directive: refTarget.directive.ref.node,
31128 };
31129 }
31130 }
31131 identifier = {
31132 name,
31133 span,
31134 kind: IdentifierKind.Reference,
31135 target,
31136 };
31137 }
31138 else {
31139 identifier = {
31140 name,
31141 span,
31142 kind: IdentifierKind.Variable,
31143 };
31144 }
31145 this.targetIdentifierCache.set(node, identifier);
31146 return identifier;
31147 }
31148 /** Gets the start location of a string in a SourceSpan */
31149 getStartLocation(name, context) {
31150 const localStr = context.toString();
31151 if (!localStr.includes(name)) {
31152 throw new Error(`Impossible state: "${name}" not found in "${localStr}"`);
31153 }
31154 return context.start.offset + localStr.indexOf(name);
31155 }
31156 /**
31157 * Visits a node's expression and adds its identifiers, if any, to the visitor's state.
31158 * Only ASTs with information about the expression source and its location are visited.
31159 *
31160 * @param node node whose expression to visit
31161 */
31162 visitExpression(ast) {
31163 // Only include ASTs that have information about their source and absolute source spans.
31164 if (ast instanceof ASTWithSource && ast.source !== null) {
31165 // Make target to identifier mapping closure stateful to this visitor instance.
31166 const targetToIdentifier = this.targetToIdentifier.bind(this);
31167 const absoluteOffset = ast.sourceSpan.start;
31168 const identifiers = ExpressionVisitor.getIdentifiers(ast, ast.source, absoluteOffset, this.boundTemplate, targetToIdentifier);
31169 identifiers.forEach(id => this.identifiers.add(id));
31170 }
31171 }
31172 }
31173 /**
31174 * Traverses a template AST and builds identifiers discovered in it.
31175 *
31176 * @param boundTemplate bound template target, which can be used for querying expression targets.
31177 * @return identifiers in template
31178 */
31179 function getTemplateIdentifiers(boundTemplate) {
31180 const visitor = new TemplateVisitor(boundTemplate);
31181 if (boundTemplate.target.template !== undefined) {
31182 visitor.visitAll(boundTemplate.target.template);
31183 }
31184 return visitor.identifiers;
31185 }
31186
31187 /**
31188 * @license
31189 * Copyright Google LLC All Rights Reserved.
31190 *
31191 * Use of this source code is governed by an MIT-style license that can be
31192 * found in the LICENSE file at https://angular.io/license
31193 */
31194 /**
31195 * Generates `IndexedComponent` entries from a `IndexingContext`, which has information
31196 * about components discovered in the program registered in it.
31197 *
31198 * The context must be populated before `generateAnalysis` is called.
31199 */
31200 function generateAnalysis(context) {
31201 const analysis = new Map();
31202 context.components.forEach(({ declaration, selector, boundTemplate, templateMeta }) => {
31203 const name = declaration.name.getText();
31204 const usedComponents = new Set();
31205 const usedDirs = boundTemplate.getUsedDirectives();
31206 usedDirs.forEach(dir => {
31207 if (dir.isComponent) {
31208 usedComponents.add(dir.ref.node);
31209 }
31210 });
31211 // Get source files for the component and the template. If the template is inline, its source
31212 // file is the component's.
31213 const componentFile = new ParseSourceFile(declaration.getSourceFile().getFullText(), declaration.getSourceFile().fileName);
31214 let templateFile;
31215 if (templateMeta.isInline) {
31216 templateFile = componentFile;
31217 }
31218 else {
31219 templateFile = templateMeta.file;
31220 }
31221 analysis.set(declaration, {
31222 name,
31223 selector,
31224 file: componentFile,
31225 template: {
31226 identifiers: getTemplateIdentifiers(boundTemplate),
31227 usedComponents,
31228 isInline: templateMeta.isInline,
31229 file: templateFile,
31230 },
31231 });
31232 });
31233 return analysis;
31234 }
31235
31236 /**
31237 * @license
31238 * Copyright Google LLC All Rights Reserved.
31239 *
31240 * Use of this source code is governed by an MIT-style license that can be
31241 * found in the LICENSE file at https://angular.io/license
31242 */
31243 class ModuleWithProvidersScanner {
31244 constructor(host, evaluator, emitter) {
31245 this.host = host;
31246 this.evaluator = evaluator;
31247 this.emitter = emitter;
31248 }
31249 scan(sf, dts) {
31250 for (const stmt of sf.statements) {
31251 this.visitStatement(dts, stmt);
31252 }
31253 }
31254 visitStatement(dts, stmt) {
31255 // Detect whether a statement is exported, which is used as one of the hints whether to look
31256 // more closely at possible MWP functions within. This is a syntactic check, not a semantic
31257 // check, so it won't detect cases like:
31258 //
31259 // var X = ...;
31260 // export {X}
31261 //
31262 // This is intentional, because the alternative is slow and this will catch 99% of the cases we
31263 // need to handle.
31264 const isExported = stmt.modifiers !== undefined &&
31265 stmt.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.ExportKeyword);
31266 if (!isExported) {
31267 return;
31268 }
31269 if (ts$1.isClassDeclaration(stmt)) {
31270 for (const member of stmt.members) {
31271 if (!ts$1.isMethodDeclaration(member) || !isStatic(member)) {
31272 continue;
31273 }
31274 this.visitFunctionOrMethodDeclaration(dts, member);
31275 }
31276 }
31277 else if (ts$1.isFunctionDeclaration(stmt)) {
31278 this.visitFunctionOrMethodDeclaration(dts, stmt);
31279 }
31280 }
31281 visitFunctionOrMethodDeclaration(dts, decl) {
31282 // First, some sanity. This should have a method body with a single return statement.
31283 if (decl.body === undefined || decl.body.statements.length !== 1) {
31284 return;
31285 }
31286 const retStmt = decl.body.statements[0];
31287 if (!ts$1.isReturnStatement(retStmt) || retStmt.expression === undefined) {
31288 return;
31289 }
31290 const retValue = retStmt.expression;
31291 // Now, look at the return type of the method. Maybe bail if the type is already marked, or if
31292 // it's incompatible with a MWP function.
31293 const returnType = this.returnTypeOf(decl);
31294 if (returnType === ReturnType.OTHER || returnType === ReturnType.MWP_WITH_TYPE) {
31295 // Don't process this declaration, it either already declares the right return type, or an
31296 // incompatible one.
31297 return;
31298 }
31299 const value = this.evaluator.evaluate(retValue);
31300 if (!(value instanceof Map) || !value.has('ngModule')) {
31301 // The return value does not provide sufficient information to be able to add a generic type.
31302 return;
31303 }
31304 if (returnType === ReturnType.INFERRED && !isModuleWithProvidersType(value)) {
31305 // The return type is inferred but the returned object is not of the correct shape, so we
31306 // shouldn's modify the return type to become `ModuleWithProviders`.
31307 return;
31308 }
31309 // The return type has been verified to represent the `ModuleWithProviders` type, but either the
31310 // return type is inferred or the generic type argument is missing. In both cases, a new return
31311 // type is created where the `ngModule` type is included as generic type argument.
31312 const ngModule = value.get('ngModule');
31313 if (!(ngModule instanceof Reference$1) || !ts$1.isClassDeclaration(ngModule.node)) {
31314 return;
31315 }
31316 const ngModuleExpr = this.emitter.emit(ngModule, decl.getSourceFile(), ImportFlags.ForceNewImport);
31317 const ngModuleType = new ExpressionType(ngModuleExpr);
31318 const mwpNgType = new ExpressionType(new ExternalExpr(Identifiers$1.ModuleWithProviders), [ /* modifiers */], [ngModuleType]);
31319 dts.addTypeReplacement(decl, mwpNgType);
31320 }
31321 returnTypeOf(decl) {
31322 if (decl.type === undefined) {
31323 return ReturnType.INFERRED;
31324 }
31325 else if (!ts$1.isTypeReferenceNode(decl.type)) {
31326 return ReturnType.OTHER;
31327 }
31328 // Try to figure out if the type is of a familiar form, something that looks like it was
31329 // imported.
31330 let typeId;
31331 if (ts$1.isIdentifier(decl.type.typeName)) {
31332 // def: ModuleWithProviders
31333 typeId = decl.type.typeName;
31334 }
31335 else if (ts$1.isQualifiedName(decl.type.typeName) && ts$1.isIdentifier(decl.type.typeName.left)) {
31336 // def: i0.ModuleWithProviders
31337 typeId = decl.type.typeName.right;
31338 }
31339 else {
31340 return ReturnType.OTHER;
31341 }
31342 const importDecl = this.host.getImportOfIdentifier(typeId);
31343 if (importDecl === null || importDecl.from !== '@angular/core' ||
31344 importDecl.name !== 'ModuleWithProviders') {
31345 return ReturnType.OTHER;
31346 }
31347 if (decl.type.typeArguments === undefined || decl.type.typeArguments.length === 0) {
31348 // The return type is indeed ModuleWithProviders, but no generic type parameter was found.
31349 return ReturnType.MWP_NO_TYPE;
31350 }
31351 else {
31352 // The return type is ModuleWithProviders, and the user has already specified a generic type.
31353 return ReturnType.MWP_WITH_TYPE;
31354 }
31355 }
31356 }
31357 var ReturnType;
31358 (function (ReturnType) {
31359 ReturnType[ReturnType["INFERRED"] = 0] = "INFERRED";
31360 ReturnType[ReturnType["MWP_NO_TYPE"] = 1] = "MWP_NO_TYPE";
31361 ReturnType[ReturnType["MWP_WITH_TYPE"] = 2] = "MWP_WITH_TYPE";
31362 ReturnType[ReturnType["OTHER"] = 3] = "OTHER";
31363 })(ReturnType || (ReturnType = {}));
31364 /** Whether the resolved value map represents a ModuleWithProviders object */
31365 function isModuleWithProvidersType(value) {
31366 const ngModule = value.has('ngModule');
31367 const providers = value.has('providers');
31368 return ngModule && (value.size === 1 || (providers && value.size === 2));
31369 }
31370 function isStatic(node) {
31371 return node.modifiers !== undefined &&
31372 node.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.StaticKeyword);
31373 }
31374
31375 const NOOP_PERF_RECORDER = {
31376 enabled: false,
31377 mark: (name, node, category, detail) => { },
31378 start: (name, node, category, detail) => {
31379 return 0;
31380 },
31381 stop: (span) => { },
31382 };
31383
31384 /**
31385 * @license
31386 * Copyright Google LLC All Rights Reserved.
31387 *
31388 * Use of this source code is governed by an MIT-style license that can be
31389 * found in the LICENSE file at https://angular.io/license
31390 */
31391 var PerfLogEventType;
31392 (function (PerfLogEventType) {
31393 PerfLogEventType[PerfLogEventType["SPAN_OPEN"] = 0] = "SPAN_OPEN";
31394 PerfLogEventType[PerfLogEventType["SPAN_CLOSE"] = 1] = "SPAN_CLOSE";
31395 PerfLogEventType[PerfLogEventType["MARK"] = 2] = "MARK";
31396 })(PerfLogEventType || (PerfLogEventType = {}));
31397
31398 /**
31399 * @license
31400 * Copyright Google LLC All Rights Reserved.
31401 *
31402 * Use of this source code is governed by an MIT-style license that can be
31403 * found in the LICENSE file at https://angular.io/license
31404 */
31405 const CSS_PREPROCESSOR_EXT = /(\.scss|\.sass|\.less|\.styl)$/;
31406 const RESOURCE_MARKER = '.$ngresource$';
31407 const RESOURCE_MARKER_TS = RESOURCE_MARKER + '.ts';
31408 /**
31409 * `ResourceLoader` which delegates to an `NgCompilerAdapter`'s resource loading methods.
31410 */
31411 class AdapterResourceLoader {
31412 constructor(adapter, options) {
31413 this.adapter = adapter;
31414 this.options = options;
31415 this.cache = new Map();
31416 this.fetching = new Map();
31417 this.lookupResolutionHost = createLookupResolutionHost(this.adapter);
31418 this.canPreload = !!this.adapter.readResource;
31419 }
31420 /**
31421 * Resolve the url of a resource relative to the file that contains the reference to it.
31422 * The return value of this method can be used in the `load()` and `preload()` methods.
31423 *
31424 * Uses the provided CompilerHost if it supports mapping resources to filenames.
31425 * Otherwise, uses a fallback mechanism that searches the module resolution candidates.
31426 *
31427 * @param url The, possibly relative, url of the resource.
31428 * @param fromFile The path to the file that contains the URL of the resource.
31429 * @returns A resolved url of resource.
31430 * @throws An error if the resource cannot be resolved.
31431 */
31432 resolve(url, fromFile) {
31433 let resolvedUrl = null;
31434 if (this.adapter.resourceNameToFileName) {
31435 resolvedUrl = this.adapter.resourceNameToFileName(url, fromFile);
31436 }
31437 else {
31438 resolvedUrl = this.fallbackResolve(url, fromFile);
31439 }
31440 if (resolvedUrl === null) {
31441 throw new Error(`HostResourceResolver: could not resolve ${url} in context of ${fromFile})`);
31442 }
31443 return resolvedUrl;
31444 }
31445 /**
31446 * Preload the specified resource, asynchronously.
31447 *
31448 * Once the resource is loaded, its value is cached so it can be accessed synchronously via the
31449 * `load()` method.
31450 *
31451 * @param resolvedUrl The url (resolved by a call to `resolve()`) of the resource to preload.
31452 * @returns A Promise that is resolved once the resource has been loaded or `undefined` if the
31453 * file has already been loaded.
31454 * @throws An Error if pre-loading is not available.
31455 */
31456 preload(resolvedUrl) {
31457 if (!this.adapter.readResource) {
31458 throw new Error('HostResourceLoader: the CompilerHost provided does not support pre-loading resources.');
31459 }
31460 if (this.cache.has(resolvedUrl)) {
31461 return undefined;
31462 }
31463 else if (this.fetching.has(resolvedUrl)) {
31464 return this.fetching.get(resolvedUrl);
31465 }
31466 const result = this.adapter.readResource(resolvedUrl);
31467 if (typeof result === 'string') {
31468 this.cache.set(resolvedUrl, result);
31469 return undefined;
31470 }
31471 else {
31472 const fetchCompletion = result.then(str => {
31473 this.fetching.delete(resolvedUrl);
31474 this.cache.set(resolvedUrl, str);
31475 });
31476 this.fetching.set(resolvedUrl, fetchCompletion);
31477 return fetchCompletion;
31478 }
31479 }
31480 /**
31481 * Load the resource at the given url, synchronously.
31482 *
31483 * The contents of the resource may have been cached by a previous call to `preload()`.
31484 *
31485 * @param resolvedUrl The url (resolved by a call to `resolve()`) of the resource to load.
31486 * @returns The contents of the resource.
31487 */
31488 load(resolvedUrl) {
31489 if (this.cache.has(resolvedUrl)) {
31490 return this.cache.get(resolvedUrl);
31491 }
31492 const result = this.adapter.readResource ? this.adapter.readResource(resolvedUrl) :
31493 this.adapter.readFile(resolvedUrl);
31494 if (typeof result !== 'string') {
31495 throw new Error(`HostResourceLoader: loader(${resolvedUrl}) returned a Promise`);
31496 }
31497 this.cache.set(resolvedUrl, result);
31498 return result;
31499 }
31500 /**
31501 * Invalidate the entire resource cache.
31502 */
31503 invalidate() {
31504 this.cache.clear();
31505 }
31506 /**
31507 * Attempt to resolve `url` in the context of `fromFile`, while respecting the rootDirs
31508 * option from the tsconfig. First, normalize the file name.
31509 */
31510 fallbackResolve(url, fromFile) {
31511 let candidateLocations;
31512 if (url.startsWith('/')) {
31513 // This path is not really an absolute path, but instead the leading '/' means that it's
31514 // rooted in the project rootDirs. So look for it according to the rootDirs.
31515 candidateLocations = this.getRootedCandidateLocations(url);
31516 }
31517 else {
31518 // This path is a "relative" path and can be resolved as such. To make this easier on the
31519 // downstream resolver, the './' prefix is added if missing to distinguish these paths from
31520 // absolute node_modules paths.
31521 if (!url.startsWith('.')) {
31522 url = `./${url}`;
31523 }
31524 candidateLocations = this.getResolvedCandidateLocations(url, fromFile);
31525 }
31526 for (const candidate of candidateLocations) {
31527 if (this.adapter.fileExists(candidate)) {
31528 return candidate;
31529 }
31530 else if (CSS_PREPROCESSOR_EXT.test(candidate)) {
31531 /**
31532 * If the user specified styleUrl points to *.scss, but the Sass compiler was run before
31533 * Angular, then the resource may have been generated as *.css. Simply try the resolution
31534 * again.
31535 */
31536 const cssFallbackUrl = candidate.replace(CSS_PREPROCESSOR_EXT, '.css');
31537 if (this.adapter.fileExists(cssFallbackUrl)) {
31538 return cssFallbackUrl;
31539 }
31540 }
31541 }
31542 return null;
31543 }
31544 getRootedCandidateLocations(url) {
31545 // The path already starts with '/', so add a '.' to make it relative.
31546 const segment = ('.' + url);
31547 return this.adapter.rootDirs.map(rootDir => join(rootDir, segment));
31548 }
31549 /**
31550 * TypeScript provides utilities to resolve module names, but not resource files (which aren't
31551 * a part of the ts.Program). However, TypeScript's module resolution can be used creatively
31552 * to locate where resource files should be expected to exist. Since module resolution returns
31553 * a list of file names that were considered, the loader can enumerate the possible locations
31554 * for the file by setting up a module resolution for it that will fail.
31555 */
31556 getResolvedCandidateLocations(url, fromFile) {
31557 // clang-format off
31558 const failedLookup = ts$1.resolveModuleName(url + RESOURCE_MARKER, fromFile, this.options, this.lookupResolutionHost);
31559 // clang-format on
31560 if (failedLookup.failedLookupLocations === undefined) {
31561 throw new Error(`Internal error: expected to find failedLookupLocations during resolution of resource '${url}' in context of ${fromFile}`);
31562 }
31563 return failedLookup.failedLookupLocations
31564 .filter(candidate => candidate.endsWith(RESOURCE_MARKER_TS))
31565 .map(candidate => candidate.slice(0, -RESOURCE_MARKER_TS.length));
31566 }
31567 }
31568 /**
31569 * Derives a `ts.ModuleResolutionHost` from a compiler adapter that recognizes the special resource
31570 * marker and does not go to the filesystem for these requests, as they are known not to exist.
31571 */
31572 function createLookupResolutionHost(adapter) {
31573 var _a, _b, _c;
31574 return {
31575 directoryExists(directoryName) {
31576 if (directoryName.includes(RESOURCE_MARKER)) {
31577 return false;
31578 }
31579 else if (adapter.directoryExists !== undefined) {
31580 return adapter.directoryExists(directoryName);
31581 }
31582 else {
31583 // TypeScript's module resolution logic assumes that the directory exists when no host
31584 // implementation is available.
31585 return true;
31586 }
31587 },
31588 fileExists(fileName) {
31589 if (fileName.includes(RESOURCE_MARKER)) {
31590 return false;
31591 }
31592 else {
31593 return adapter.fileExists(fileName);
31594 }
31595 },
31596 readFile: adapter.readFile.bind(adapter),
31597 getCurrentDirectory: adapter.getCurrentDirectory.bind(adapter),
31598 getDirectories: (_a = adapter.getDirectories) === null || _a === void 0 ? void 0 : _a.bind(adapter),
31599 realpath: (_b = adapter.realpath) === null || _b === void 0 ? void 0 : _b.bind(adapter),
31600 trace: (_c = adapter.trace) === null || _c === void 0 ? void 0 : _c.bind(adapter),
31601 };
31602 }
31603
31604 /**
31605 * @license
31606 * Copyright Google LLC All Rights Reserved.
31607 *
31608 * Use of this source code is governed by an MIT-style license that can be
31609 * found in the LICENSE file at https://angular.io/license
31610 */
31611 class RouterEntryPointImpl {
31612 constructor(filePath, moduleName) {
31613 this.filePath = filePath;
31614 this.moduleName = moduleName;
31615 }
31616 get name() {
31617 return this.moduleName;
31618 }
31619 // For debugging purposes.
31620 toString() {
31621 return `RouterEntryPoint(name: ${this.name}, filePath: ${this.filePath})`;
31622 }
31623 }
31624 class RouterEntryPointManager {
31625 constructor(moduleResolver) {
31626 this.moduleResolver = moduleResolver;
31627 this.map = new Map();
31628 }
31629 resolveLoadChildrenIdentifier(loadChildrenIdentifier, context) {
31630 const [relativeFile, moduleName] = loadChildrenIdentifier.split('#');
31631 if (moduleName === undefined) {
31632 return null;
31633 }
31634 const resolvedSf = this.moduleResolver.resolveModule(relativeFile, context.fileName);
31635 if (resolvedSf === null) {
31636 return null;
31637 }
31638 return this.fromNgModule(resolvedSf, moduleName);
31639 }
31640 fromNgModule(sf, moduleName) {
31641 const key = entryPointKeyFor(sf.fileName, moduleName);
31642 if (!this.map.has(key)) {
31643 this.map.set(key, new RouterEntryPointImpl(sf.fileName, moduleName));
31644 }
31645 return this.map.get(key);
31646 }
31647 }
31648 function entryPointKeyFor(filePath, moduleName) {
31649 // Drop the extension to be compatible with how cli calls `listLazyRoutes(entryRoute)`.
31650 return `${filePath.replace(/\.tsx?$/i, '')}#${moduleName}`;
31651 }
31652
31653 /**
31654 * @license
31655 * Copyright Google LLC All Rights Reserved.
31656 *
31657 * Use of this source code is governed by an MIT-style license that can be
31658 * found in the LICENSE file at https://angular.io/license
31659 */
31660 const ROUTES_MARKER = '__ngRoutesMarker__';
31661 function scanForCandidateTransitiveModules(expr, evaluator) {
31662 if (expr === null) {
31663 return [];
31664 }
31665 const candidateModuleKeys = [];
31666 const entries = evaluator.evaluate(expr);
31667 function recursivelyAddModules(entry) {
31668 if (Array.isArray(entry)) {
31669 for (const e of entry) {
31670 recursivelyAddModules(e);
31671 }
31672 }
31673 else if (entry instanceof Map) {
31674 if (entry.has('ngModule')) {
31675 recursivelyAddModules(entry.get('ngModule'));
31676 }
31677 }
31678 else if ((entry instanceof Reference$1) && hasIdentifier(entry.node)) {
31679 const filePath = entry.node.getSourceFile().fileName;
31680 const moduleName = entry.node.name.text;
31681 candidateModuleKeys.push(entryPointKeyFor(filePath, moduleName));
31682 }
31683 }
31684 recursivelyAddModules(entries);
31685 return candidateModuleKeys;
31686 }
31687 function scanForRouteEntryPoints(ngModule, moduleName, data, entryPointManager, evaluator) {
31688 const loadChildrenIdentifiers = [];
31689 const from = entryPointManager.fromNgModule(ngModule, moduleName);
31690 if (data.providers !== null) {
31691 loadChildrenIdentifiers.push(...scanForProviders(data.providers, evaluator));
31692 }
31693 if (data.imports !== null) {
31694 loadChildrenIdentifiers.push(...scanForRouterModuleUsage(data.imports, evaluator));
31695 }
31696 if (data.exports !== null) {
31697 loadChildrenIdentifiers.push(...scanForRouterModuleUsage(data.exports, evaluator));
31698 }
31699 const routes = [];
31700 for (const loadChildren of loadChildrenIdentifiers) {
31701 const resolvedTo = entryPointManager.resolveLoadChildrenIdentifier(loadChildren, ngModule);
31702 if (resolvedTo !== null) {
31703 routes.push({
31704 loadChildren,
31705 from,
31706 resolvedTo,
31707 });
31708 }
31709 }
31710 return routes;
31711 }
31712 function scanForProviders(expr, evaluator) {
31713 const loadChildrenIdentifiers = [];
31714 const providers = evaluator.evaluate(expr);
31715 function recursivelyAddProviders(provider) {
31716 if (Array.isArray(provider)) {
31717 for (const entry of provider) {
31718 recursivelyAddProviders(entry);
31719 }
31720 }
31721 else if (provider instanceof Map) {
31722 if (provider.has('provide') && provider.has('useValue')) {
31723 const provide = provider.get('provide');
31724 const useValue = provider.get('useValue');
31725 if (isRouteToken(provide) && Array.isArray(useValue)) {
31726 loadChildrenIdentifiers.push(...scanForLazyRoutes(useValue));
31727 }
31728 }
31729 }
31730 }
31731 recursivelyAddProviders(providers);
31732 return loadChildrenIdentifiers;
31733 }
31734 function scanForRouterModuleUsage(expr, evaluator) {
31735 const loadChildrenIdentifiers = [];
31736 const imports = evaluator.evaluate(expr, routerModuleFFR);
31737 function recursivelyAddRoutes(imp) {
31738 if (Array.isArray(imp)) {
31739 for (const entry of imp) {
31740 recursivelyAddRoutes(entry);
31741 }
31742 }
31743 else if (imp instanceof Map) {
31744 if (imp.has(ROUTES_MARKER) && imp.has('routes')) {
31745 const routes = imp.get('routes');
31746 if (Array.isArray(routes)) {
31747 loadChildrenIdentifiers.push(...scanForLazyRoutes(routes));
31748 }
31749 }
31750 }
31751 }
31752 recursivelyAddRoutes(imports);
31753 return loadChildrenIdentifiers;
31754 }
31755 function scanForLazyRoutes(routes) {
31756 const loadChildrenIdentifiers = [];
31757 function recursivelyScanRoutes(routes) {
31758 for (let route of routes) {
31759 if (!(route instanceof Map)) {
31760 continue;
31761 }
31762 if (route.has('loadChildren')) {
31763 const loadChildren = route.get('loadChildren');
31764 if (typeof loadChildren === 'string') {
31765 loadChildrenIdentifiers.push(loadChildren);
31766 }
31767 }
31768 else if (route.has('children')) {
31769 const children = route.get('children');
31770 if (Array.isArray(children)) {
31771 recursivelyScanRoutes(children);
31772 }
31773 }
31774 }
31775 }
31776 recursivelyScanRoutes(routes);
31777 return loadChildrenIdentifiers;
31778 }
31779 /**
31780 * A foreign function resolver that converts `RouterModule.forRoot/forChild(X)` to a special object
31781 * of the form `{__ngRoutesMarker__: true, routes: X}`.
31782 *
31783 * These objects are then recognizable inside the larger set of imports/exports.
31784 */
31785 const routerModuleFFR = function routerModuleFFR(ref, args) {
31786 if (!isMethodNodeReference(ref) || !ts$1.isClassDeclaration(ref.node.parent)) {
31787 return null;
31788 }
31789 else if (ref.bestGuessOwningModule === null ||
31790 ref.bestGuessOwningModule.specifier !== '@angular/router') {
31791 return null;
31792 }
31793 else if (ref.node.parent.name === undefined || ref.node.parent.name.text !== 'RouterModule') {
31794 return null;
31795 }
31796 else if (!ts$1.isIdentifier(ref.node.name) ||
31797 (ref.node.name.text !== 'forRoot' && ref.node.name.text !== 'forChild')) {
31798 return null;
31799 }
31800 const routes = args[0];
31801 return ts$1.createObjectLiteral([
31802 ts$1.createPropertyAssignment(ROUTES_MARKER, ts$1.createTrue()),
31803 ts$1.createPropertyAssignment('routes', routes),
31804 ]);
31805 };
31806 function hasIdentifier(node) {
31807 const node_ = node;
31808 return (node_.name !== undefined) && ts$1.isIdentifier(node_.name);
31809 }
31810 function isMethodNodeReference(ref) {
31811 return ts$1.isMethodDeclaration(ref.node);
31812 }
31813 function isRouteToken(ref) {
31814 return ref instanceof Reference$1 && ref.bestGuessOwningModule !== null &&
31815 ref.bestGuessOwningModule.specifier === '@angular/router' && ref.debugName === 'ROUTES';
31816 }
31817
31818 /**
31819 * @license
31820 * Copyright Google LLC All Rights Reserved.
31821 *
31822 * Use of this source code is governed by an MIT-style license that can be
31823 * found in the LICENSE file at https://angular.io/license
31824 */
31825 class NgModuleRouteAnalyzer {
31826 constructor(moduleResolver, evaluator) {
31827 this.evaluator = evaluator;
31828 this.modules = new Map();
31829 this.entryPointManager = new RouterEntryPointManager(moduleResolver);
31830 }
31831 add(sourceFile, moduleName, imports, exports, providers) {
31832 const key = entryPointKeyFor(sourceFile.fileName, moduleName);
31833 if (this.modules.has(key)) {
31834 throw new Error(`Double route analyzing for '${key}'.`);
31835 }
31836 this.modules.set(key, {
31837 sourceFile,
31838 moduleName,
31839 imports,
31840 exports,
31841 providers,
31842 });
31843 }
31844 listLazyRoutes(entryModuleKey) {
31845 if ((entryModuleKey !== undefined) && !this.modules.has(entryModuleKey)) {
31846 throw new Error(`Failed to list lazy routes: Unknown module '${entryModuleKey}'.`);
31847 }
31848 const routes = [];
31849 const scannedModuleKeys = new Set();
31850 const pendingModuleKeys = entryModuleKey ? [entryModuleKey] : Array.from(this.modules.keys());
31851 // When listing lazy routes for a specific entry module, we need to recursively extract
31852 // "transitive" routes from imported/exported modules. This is not necessary when listing all
31853 // lazy routes, because all analyzed modules will be scanned anyway.
31854 const scanRecursively = entryModuleKey !== undefined;
31855 while (pendingModuleKeys.length > 0) {
31856 const key = pendingModuleKeys.pop();
31857 if (scannedModuleKeys.has(key)) {
31858 continue;
31859 }
31860 else {
31861 scannedModuleKeys.add(key);
31862 }
31863 const data = this.modules.get(key);
31864 const entryPoints = scanForRouteEntryPoints(data.sourceFile, data.moduleName, data, this.entryPointManager, this.evaluator);
31865 routes.push(...entryPoints.map(entryPoint => ({
31866 route: entryPoint.loadChildren,
31867 module: entryPoint.from,
31868 referencedModule: entryPoint.resolvedTo,
31869 })));
31870 if (scanRecursively) {
31871 pendingModuleKeys.push(...[
31872 // Scan the retrieved lazy route entry points.
31873 ...entryPoints.map(({ resolvedTo }) => entryPointKeyFor(resolvedTo.filePath, resolvedTo.moduleName)),
31874 // Scan the current module's imported modules.
31875 ...scanForCandidateTransitiveModules(data.imports, this.evaluator),
31876 // Scan the current module's exported modules.
31877 ...scanForCandidateTransitiveModules(data.exports, this.evaluator),
31878 ].filter(key => this.modules.has(key)));
31879 }
31880 }
31881 return routes;
31882 }
31883 }
31884
31885 /**
31886 * @license
31887 * Copyright Google LLC All Rights Reserved.
31888 *
31889 * Use of this source code is governed by an MIT-style license that can be
31890 * found in the LICENSE file at https://angular.io/license
31891 */
31892 /**
31893 * Reads Angular metadata from classes declared in .d.ts files and computes an `ExportScope`.
31894 *
31895 * Given an NgModule declared in a .d.ts file, this resolver can produce a transitive `ExportScope`
31896 * of all of the directives/pipes it exports. It does this by reading metadata off of Ivy static
31897 * fields on directives, components, pipes, and NgModules.
31898 */
31899 class MetadataDtsModuleScopeResolver {
31900 /**
31901 * @param dtsMetaReader a `MetadataReader` which can read metadata from `.d.ts` files.
31902 */
31903 constructor(dtsMetaReader, aliasingHost) {
31904 this.dtsMetaReader = dtsMetaReader;
31905 this.aliasingHost = aliasingHost;
31906 /**
31907 * Cache which holds fully resolved scopes for NgModule classes from .d.ts files.
31908 */
31909 this.cache = new Map();
31910 }
31911 /**
31912 * Resolve a `Reference`'d NgModule from a .d.ts file and produce a transitive `ExportScope`
31913 * listing the directives and pipes which that NgModule exports to others.
31914 *
31915 * This operation relies on a `Reference` instead of a direct TypeScrpt node as the `Reference`s
31916 * produced depend on how the original NgModule was imported.
31917 */
31918 resolve(ref) {
31919 const clazz = ref.node;
31920 const sourceFile = clazz.getSourceFile();
31921 if (!sourceFile.isDeclarationFile) {
31922 throw new Error(`Debug error: DtsModuleScopeResolver.read(${ref.debugName} from ${sourceFile.fileName}), but not a .d.ts file`);
31923 }
31924 if (this.cache.has(clazz)) {
31925 return this.cache.get(clazz);
31926 }
31927 // Build up the export scope - those directives and pipes made visible by this module.
31928 const directives = [];
31929 const pipes = [];
31930 const ngModules = new Set([clazz]);
31931 const meta = this.dtsMetaReader.getNgModuleMetadata(ref);
31932 if (meta === null) {
31933 this.cache.set(clazz, null);
31934 return null;
31935 }
31936 const declarations = new Set();
31937 for (const declRef of meta.declarations) {
31938 declarations.add(declRef.node);
31939 }
31940 // Only the 'exports' field of the NgModule's metadata is important. Imports and declarations
31941 // don't affect the export scope.
31942 for (const exportRef of meta.exports) {
31943 // Attempt to process the export as a directive.
31944 const directive = this.dtsMetaReader.getDirectiveMetadata(exportRef);
31945 if (directive !== null) {
31946 const isReExport = !declarations.has(exportRef.node);
31947 directives.push(this.maybeAlias(directive, sourceFile, isReExport));
31948 continue;
31949 }
31950 // Attempt to process the export as a pipe.
31951 const pipe = this.dtsMetaReader.getPipeMetadata(exportRef);
31952 if (pipe !== null) {
31953 const isReExport = !declarations.has(exportRef.node);
31954 pipes.push(this.maybeAlias(pipe, sourceFile, isReExport));
31955 continue;
31956 }
31957 // Attempt to process the export as a module.
31958 const exportScope = this.resolve(exportRef);
31959 if (exportScope !== null) {
31960 // It is a module. Add exported directives and pipes to the current scope. This might
31961 // involve rewriting the `Reference`s to those types to have an alias expression if one is
31962 // required.
31963 if (this.aliasingHost === null) {
31964 // Fast path when aliases aren't required.
31965 directives.push(...exportScope.exported.directives);
31966 pipes.push(...exportScope.exported.pipes);
31967 }
31968 else {
31969 // It's necessary to rewrite the `Reference`s to add alias expressions. This way, imports
31970 // generated to these directives and pipes will use a shallow import to `sourceFile`
31971 // instead of a deep import directly to the directive or pipe class.
31972 //
31973 // One important check here is whether the directive/pipe is declared in the same
31974 // source file as the re-exporting NgModule. This can happen if both a directive, its
31975 // NgModule, and the re-exporting NgModule are all in the same file. In this case,
31976 // no import alias is needed as it would go to the same file anyway.
31977 for (const directive of exportScope.exported.directives) {
31978 directives.push(this.maybeAlias(directive, sourceFile, /* isReExport */ true));
31979 }
31980 for (const pipe of exportScope.exported.pipes) {
31981 pipes.push(this.maybeAlias(pipe, sourceFile, /* isReExport */ true));
31982 }
31983 for (const ngModule of exportScope.exported.ngModules) {
31984 ngModules.add(ngModule);
31985 }
31986 }
31987 }
31988 continue;
31989 }
31990 const exportScope = {
31991 exported: {
31992 directives,
31993 pipes,
31994 ngModules: Array.from(ngModules),
31995 isPoisoned: false,
31996 },
31997 };
31998 this.cache.set(clazz, exportScope);
31999 return exportScope;
32000 }
32001 maybeAlias(dirOrPipe, maybeAliasFrom, isReExport) {
32002 const ref = dirOrPipe.ref;
32003 if (this.aliasingHost === null || ref.node.getSourceFile() === maybeAliasFrom) {
32004 return dirOrPipe;
32005 }
32006 const alias = this.aliasingHost.getAliasIn(ref.node, maybeAliasFrom, isReExport);
32007 if (alias === null) {
32008 return dirOrPipe;
32009 }
32010 return Object.assign(Object.assign({}, dirOrPipe), { ref: ref.cloneWithAlias(alias) });
32011 }
32012 }
32013
32014 /**
32015 * @license
32016 * Copyright Google LLC All Rights Reserved.
32017 *
32018 * Use of this source code is governed by an MIT-style license that can be
32019 * found in the LICENSE file at https://angular.io/license
32020 */
32021 /**
32022 * A registry which collects information about NgModules, Directives, Components, and Pipes which
32023 * are local (declared in the ts.Program being compiled), and can produce `LocalModuleScope`s
32024 * which summarize the compilation scope of a component.
32025 *
32026 * This class implements the logic of NgModule declarations, imports, and exports and can produce,
32027 * for a given component, the set of directives and pipes which are "visible" in that component's
32028 * template.
32029 *
32030 * The `LocalModuleScopeRegistry` has two "modes" of operation. During analysis, data for each
32031 * individual NgModule, Directive, Component, and Pipe is added to the registry. No attempt is made
32032 * to traverse or validate the NgModule graph (imports, exports, etc). After analysis, one of
32033 * `getScopeOfModule` or `getScopeForComponent` can be called, which traverses the NgModule graph
32034 * and applies the NgModule logic to generate a `LocalModuleScope`, the full scope for the given
32035 * module or component.
32036 *
32037 * The `LocalModuleScopeRegistry` is also capable of producing `ts.Diagnostic` errors when Angular
32038 * semantics are violated.
32039 */
32040 class LocalModuleScopeRegistry {
32041 constructor(localReader, dependencyScopeReader, refEmitter, aliasingHost) {
32042 this.localReader = localReader;
32043 this.dependencyScopeReader = dependencyScopeReader;
32044 this.refEmitter = refEmitter;
32045 this.aliasingHost = aliasingHost;
32046 /**
32047 * Tracks whether the registry has been asked to produce scopes for a module or component. Once
32048 * this is true, the registry cannot accept registrations of new directives/pipes/modules as it
32049 * would invalidate the cached scope data.
32050 */
32051 this.sealed = false;
32052 /**
32053 * A map of components from the current compilation unit to the NgModule which declared them.
32054 *
32055 * As components and directives are not distinguished at the NgModule level, this map may also
32056 * contain directives. This doesn't cause any problems but isn't useful as there is no concept of
32057 * a directive's compilation scope.
32058 */
32059 this.declarationToModule = new Map();
32060 /**
32061 * This maps from the directive/pipe class to a map of data for each NgModule that declares the
32062 * directive/pipe. This data is needed to produce an error for the given class.
32063 */
32064 this.duplicateDeclarations = new Map();
32065 this.moduleToRef = new Map();
32066 /**
32067 * A cache of calculated `LocalModuleScope`s for each NgModule declared in the current program.
32068
32069 */
32070 this.cache = new Map();
32071 /**
32072 * Tracks the `RemoteScope` for components requiring "remote scoping".
32073 *
32074 * Remote scoping is when the set of directives which apply to a given component is set in the
32075 * NgModule's file instead of directly on the component def (which is sometimes needed to get
32076 * around cyclic import issues). This is not used in calculation of `LocalModuleScope`s, but is
32077 * tracked here for convenience.
32078 */
32079 this.remoteScoping = new Map();
32080 /**
32081 * Tracks errors accumulated in the processing of scopes for each module declaration.
32082 */
32083 this.scopeErrors = new Map();
32084 /**
32085 * Tracks which NgModules have directives/pipes that are declared in more than one module.
32086 */
32087 this.modulesWithStructuralErrors = new Set();
32088 }
32089 /**
32090 * Add an NgModule's data to the registry.
32091 */
32092 registerNgModuleMetadata(data) {
32093 this.assertCollecting();
32094 const ngModule = data.ref.node;
32095 this.moduleToRef.set(data.ref.node, data.ref);
32096 // Iterate over the module's declarations, and add them to declarationToModule. If duplicates
32097 // are found, they're instead tracked in duplicateDeclarations.
32098 for (const decl of data.declarations) {
32099 this.registerDeclarationOfModule(ngModule, decl, data.rawDeclarations);
32100 }
32101 }
32102 registerDirectiveMetadata(directive) { }
32103 registerPipeMetadata(pipe) { }
32104 getScopeForComponent(clazz) {
32105 const scope = !this.declarationToModule.has(clazz) ?
32106 null :
32107 this.getScopeOfModule(this.declarationToModule.get(clazz).ngModule);
32108 return scope;
32109 }
32110 /**
32111 * If `node` is declared in more than one NgModule (duplicate declaration), then get the
32112 * `DeclarationData` for each offending declaration.
32113 *
32114 * Ordinarily a class is only declared in one NgModule, in which case this function returns
32115 * `null`.
32116 */
32117 getDuplicateDeclarations(node) {
32118 if (!this.duplicateDeclarations.has(node)) {
32119 return null;
32120 }
32121 return Array.from(this.duplicateDeclarations.get(node).values());
32122 }
32123 /**
32124 * Collects registered data for a module and its directives/pipes and convert it into a full
32125 * `LocalModuleScope`.
32126 *
32127 * This method implements the logic of NgModule imports and exports. It returns the
32128 * `LocalModuleScope` for the given NgModule if one can be produced, `null` if no scope was ever
32129 * defined, or the string `'error'` if the scope contained errors.
32130 */
32131 getScopeOfModule(clazz) {
32132 return this.moduleToRef.has(clazz) ?
32133 this.getScopeOfModuleReference(this.moduleToRef.get(clazz)) :
32134 null;
32135 }
32136 /**
32137 * Retrieves any `ts.Diagnostic`s produced during the calculation of the `LocalModuleScope` for
32138 * the given NgModule, or `null` if no errors were present.
32139 */
32140 getDiagnosticsOfModule(clazz) {
32141 // Required to ensure the errors are populated for the given class. If it has been processed
32142 // before, this will be a no-op due to the scope cache.
32143 this.getScopeOfModule(clazz);
32144 if (this.scopeErrors.has(clazz)) {
32145 return this.scopeErrors.get(clazz);
32146 }
32147 else {
32148 return null;
32149 }
32150 }
32151 /**
32152 * Returns a collection of the compilation scope for each registered declaration.
32153 */
32154 getCompilationScopes() {
32155 const scopes = [];
32156 this.declarationToModule.forEach((declData, declaration) => {
32157 const scope = this.getScopeOfModule(declData.ngModule);
32158 if (scope !== null) {
32159 scopes.push(Object.assign({ declaration, ngModule: declData.ngModule }, scope.compilation));
32160 }
32161 });
32162 return scopes;
32163 }
32164 registerDeclarationOfModule(ngModule, decl, rawDeclarations) {
32165 const declData = {
32166 ngModule,
32167 ref: decl,
32168 rawDeclarations,
32169 };
32170 // First, check for duplicate declarations of the same directive/pipe.
32171 if (this.duplicateDeclarations.has(decl.node)) {
32172 // This directive/pipe has already been identified as being duplicated. Add this module to the
32173 // map of modules for which a duplicate declaration exists.
32174 this.duplicateDeclarations.get(decl.node).set(ngModule, declData);
32175 }
32176 else if (this.declarationToModule.has(decl.node) &&
32177 this.declarationToModule.get(decl.node).ngModule !== ngModule) {
32178 // This directive/pipe is already registered as declared in another module. Mark it as a
32179 // duplicate instead.
32180 const duplicateDeclMap = new Map();
32181 const firstDeclData = this.declarationToModule.get(decl.node);
32182 // Mark both modules as having duplicate declarations.
32183 this.modulesWithStructuralErrors.add(firstDeclData.ngModule);
32184 this.modulesWithStructuralErrors.add(ngModule);
32185 // Being detected as a duplicate means there are two NgModules (for now) which declare this
32186 // directive/pipe. Add both of them to the duplicate tracking map.
32187 duplicateDeclMap.set(firstDeclData.ngModule, firstDeclData);
32188 duplicateDeclMap.set(ngModule, declData);
32189 this.duplicateDeclarations.set(decl.node, duplicateDeclMap);
32190 // Remove the directive/pipe from `declarationToModule` as it's a duplicate declaration, and
32191 // therefore not valid.
32192 this.declarationToModule.delete(decl.node);
32193 }
32194 else {
32195 // This is the first declaration of this directive/pipe, so map it.
32196 this.declarationToModule.set(decl.node, declData);
32197 }
32198 }
32199 /**
32200 * Implementation of `getScopeOfModule` which accepts a reference to a class.
32201 */
32202 getScopeOfModuleReference(ref) {
32203 if (this.cache.has(ref.node)) {
32204 return this.cache.get(ref.node);
32205 }
32206 // Seal the registry to protect the integrity of the `LocalModuleScope` cache.
32207 this.sealed = true;
32208 // `ref` should be an NgModule previously added to the registry. If not, a scope for it
32209 // cannot be produced.
32210 const ngModule = this.localReader.getNgModuleMetadata(ref);
32211 if (ngModule === null) {
32212 this.cache.set(ref.node, null);
32213 return null;
32214 }
32215 // Modules which contributed to the compilation scope of this module.
32216 const compilationModules = new Set([ngModule.ref.node]);
32217 // Modules which contributed to the export scope of this module.
32218 const exportedModules = new Set([ngModule.ref.node]);
32219 // Errors produced during computation of the scope are recorded here. At the end, if this array
32220 // isn't empty then `undefined` will be cached and returned to indicate this scope is invalid.
32221 const diagnostics = [];
32222 // At this point, the goal is to produce two distinct transitive sets:
32223 // - the directives and pipes which are visible to components declared in the NgModule.
32224 // - the directives and pipes which are exported to any NgModules which import this one.
32225 // Directives and pipes in the compilation scope.
32226 const compilationDirectives = new Map();
32227 const compilationPipes = new Map();
32228 const declared = new Set();
32229 // Directives and pipes exported to any importing NgModules.
32230 const exportDirectives = new Map();
32231 const exportPipes = new Map();
32232 // The algorithm is as follows:
32233 // 1) Add all of the directives/pipes from each NgModule imported into the current one to the
32234 // compilation scope.
32235 // 2) Add directives/pipes declared in the NgModule to the compilation scope. At this point, the
32236 // compilation scope is complete.
32237 // 3) For each entry in the NgModule's exports:
32238 // a) Attempt to resolve it as an NgModule with its own exported directives/pipes. If it is
32239 // one, add them to the export scope of this NgModule.
32240 // b) Otherwise, it should be a class in the compilation scope of this NgModule. If it is,
32241 // add it to the export scope.
32242 // c) If it's neither an NgModule nor a directive/pipe in the compilation scope, then this
32243 // is an error.
32244 //
32245 let isPoisoned = false;
32246 if (this.modulesWithStructuralErrors.has(ngModule.ref.node)) {
32247 // If the module contains declarations that are duplicates, then it's considered poisoned.
32248 isPoisoned = true;
32249 }
32250 // 1) process imports.
32251 for (const decl of ngModule.imports) {
32252 const importScope = this.getExportedScope(decl, diagnostics, ref.node, 'import');
32253 if (importScope === null) {
32254 // An import wasn't an NgModule, so record an error.
32255 diagnostics.push(invalidRef(ref.node, decl, 'import'));
32256 isPoisoned = true;
32257 continue;
32258 }
32259 else if (importScope === 'invalid' || importScope.exported.isPoisoned) {
32260 // An import was an NgModule but contained errors of its own. Record this as an error too,
32261 // because this scope is always going to be incorrect if one of its imports could not be
32262 // read.
32263 diagnostics.push(invalidTransitiveNgModuleRef(ref.node, decl, 'import'));
32264 isPoisoned = true;
32265 if (importScope === 'invalid') {
32266 continue;
32267 }
32268 }
32269 for (const directive of importScope.exported.directives) {
32270 compilationDirectives.set(directive.ref.node, directive);
32271 }
32272 for (const pipe of importScope.exported.pipes) {
32273 compilationPipes.set(pipe.ref.node, pipe);
32274 }
32275 for (const importedModule of importScope.exported.ngModules) {
32276 compilationModules.add(importedModule);
32277 }
32278 }
32279 // 2) add declarations.
32280 for (const decl of ngModule.declarations) {
32281 const directive = this.localReader.getDirectiveMetadata(decl);
32282 const pipe = this.localReader.getPipeMetadata(decl);
32283 if (directive !== null) {
32284 compilationDirectives.set(decl.node, Object.assign(Object.assign({}, directive), { ref: decl }));
32285 if (directive.isPoisoned) {
32286 isPoisoned = true;
32287 }
32288 }
32289 else if (pipe !== null) {
32290 compilationPipes.set(decl.node, Object.assign(Object.assign({}, pipe), { ref: decl }));
32291 }
32292 else {
32293 const errorNode = decl.getOriginForDiagnostics(ngModule.rawDeclarations);
32294 diagnostics.push(makeDiagnostic(ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, `The class '${decl.node.name.text}' is listed in the declarations ` +
32295 `of the NgModule '${ngModule.ref.node.name
32296 .text}', but is not a directive, a component, or a pipe. ` +
32297 `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.`)]));
32298 isPoisoned = true;
32299 continue;
32300 }
32301 declared.add(decl.node);
32302 }
32303 // 3) process exports.
32304 // Exports can contain modules, components, or directives. They're processed differently.
32305 // Modules are straightforward. Directives and pipes from exported modules are added to the
32306 // export maps. Directives/pipes are different - they might be exports of declared types or
32307 // imported types.
32308 for (const decl of ngModule.exports) {
32309 // Attempt to resolve decl as an NgModule.
32310 const exportScope = this.getExportedScope(decl, diagnostics, ref.node, 'export');
32311 if (exportScope === 'invalid' || (exportScope !== null && exportScope.exported.isPoisoned)) {
32312 // An export was an NgModule but contained errors of its own. Record this as an error too,
32313 // because this scope is always going to be incorrect if one of its exports could not be
32314 // read.
32315 diagnostics.push(invalidTransitiveNgModuleRef(ref.node, decl, 'export'));
32316 isPoisoned = true;
32317 if (exportScope === 'invalid') {
32318 continue;
32319 }
32320 }
32321 else if (exportScope !== null) {
32322 // decl is an NgModule.
32323 for (const directive of exportScope.exported.directives) {
32324 exportDirectives.set(directive.ref.node, directive);
32325 }
32326 for (const pipe of exportScope.exported.pipes) {
32327 exportPipes.set(pipe.ref.node, pipe);
32328 }
32329 for (const exportedModule of exportScope.exported.ngModules) {
32330 exportedModules.add(exportedModule);
32331 }
32332 }
32333 else if (compilationDirectives.has(decl.node)) {
32334 // decl is a directive or component in the compilation scope of this NgModule.
32335 const directive = compilationDirectives.get(decl.node);
32336 exportDirectives.set(decl.node, directive);
32337 }
32338 else if (compilationPipes.has(decl.node)) {
32339 // decl is a pipe in the compilation scope of this NgModule.
32340 const pipe = compilationPipes.get(decl.node);
32341 exportPipes.set(decl.node, pipe);
32342 }
32343 else {
32344 // decl is an unknown export.
32345 if (this.localReader.getDirectiveMetadata(decl) !== null ||
32346 this.localReader.getPipeMetadata(decl) !== null) {
32347 diagnostics.push(invalidReexport(ref.node, decl));
32348 }
32349 else {
32350 diagnostics.push(invalidRef(ref.node, decl, 'export'));
32351 }
32352 isPoisoned = true;
32353 continue;
32354 }
32355 }
32356 const exported = {
32357 directives: Array.from(exportDirectives.values()),
32358 pipes: Array.from(exportPipes.values()),
32359 ngModules: Array.from(exportedModules),
32360 isPoisoned,
32361 };
32362 const reexports = this.getReexports(ngModule, ref, declared, exported, diagnostics);
32363 // Finally, produce the `LocalModuleScope` with both the compilation and export scopes.
32364 const scope = {
32365 ngModule: ngModule.ref.node,
32366 compilation: {
32367 directives: Array.from(compilationDirectives.values()),
32368 pipes: Array.from(compilationPipes.values()),
32369 ngModules: Array.from(compilationModules),
32370 isPoisoned,
32371 },
32372 exported,
32373 reexports,
32374 schemas: ngModule.schemas,
32375 };
32376 // Check if this scope had any errors during production.
32377 if (diagnostics.length > 0) {
32378 // Save the errors for retrieval.
32379 this.scopeErrors.set(ref.node, diagnostics);
32380 // Mark this module as being tainted.
32381 this.modulesWithStructuralErrors.add(ref.node);
32382 }
32383 this.cache.set(ref.node, scope);
32384 return scope;
32385 }
32386 /**
32387 * Check whether a component requires remote scoping.
32388 */
32389 getRemoteScope(node) {
32390 return this.remoteScoping.has(node) ? this.remoteScoping.get(node) : null;
32391 }
32392 /**
32393 * Set a component as requiring remote scoping, with the given directives and pipes to be
32394 * registered remotely.
32395 */
32396 setComponentRemoteScope(node, directives, pipes) {
32397 this.remoteScoping.set(node, { directives, pipes });
32398 }
32399 /**
32400 * Look up the `ExportScope` of a given `Reference` to an NgModule.
32401 *
32402 * The NgModule in question may be declared locally in the current ts.Program, or it may be
32403 * declared in a .d.ts file.
32404 *
32405 * @returns `null` if no scope could be found, or `'invalid'` if the `Reference` is not a valid
32406 * NgModule.
32407 *
32408 * May also contribute diagnostics of its own by adding to the given `diagnostics`
32409 * array parameter.
32410 */
32411 getExportedScope(ref, diagnostics, ownerForErrors, type) {
32412 if (ref.node.getSourceFile().isDeclarationFile) {
32413 // The NgModule is declared in a .d.ts file. Resolve it with the `DependencyScopeReader`.
32414 if (!ts$1.isClassDeclaration(ref.node)) {
32415 // The NgModule is in a .d.ts file but is not declared as a ts.ClassDeclaration. This is an
32416 // error in the .d.ts metadata.
32417 const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT :
32418 ErrorCode.NGMODULE_INVALID_EXPORT;
32419 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`));
32420 return 'invalid';
32421 }
32422 return this.dependencyScopeReader.resolve(ref);
32423 }
32424 else {
32425 // The NgModule is declared locally in the current program. Resolve it from the registry.
32426 return this.getScopeOfModuleReference(ref);
32427 }
32428 }
32429 getReexports(ngModule, ref, declared, exported, diagnostics) {
32430 let reexports = null;
32431 const sourceFile = ref.node.getSourceFile();
32432 if (this.aliasingHost === null) {
32433 return null;
32434 }
32435 reexports = [];
32436 // Track re-exports by symbol name, to produce diagnostics if two alias re-exports would share
32437 // the same name.
32438 const reexportMap = new Map();
32439 // Alias ngModuleRef added for readability below.
32440 const ngModuleRef = ref;
32441 const addReexport = (exportRef) => {
32442 if (exportRef.node.getSourceFile() === sourceFile) {
32443 return;
32444 }
32445 const isReExport = !declared.has(exportRef.node);
32446 const exportName = this.aliasingHost.maybeAliasSymbolAs(exportRef, sourceFile, ngModule.ref.node.name.text, isReExport);
32447 if (exportName === null) {
32448 return;
32449 }
32450 if (!reexportMap.has(exportName)) {
32451 if (exportRef.alias && exportRef.alias instanceof ExternalExpr) {
32452 reexports.push({
32453 fromModule: exportRef.alias.value.moduleName,
32454 symbolName: exportRef.alias.value.name,
32455 asAlias: exportName,
32456 });
32457 }
32458 else {
32459 const expr = this.refEmitter.emit(exportRef.cloneWithNoIdentifiers(), sourceFile);
32460 if (!(expr instanceof ExternalExpr) || expr.value.moduleName === null ||
32461 expr.value.name === null) {
32462 throw new Error('Expected ExternalExpr');
32463 }
32464 reexports.push({
32465 fromModule: expr.value.moduleName,
32466 symbolName: expr.value.name,
32467 asAlias: exportName,
32468 });
32469 }
32470 reexportMap.set(exportName, exportRef);
32471 }
32472 else {
32473 // Another re-export already used this name. Produce a diagnostic.
32474 const prevRef = reexportMap.get(exportName);
32475 diagnostics.push(reexportCollision(ngModuleRef.node, prevRef, exportRef));
32476 }
32477 };
32478 for (const { ref } of exported.directives) {
32479 addReexport(ref);
32480 }
32481 for (const { ref } of exported.pipes) {
32482 addReexport(ref);
32483 }
32484 return reexports;
32485 }
32486 assertCollecting() {
32487 if (this.sealed) {
32488 throw new Error(`Assertion: LocalModuleScopeRegistry is not COLLECTING`);
32489 }
32490 }
32491 }
32492 /**
32493 * Produce a `ts.Diagnostic` for an invalid import or export from an NgModule.
32494 */
32495 function invalidRef(clazz, decl, type) {
32496 const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT;
32497 const resolveTarget = type === 'import' ? 'NgModule' : 'NgModule, Component, Directive, or Pipe';
32498 let message = `Appears in the NgModule.${type}s of ${nodeNameForError(clazz)}, but could not be resolved to an ${resolveTarget} class.` +
32499 '\n\n';
32500 const library = decl.ownedByModuleGuess !== null ? ` (${decl.ownedByModuleGuess})` : '';
32501 const sf = decl.node.getSourceFile();
32502 // Provide extra context to the error for the user.
32503 if (!sf.isDeclarationFile) {
32504 // This is a file in the user's program.
32505 const annotationType = type === 'import' ? '@NgModule' : 'Angular';
32506 message += `Is it missing an ${annotationType} annotation?`;
32507 }
32508 else if (sf.fileName.indexOf('node_modules') !== -1) {
32509 // This file comes from a third-party library in node_modules.
32510 message +=
32511 `This likely means that the library${library} which declares ${decl.debugName} has not ` +
32512 'been processed correctly by ngcc, or is not compatible with Angular Ivy. Check if a ' +
32513 'newer version of the library is available, and update if so. Also consider checking ' +
32514 'with the library\'s authors to see if the library is expected to be compatible with Ivy.';
32515 }
32516 else {
32517 // This is a monorepo style local dependency. Unfortunately these are too different to really
32518 // offer much more advice than this.
32519 message += `This likely means that the dependency${library} which declares ${decl.debugName} has not been processed correctly by ngcc.`;
32520 }
32521 return makeDiagnostic(code, identifierOfNode(decl.node) || decl.node, message);
32522 }
32523 /**
32524 * Produce a `ts.Diagnostic` for an import or export which itself has errors.
32525 */
32526 function invalidTransitiveNgModuleRef(clazz, decl, type) {
32527 const code = type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT;
32528 return makeDiagnostic(code, identifierOfNode(decl.node) || decl.node, `Appears in the NgModule.${type}s of ${nodeNameForError(clazz)}, but itself has errors`);
32529 }
32530 /**
32531 * Produce a `ts.Diagnostic` for an exported directive or pipe which was not declared or imported
32532 * by the NgModule in question.
32533 */
32534 function invalidReexport(clazz, decl) {
32535 return makeDiagnostic(ErrorCode.NGMODULE_INVALID_REEXPORT, identifierOfNode(decl.node) || decl.node, `Present in the NgModule.exports of ${nodeNameForError(clazz)} but neither declared nor imported`);
32536 }
32537 /**
32538 * Produce a `ts.Diagnostic` for a collision in re-export names between two directives/pipes.
32539 */
32540 function reexportCollision(module, refA, refB) {
32541 const childMessageText = `This directive/pipe is part of the exports of '${module.name.text}' and shares the same name as another exported directive/pipe.`;
32542 return makeDiagnostic(ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION, module.name, `
32543 There was a name collision between two classes named '${refA.node.name.text}', which are both part of the exports of '${module.name.text}'.
32544
32545 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.
32546
32547 To fix this problem please re-export one or both classes directly from this file.
32548 `.trim(), [
32549 makeRelatedInformation(refA.node.name, childMessageText),
32550 makeRelatedInformation(refB.node.name, childMessageText),
32551 ]);
32552 }
32553
32554 /**
32555 * @license
32556 * Copyright Google LLC All Rights Reserved.
32557 *
32558 * Use of this source code is governed by an MIT-style license that can be
32559 * found in the LICENSE file at https://angular.io/license
32560 */
32561 /**
32562 * Computes scope information to be used in template type checking.
32563 */
32564 class TypeCheckScopeRegistry {
32565 constructor(scopeReader, metaReader) {
32566 this.scopeReader = scopeReader;
32567 this.metaReader = metaReader;
32568 /**
32569 * Cache of flattened directive metadata. Because flattened metadata is scope-invariant it's
32570 * cached individually, such that all scopes refer to the same flattened metadata.
32571 */
32572 this.flattenedDirectiveMetaCache = new Map();
32573 /**
32574 * Cache of the computed type check scope per NgModule declaration.
32575 */
32576 this.scopeCache = new Map();
32577 }
32578 /**
32579 * Computes the type-check scope information for the component declaration. If the NgModule
32580 * contains an error, then 'error' is returned. If the component is not declared in any NgModule,
32581 * an empty type-check scope is returned.
32582 */
32583 getTypeCheckScope(node) {
32584 const matcher = new SelectorMatcher();
32585 const directives = [];
32586 const pipes = new Map();
32587 const scope = this.scopeReader.getScopeForComponent(node);
32588 if (scope === null) {
32589 return {
32590 matcher,
32591 directives,
32592 pipes,
32593 schemas: [],
32594 isPoisoned: false,
32595 };
32596 }
32597 if (this.scopeCache.has(scope.ngModule)) {
32598 return this.scopeCache.get(scope.ngModule);
32599 }
32600 for (const meta of scope.compilation.directives) {
32601 if (meta.selector !== null) {
32602 const extMeta = this.getTypeCheckDirectiveMetadata(meta.ref);
32603 matcher.addSelectables(CssSelector.parse(meta.selector), extMeta);
32604 directives.push(extMeta);
32605 }
32606 }
32607 for (const { name, ref } of scope.compilation.pipes) {
32608 if (!ts$1.isClassDeclaration(ref.node)) {
32609 throw new Error(`Unexpected non-class declaration ${ts$1.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`);
32610 }
32611 pipes.set(name, ref);
32612 }
32613 const typeCheckScope = {
32614 matcher,
32615 directives,
32616 pipes,
32617 schemas: scope.schemas,
32618 isPoisoned: scope.compilation.isPoisoned || scope.exported.isPoisoned,
32619 };
32620 this.scopeCache.set(scope.ngModule, typeCheckScope);
32621 return typeCheckScope;
32622 }
32623 getTypeCheckDirectiveMetadata(ref) {
32624 const clazz = ref.node;
32625 if (this.flattenedDirectiveMetaCache.has(clazz)) {
32626 return this.flattenedDirectiveMetaCache.get(clazz);
32627 }
32628 const meta = flattenInheritedDirectiveMetadata(this.metaReader, ref);
32629 this.flattenedDirectiveMetaCache.set(clazz, meta);
32630 return meta;
32631 }
32632 }
32633
32634 /**
32635 * @license
32636 * Copyright Google LLC All Rights Reserved.
32637 *
32638 * Use of this source code is governed by an MIT-style license that can be
32639 * found in the LICENSE file at https://angular.io/license
32640 */
32641 /**
32642 * A `Symbol` which is used to patch extension data onto `ts.SourceFile`s.
32643 */
32644 const NgExtension = Symbol('NgExtension');
32645 /**
32646 * Narrows a `ts.SourceFile` if it has an `NgExtension` property.
32647 */
32648 function isExtended(sf) {
32649 return sf[NgExtension] !== undefined;
32650 }
32651 /**
32652 * Check whether `sf` is a shim `ts.SourceFile` (either a per-file shim or a top-level shim).
32653 */
32654 function isShim(sf) {
32655 return isExtended(sf) && (sf[NgExtension].fileShim !== null || sf[NgExtension].isTopLevelShim);
32656 }
32657
32658 /**
32659 * @license
32660 * Copyright Google LLC All Rights Reserved.
32661 *
32662 * Use of this source code is governed by an MIT-style license that can be
32663 * found in the LICENSE file at https://angular.io/license
32664 */
32665 const STRIP_NG_FACTORY = /(.*)NgFactory$/;
32666 function generatedFactoryTransform(factoryMap, importRewriter) {
32667 return (context) => {
32668 return (file) => {
32669 return transformFactorySourceFile(factoryMap, context, importRewriter, file);
32670 };
32671 };
32672 }
32673 function transformFactorySourceFile(factoryMap, context, importRewriter, file) {
32674 // If this is not a generated file, it won't have factory info associated with it.
32675 if (!factoryMap.has(file.fileName)) {
32676 // Don't transform non-generated code.
32677 return file;
32678 }
32679 const { moduleSymbols, sourceFilePath } = factoryMap.get(file.fileName);
32680 // Not every exported factory statement is valid. They were generated before the program was
32681 // analyzed, and before ngtsc knew which symbols were actually NgModules. factoryMap contains
32682 // that knowledge now, so this transform filters the statement list and removes exported factories
32683 // that aren't actually factories.
32684 //
32685 // This could leave the generated factory file empty. To prevent this (it causes issues with
32686 // closure compiler) a 'ɵNonEmptyModule' export was added when the factory shim was created.
32687 // Preserve that export if needed, and remove it otherwise.
32688 //
32689 // Additionally, an import to @angular/core is generated, but the current compilation unit could
32690 // actually be @angular/core, in which case such an import is invalid and should be replaced with
32691 // the proper path to access Ivy symbols in core.
32692 // The filtered set of statements.
32693 const transformedStatements = [];
32694 // The statement identified as the ɵNonEmptyModule export.
32695 let nonEmptyExport = null;
32696 // Extracted identifiers which refer to import statements from @angular/core.
32697 const coreImportIdentifiers = new Set();
32698 // Consider all the statements.
32699 for (const stmt of file.statements) {
32700 // Look for imports to @angular/core.
32701 if (ts$1.isImportDeclaration(stmt) && ts$1.isStringLiteral(stmt.moduleSpecifier) &&
32702 stmt.moduleSpecifier.text === '@angular/core') {
32703 // Update the import path to point to the correct file using the ImportRewriter.
32704 const rewrittenModuleSpecifier = importRewriter.rewriteSpecifier('@angular/core', sourceFilePath);
32705 if (rewrittenModuleSpecifier !== stmt.moduleSpecifier.text) {
32706 transformedStatements.push(ts$1.updateImportDeclaration(stmt, stmt.decorators, stmt.modifiers, stmt.importClause, ts$1.createStringLiteral(rewrittenModuleSpecifier)));
32707 // Record the identifier by which this imported module goes, so references to its symbols
32708 // can be discovered later.
32709 if (stmt.importClause !== undefined && stmt.importClause.namedBindings !== undefined &&
32710 ts$1.isNamespaceImport(stmt.importClause.namedBindings)) {
32711 coreImportIdentifiers.add(stmt.importClause.namedBindings.name.text);
32712 }
32713 }
32714 else {
32715 transformedStatements.push(stmt);
32716 }
32717 }
32718 else if (ts$1.isVariableStatement(stmt) && stmt.declarationList.declarations.length === 1) {
32719 const decl = stmt.declarationList.declarations[0];
32720 // If this is the ɵNonEmptyModule export, then save it for later.
32721 if (ts$1.isIdentifier(decl.name)) {
32722 if (decl.name.text === 'ɵNonEmptyModule') {
32723 nonEmptyExport = stmt;
32724 continue;
32725 }
32726 // Otherwise, check if this export is a factory for a known NgModule, and retain it if so.
32727 const match = STRIP_NG_FACTORY.exec(decl.name.text);
32728 const module = match ? moduleSymbols.get(match[1]) : null;
32729 if (module) {
32730 // If the module can be tree shaken, then the factory should be wrapped in a
32731 // `noSideEffects()` call which tells Closure to treat the expression as pure, allowing
32732 // it to be removed if the result is not used.
32733 //
32734 // `NgModule`s with an `id` property will be lazy loaded. Google-internal lazy loading
32735 // infra relies on a side effect from the `new NgModuleFactory()` call, which registers
32736 // the module globally. Because of this, we **cannot** tree shake any module which has
32737 // an `id` property. Doing so would cause lazy loaded modules to never be registered.
32738 const moduleIsTreeShakable = !module.hasId;
32739 const newStmt = !moduleIsTreeShakable ?
32740 stmt :
32741 updateInitializers(stmt, (init) => init ? wrapInNoSideEffects(init) : undefined);
32742 transformedStatements.push(newStmt);
32743 }
32744 }
32745 else {
32746 // Leave the statement alone, as it can't be understood.
32747 transformedStatements.push(stmt);
32748 }
32749 }
32750 else {
32751 // Include non-variable statements (imports, etc).
32752 transformedStatements.push(stmt);
32753 }
32754 }
32755 // Check whether the empty module export is still needed.
32756 if (!transformedStatements.some(ts$1.isVariableStatement) && nonEmptyExport !== null) {
32757 // If the resulting file has no factories, include an empty export to
32758 // satisfy closure compiler.
32759 transformedStatements.push(nonEmptyExport);
32760 }
32761 file = ts$1.updateSourceFileNode(file, transformedStatements);
32762 // If any imports to @angular/core were detected and rewritten (which happens when compiling
32763 // @angular/core), go through the SourceFile and rewrite references to symbols imported from core.
32764 if (coreImportIdentifiers.size > 0) {
32765 const visit = (node) => {
32766 node = ts$1.visitEachChild(node, child => visit(child), context);
32767 // Look for expressions of the form "i.s" where 'i' is a detected name for an @angular/core
32768 // import that was changed above. Rewrite 's' using the ImportResolver.
32769 if (ts$1.isPropertyAccessExpression(node) && ts$1.isIdentifier(node.expression) &&
32770 coreImportIdentifiers.has(node.expression.text)) {
32771 // This is an import of a symbol from @angular/core. Transform it with the importRewriter.
32772 const rewrittenSymbol = importRewriter.rewriteSymbol(node.name.text, '@angular/core');
32773 if (rewrittenSymbol !== node.name.text) {
32774 const updated = ts$1.updatePropertyAccess(node, node.expression, ts$1.createIdentifier(rewrittenSymbol));
32775 node = updated;
32776 }
32777 }
32778 return node;
32779 };
32780 file = visit(file);
32781 }
32782 return file;
32783 }
32784 /**
32785 * Wraps the given expression in a call to `ɵnoSideEffects()`, which tells
32786 * Closure we don't care about the side effects of this expression and it should
32787 * be treated as "pure". Closure is free to tree shake this expression if its
32788 * result is not used.
32789 *
32790 * Example: Takes `1 + 2` and returns `i0.ɵnoSideEffects(() => 1 + 2)`.
32791 */
32792 function wrapInNoSideEffects(expr) {
32793 const noSideEffects = ts$1.createPropertyAccess(ts$1.createIdentifier('i0'), 'ɵnoSideEffects');
32794 return ts$1.createCall(noSideEffects,
32795 /* typeArguments */ [],
32796 /* arguments */
32797 [
32798 ts$1.createFunctionExpression(
32799 /* modifiers */ [],
32800 /* asteriskToken */ undefined,
32801 /* name */ undefined,
32802 /* typeParameters */ [],
32803 /* parameters */ [],
32804 /* type */ undefined,
32805 /* body */ ts$1.createBlock([
32806 ts$1.createReturn(expr),
32807 ])),
32808 ]);
32809 }
32810 /**
32811 * Clones and updates the initializers for a given statement to use the new
32812 * expression provided. Does not mutate the input statement.
32813 */
32814 function updateInitializers(stmt, update) {
32815 return ts$1.updateVariableStatement(stmt, stmt.modifiers, ts$1.updateVariableDeclarationList(stmt.declarationList, stmt.declarationList.declarations.map((decl) => ts$1.updateVariableDeclaration(decl, decl.name, decl.type, update(decl.initializer)))));
32816 }
32817
32818 /**
32819 * @license
32820 * Copyright Google LLC All Rights Reserved.
32821 *
32822 * Use of this source code is governed by an MIT-style license that can be
32823 * found in the LICENSE file at https://angular.io/license
32824 */
32825 const IVY_SWITCH_PRE_SUFFIX = '__PRE_R3__';
32826 const IVY_SWITCH_POST_SUFFIX = '__POST_R3__';
32827 function ivySwitchTransform(_) {
32828 return flipIvySwitchInFile;
32829 }
32830 function flipIvySwitchInFile(sf) {
32831 // To replace the statements array, it must be copied. This only needs to happen if a statement
32832 // must actually be replaced within the array, so the newStatements array is lazily initialized.
32833 let newStatements = undefined;
32834 // Iterate over the statements in the file.
32835 for (let i = 0; i < sf.statements.length; i++) {
32836 const statement = sf.statements[i];
32837 // Skip over everything that isn't a variable statement.
32838 if (!ts$1.isVariableStatement(statement) || !hasIvySwitches(statement)) {
32839 continue;
32840 }
32841 // This statement needs to be replaced. Check if the newStatements array needs to be lazily
32842 // initialized to a copy of the original statements.
32843 if (newStatements === undefined) {
32844 newStatements = [...sf.statements];
32845 }
32846 // Flip any switches in the VariableStatement. If there were any, a new statement will be
32847 // returned; otherwise the old statement will be.
32848 newStatements[i] = flipIvySwitchesInVariableStatement(statement, sf.statements);
32849 }
32850 // Only update the statements in the SourceFile if any have changed.
32851 if (newStatements !== undefined) {
32852 return ts$1.updateSourceFileNode(sf, newStatements);
32853 }
32854 return sf;
32855 }
32856 /**
32857 * Look for the ts.Identifier of a ts.Declaration with this name.
32858 *
32859 * The real identifier is needed (rather than fabricating one) as TypeScript decides how to
32860 * reference this identifier based on information stored against its node in the AST, which a
32861 * synthetic node would not have. In particular, since the post-switch variable is often exported,
32862 * TypeScript needs to know this so it can write `exports.VAR` instead of just `VAR` when emitting
32863 * code.
32864 *
32865 * Only variable, function, and class declarations are currently searched.
32866 */
32867 function findPostSwitchIdentifier(statements, name) {
32868 for (const stmt of statements) {
32869 if (ts$1.isVariableStatement(stmt)) {
32870 const decl = stmt.declarationList.declarations.find(decl => ts$1.isIdentifier(decl.name) && decl.name.text === name);
32871 if (decl !== undefined) {
32872 return decl.name;
32873 }
32874 }
32875 else if (ts$1.isFunctionDeclaration(stmt) || ts$1.isClassDeclaration(stmt)) {
32876 if (stmt.name !== undefined && ts$1.isIdentifier(stmt.name) && stmt.name.text === name) {
32877 return stmt.name;
32878 }
32879 }
32880 }
32881 return null;
32882 }
32883 /**
32884 * Flip any Ivy switches which are discovered in the given ts.VariableStatement.
32885 */
32886 function flipIvySwitchesInVariableStatement(stmt, statements) {
32887 // Build a new list of variable declarations. Specific declarations that are initialized to a
32888 // pre-switch identifier will be replaced with a declaration initialized to the post-switch
32889 // identifier.
32890 const newDeclarations = [...stmt.declarationList.declarations];
32891 for (let i = 0; i < newDeclarations.length; i++) {
32892 const decl = newDeclarations[i];
32893 // Skip declarations that aren't initialized to an identifier.
32894 if (decl.initializer === undefined || !ts$1.isIdentifier(decl.initializer)) {
32895 continue;
32896 }
32897 // Skip declarations that aren't Ivy switches.
32898 if (!decl.initializer.text.endsWith(IVY_SWITCH_PRE_SUFFIX)) {
32899 continue;
32900 }
32901 // Determine the name of the post-switch variable.
32902 const postSwitchName = decl.initializer.text.replace(IVY_SWITCH_PRE_SUFFIX, IVY_SWITCH_POST_SUFFIX);
32903 // Find the post-switch variable identifier. If one can't be found, it's an error. This is
32904 // reported as a thrown error and not a diagnostic as transformers cannot output diagnostics.
32905 const newIdentifier = findPostSwitchIdentifier(statements, postSwitchName);
32906 if (newIdentifier === null) {
32907 throw new Error(`Unable to find identifier ${postSwitchName} in ${stmt.getSourceFile().fileName} for the Ivy switch.`);
32908 }
32909 newDeclarations[i] = ts$1.updateVariableDeclaration(
32910 /* node */ decl,
32911 /* name */ decl.name,
32912 /* type */ decl.type,
32913 /* initializer */ newIdentifier);
32914 }
32915 const newDeclList = ts$1.updateVariableDeclarationList(
32916 /* declarationList */ stmt.declarationList,
32917 /* declarations */ newDeclarations);
32918 const newStmt = ts$1.updateVariableStatement(
32919 /* statement */ stmt,
32920 /* modifiers */ stmt.modifiers,
32921 /* declarationList */ newDeclList);
32922 return newStmt;
32923 }
32924 /**
32925 * Check whether the given VariableStatement has any Ivy switch variables.
32926 */
32927 function hasIvySwitches(stmt) {
32928 return stmt.declarationList.declarations.some(decl => decl.initializer !== undefined && ts$1.isIdentifier(decl.initializer) &&
32929 decl.initializer.text.endsWith(IVY_SWITCH_PRE_SUFFIX));
32930 }
32931
32932 /**
32933 * @license
32934 * Copyright Google LLC All Rights Reserved.
32935 *
32936 * Use of this source code is governed by an MIT-style license that can be
32937 * found in the LICENSE file at https://angular.io/license
32938 */
32939 var UpdateMode;
32940 (function (UpdateMode) {
32941 /**
32942 * A complete update creates a completely new overlay of type-checking code on top of the user's
32943 * original program, which doesn't include type-checking code from previous calls to
32944 * `updateFiles`.
32945 */
32946 UpdateMode[UpdateMode["Complete"] = 0] = "Complete";
32947 /**
32948 * An incremental update changes the contents of some files in the type-checking program without
32949 * reverting any prior changes.
32950 */
32951 UpdateMode[UpdateMode["Incremental"] = 1] = "Incremental";
32952 })(UpdateMode || (UpdateMode = {}));
32953
32954 /**
32955 * @license
32956 * Copyright Google LLC All Rights Reserved.
32957 *
32958 * Use of this source code is governed by an MIT-style license that can be
32959 * found in the LICENSE file at https://angular.io/license
32960 */
32961 /**
32962 * Describes the scope of the caller's interest in template type-checking results.
32963 */
32964 var OptimizeFor;
32965 (function (OptimizeFor) {
32966 /**
32967 * Indicates that a consumer of a `TemplateTypeChecker` is only interested in results for a given
32968 * file, and wants them as fast as possible.
32969 *
32970 * Calling `TemplateTypeChecker` methods successively for multiple files while specifying
32971 * `OptimizeFor.SingleFile` can result in significant unnecessary overhead overall.
32972 */
32973 OptimizeFor[OptimizeFor["SingleFile"] = 0] = "SingleFile";
32974 /**
32975 * Indicates that a consumer of a `TemplateTypeChecker` intends to query for results pertaining to
32976 * the entire user program, and so the type-checker should internally optimize for this case.
32977 *
32978 * Initial calls to retrieve type-checking information may take longer, but repeated calls to
32979 * gather information for the whole user program will be significantly faster with this mode of
32980 * optimization.
32981 */
32982 OptimizeFor[OptimizeFor["WholeProgram"] = 1] = "WholeProgram";
32983 })(OptimizeFor || (OptimizeFor = {}));
32984
32985 /**
32986 * @license
32987 * Copyright Google LLC All Rights Reserved.
32988 *
32989 * Use of this source code is governed by an MIT-style license that can be
32990 * found in the LICENSE file at https://angular.io/license
32991 */
32992 /**
32993 * Discriminant of an autocompletion source (a `Completion`).
32994 */
32995 var CompletionKind;
32996 (function (CompletionKind) {
32997 CompletionKind[CompletionKind["Reference"] = 0] = "Reference";
32998 CompletionKind[CompletionKind["Variable"] = 1] = "Variable";
32999 })(CompletionKind || (CompletionKind = {}));
33000
33001 /**
33002 * @license
33003 * Copyright Google LLC All Rights Reserved.
33004 *
33005 * Use of this source code is governed by an MIT-style license that can be
33006 * found in the LICENSE file at https://angular.io/license
33007 */
33008 var SymbolKind;
33009 (function (SymbolKind) {
33010 SymbolKind[SymbolKind["Input"] = 0] = "Input";
33011 SymbolKind[SymbolKind["Output"] = 1] = "Output";
33012 SymbolKind[SymbolKind["Binding"] = 2] = "Binding";
33013 SymbolKind[SymbolKind["Reference"] = 3] = "Reference";
33014 SymbolKind[SymbolKind["Variable"] = 4] = "Variable";
33015 SymbolKind[SymbolKind["Directive"] = 5] = "Directive";
33016 SymbolKind[SymbolKind["Element"] = 6] = "Element";
33017 SymbolKind[SymbolKind["Template"] = 7] = "Template";
33018 SymbolKind[SymbolKind["Expression"] = 8] = "Expression";
33019 SymbolKind[SymbolKind["DomBinding"] = 9] = "DomBinding";
33020 SymbolKind[SymbolKind["Pipe"] = 10] = "Pipe";
33021 })(SymbolKind || (SymbolKind = {}));
33022
33023 /**
33024 * @license
33025 * Copyright Google LLC All Rights Reserved.
33026 *
33027 * Use of this source code is governed by an MIT-style license that can be
33028 * found in the LICENSE file at https://angular.io/license
33029 */
33030 /**
33031 * A `ShimGenerator` which adds type-checking files to the `ts.Program`.
33032 *
33033 * This is a requirement for performant template type-checking, as TypeScript will only reuse
33034 * information in the main program when creating the type-checking program if the set of files in
33035 * each are exactly the same. Thus, the main program also needs the synthetic type-checking files.
33036 */
33037 class TypeCheckShimGenerator {
33038 constructor() {
33039 this.extensionPrefix = 'ngtypecheck';
33040 this.shouldEmit = false;
33041 }
33042 generateShimForFile(sf, genFilePath, priorShimSf) {
33043 if (priorShimSf !== null) {
33044 // If this shim existed in the previous program, reuse it now. It might not be correct, but
33045 // reusing it in the main program allows the shape of its imports to potentially remain the
33046 // same and TS can then use the fastest path for incremental program creation. Later during
33047 // the type-checking phase it's going to either be reused, or replaced anyways. Thus there's
33048 // no harm in reuse here even if it's out of date.
33049 return priorShimSf;
33050 }
33051 return ts$1.createSourceFile(genFilePath, 'export const USED_FOR_NG_TYPE_CHECKING = true;', ts$1.ScriptTarget.Latest, true, ts$1.ScriptKind.TS);
33052 }
33053 static shimFor(fileName) {
33054 return absoluteFrom(fileName.replace(/\.tsx?$/, '.ngtypecheck.ts'));
33055 }
33056 }
33057
33058 /**
33059 * @license
33060 * Copyright Google LLC All Rights Reserved.
33061 *
33062 * Use of this source code is governed by an MIT-style license that can be
33063 * found in the LICENSE file at https://angular.io/license
33064 */
33065 const parseSpanComment = /^(\d+),(\d+)$/;
33066 /**
33067 * Reads the trailing comments and finds the first match which is a span comment (i.e. 4,10) on a
33068 * node and returns it as an `AbsoluteSourceSpan`.
33069 *
33070 * Will return `null` if no trailing comments on the node match the expected form of a source span.
33071 */
33072 function readSpanComment(node, sourceFile = node.getSourceFile()) {
33073 return ts$1.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => {
33074 if (kind !== ts$1.SyntaxKind.MultiLineCommentTrivia) {
33075 return null;
33076 }
33077 const commentText = sourceFile.text.substring(pos + 2, end - 2);
33078 const match = commentText.match(parseSpanComment);
33079 if (match === null) {
33080 return null;
33081 }
33082 return new AbsoluteSourceSpan(+match[1], +match[2]);
33083 }) || null;
33084 }
33085 /** Used to identify what type the comment is. */
33086 var CommentTriviaType;
33087 (function (CommentTriviaType) {
33088 CommentTriviaType["DIAGNOSTIC"] = "D";
33089 CommentTriviaType["EXPRESSION_TYPE_IDENTIFIER"] = "T";
33090 })(CommentTriviaType || (CommentTriviaType = {}));
33091 /** Identifies what the TCB expression is for (for example, a directive declaration). */
33092 var ExpressionIdentifier;
33093 (function (ExpressionIdentifier) {
33094 ExpressionIdentifier["DIRECTIVE"] = "DIR";
33095 ExpressionIdentifier["COMPONENT_COMPLETION"] = "COMPCOMP";
33096 ExpressionIdentifier["EVENT_PARAMETER"] = "EP";
33097 })(ExpressionIdentifier || (ExpressionIdentifier = {}));
33098 /** Tags the node with the given expression identifier. */
33099 function addExpressionIdentifier(node, identifier) {
33100 ts$1.addSyntheticTrailingComment(node, ts$1.SyntaxKind.MultiLineCommentTrivia, `${CommentTriviaType.EXPRESSION_TYPE_IDENTIFIER}:${identifier}`,
33101 /* hasTrailingNewLine */ false);
33102 }
33103 const IGNORE_FOR_DIAGNOSTICS_MARKER = `${CommentTriviaType.DIAGNOSTIC}:ignore`;
33104 /**
33105 * Tag the `ts.Node` with an indication that any errors arising from the evaluation of the node
33106 * should be ignored.
33107 */
33108 function markIgnoreDiagnostics(node) {
33109 ts$1.addSyntheticTrailingComment(node, ts$1.SyntaxKind.MultiLineCommentTrivia, IGNORE_FOR_DIAGNOSTICS_MARKER,
33110 /* hasTrailingNewLine */ false);
33111 }
33112 /** Returns true if the node has a marker that indicates diagnostics errors should be ignored. */
33113 function hasIgnoreForDiagnosticsMarker(node, sourceFile) {
33114 return ts$1.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => {
33115 if (kind !== ts$1.SyntaxKind.MultiLineCommentTrivia) {
33116 return null;
33117 }
33118 const commentText = sourceFile.text.substring(pos + 2, end - 2);
33119 return commentText === IGNORE_FOR_DIAGNOSTICS_MARKER;
33120 }) === true;
33121 }
33122 function makeRecursiveVisitor(visitor) {
33123 function recursiveVisitor(node) {
33124 const res = visitor(node);
33125 return res !== null ? res : node.forEachChild(recursiveVisitor);
33126 }
33127 return recursiveVisitor;
33128 }
33129 function getSpanFromOptions(opts) {
33130 let withSpan = null;
33131 if (opts.withSpan !== undefined) {
33132 if (opts.withSpan instanceof AbsoluteSourceSpan) {
33133 withSpan = opts.withSpan;
33134 }
33135 else {
33136 withSpan = { start: opts.withSpan.start.offset, end: opts.withSpan.end.offset };
33137 }
33138 }
33139 return withSpan;
33140 }
33141 /**
33142 * Given a `ts.Node` with finds the first node whose matching the criteria specified
33143 * by the `FindOptions`.
33144 *
33145 * Returns `null` when no `ts.Node` matches the given conditions.
33146 */
33147 function findFirstMatchingNode(tcb, opts) {
33148 var _a;
33149 const withSpan = getSpanFromOptions(opts);
33150 const withExpressionIdentifier = opts.withExpressionIdentifier;
33151 const sf = tcb.getSourceFile();
33152 const visitor = makeRecursiveVisitor(node => {
33153 if (!opts.filter(node)) {
33154 return null;
33155 }
33156 if (withSpan !== null) {
33157 const comment = readSpanComment(node, sf);
33158 if (comment === null || withSpan.start !== comment.start || withSpan.end !== comment.end) {
33159 return null;
33160 }
33161 }
33162 if (withExpressionIdentifier !== undefined &&
33163 !hasExpressionIdentifier(sf, node, withExpressionIdentifier)) {
33164 return null;
33165 }
33166 return node;
33167 });
33168 return (_a = tcb.forEachChild(visitor)) !== null && _a !== void 0 ? _a : null;
33169 }
33170 /**
33171 * Given a `ts.Node` with source span comments, finds the first node whose source span comment
33172 * matches the given `sourceSpan`. Additionally, the `filter` function allows matching only
33173 * `ts.Nodes` of a given type, which provides the ability to select only matches of a given type
33174 * when there may be more than one.
33175 *
33176 * Returns `null` when no `ts.Node` matches the given conditions.
33177 */
33178 function findAllMatchingNodes(tcb, opts) {
33179 const withSpan = getSpanFromOptions(opts);
33180 const withExpressionIdentifier = opts.withExpressionIdentifier;
33181 const results = [];
33182 const stack = [tcb];
33183 const sf = tcb.getSourceFile();
33184 while (stack.length > 0) {
33185 const node = stack.pop();
33186 if (!opts.filter(node)) {
33187 stack.push(...node.getChildren());
33188 continue;
33189 }
33190 if (withSpan !== null) {
33191 const comment = readSpanComment(node, sf);
33192 if (comment === null || withSpan.start !== comment.start || withSpan.end !== comment.end) {
33193 stack.push(...node.getChildren());
33194 continue;
33195 }
33196 }
33197 if (withExpressionIdentifier !== undefined &&
33198 !hasExpressionIdentifier(sf, node, withExpressionIdentifier)) {
33199 continue;
33200 }
33201 results.push(node);
33202 }
33203 return results;
33204 }
33205 function hasExpressionIdentifier(sourceFile, node, identifier) {
33206 return ts$1.forEachTrailingCommentRange(sourceFile.text, node.getEnd(), (pos, end, kind) => {
33207 if (kind !== ts$1.SyntaxKind.MultiLineCommentTrivia) {
33208 return false;
33209 }
33210 const commentText = sourceFile.text.substring(pos + 2, end - 2);
33211 return commentText === `${CommentTriviaType.EXPRESSION_TYPE_IDENTIFIER}:${identifier}`;
33212 }) || false;
33213 }
33214
33215 /**
33216 * @license
33217 * Copyright Google LLC All Rights Reserved.
33218 *
33219 * Use of this source code is governed by an MIT-style license that can be
33220 * found in the LICENSE file at https://angular.io/license
33221 */
33222 /**
33223 * Powers autocompletion for a specific component.
33224 *
33225 * Internally caches autocompletion results, and must be discarded if the component template or
33226 * surrounding TS program have changed.
33227 */
33228 class CompletionEngine {
33229 constructor(tcb, data, shimPath) {
33230 this.tcb = tcb;
33231 this.data = data;
33232 this.shimPath = shimPath;
33233 /**
33234 * Cache of `GlobalCompletion`s for various levels of the template, including the root template
33235 * (`null`).
33236 */
33237 this.globalCompletionCache = new Map();
33238 this.expressionCompletionCache = new Map();
33239 }
33240 /**
33241 * Get global completions within the given template context - either a `TmplAstTemplate` embedded
33242 * view, or `null` for the root template context.
33243 */
33244 getGlobalCompletions(context) {
33245 if (this.globalCompletionCache.has(context)) {
33246 return this.globalCompletionCache.get(context);
33247 }
33248 // Find the component completion expression within the TCB. This looks like: `ctx. /* ... */;`
33249 const globalRead = findFirstMatchingNode(this.tcb, {
33250 filter: ts$1.isPropertyAccessExpression,
33251 withExpressionIdentifier: ExpressionIdentifier.COMPONENT_COMPLETION
33252 });
33253 if (globalRead === null) {
33254 return null;
33255 }
33256 const completion = {
33257 componentContext: {
33258 shimPath: this.shimPath,
33259 // `globalRead.name` is an empty `ts.Identifier`, so its start position immediately follows
33260 // the `.` in `ctx.`. TS autocompletion APIs can then be used to access completion results
33261 // for the component context.
33262 positionInShimFile: globalRead.name.getStart(),
33263 },
33264 templateContext: new Map(),
33265 };
33266 // The bound template already has details about the references and variables in scope in the
33267 // `context` template - they just need to be converted to `Completion`s.
33268 for (const node of this.data.boundTarget.getEntitiesInTemplateScope(context)) {
33269 if (node instanceof Reference) {
33270 completion.templateContext.set(node.name, {
33271 kind: CompletionKind.Reference,
33272 node,
33273 });
33274 }
33275 else {
33276 completion.templateContext.set(node.name, {
33277 kind: CompletionKind.Variable,
33278 node,
33279 });
33280 }
33281 }
33282 this.globalCompletionCache.set(context, completion);
33283 return completion;
33284 }
33285 getExpressionCompletionLocation(expr) {
33286 if (this.expressionCompletionCache.has(expr)) {
33287 return this.expressionCompletionCache.get(expr);
33288 }
33289 // Completion works inside property reads and method calls.
33290 let tsExpr = null;
33291 if (expr instanceof PropertyRead || expr instanceof MethodCall ||
33292 expr instanceof PropertyWrite) {
33293 // Non-safe navigation operations are trivial: `foo.bar` or `foo.bar()`
33294 tsExpr = findFirstMatchingNode(this.tcb, {
33295 filter: ts$1.isPropertyAccessExpression,
33296 withSpan: expr.nameSpan,
33297 });
33298 }
33299 else if (expr instanceof SafePropertyRead || expr instanceof SafeMethodCall) {
33300 // Safe navigation operations are a little more complex, and involve a ternary. Completion
33301 // happens in the "true" case of the ternary.
33302 const ternaryExpr = findFirstMatchingNode(this.tcb, {
33303 filter: ts$1.isParenthesizedExpression,
33304 withSpan: expr.sourceSpan,
33305 });
33306 if (ternaryExpr === null || !ts$1.isConditionalExpression(ternaryExpr.expression)) {
33307 return null;
33308 }
33309 const whenTrue = ternaryExpr.expression.whenTrue;
33310 if (expr instanceof SafePropertyRead && ts$1.isPropertyAccessExpression(whenTrue)) {
33311 tsExpr = whenTrue;
33312 }
33313 else if (expr instanceof SafeMethodCall && ts$1.isCallExpression(whenTrue) &&
33314 ts$1.isPropertyAccessExpression(whenTrue.expression)) {
33315 tsExpr = whenTrue.expression;
33316 }
33317 }
33318 if (tsExpr === null) {
33319 return null;
33320 }
33321 const res = {
33322 shimPath: this.shimPath,
33323 positionInShimFile: tsExpr.name.getEnd(),
33324 };
33325 this.expressionCompletionCache.set(expr, res);
33326 return res;
33327 }
33328 }
33329
33330 /**
33331 * @license
33332 * Copyright Google LLC All Rights Reserved.
33333 *
33334 * Use of this source code is governed by an MIT-style license that can be
33335 * found in the LICENSE file at https://angular.io/license
33336 */
33337 /**
33338 * Constructs a `ts.Diagnostic` for a given `ParseSourceSpan` within a template.
33339 */
33340 function makeTemplateDiagnostic(templateId, mapping, span, category, code, messageText, relatedMessage) {
33341 if (mapping.type === 'direct') {
33342 let relatedInformation = undefined;
33343 if (relatedMessage !== undefined) {
33344 relatedInformation = [{
33345 category: ts$1.DiagnosticCategory.Message,
33346 code: 0,
33347 file: mapping.node.getSourceFile(),
33348 start: relatedMessage.span.start.offset,
33349 length: relatedMessage.span.end.offset - relatedMessage.span.start.offset,
33350 messageText: relatedMessage.text,
33351 }];
33352 }
33353 // For direct mappings, the error is shown inline as ngtsc was able to pinpoint a string
33354 // constant within the `@Component` decorator for the template. This allows us to map the error
33355 // directly into the bytes of the source file.
33356 return {
33357 source: 'ngtsc',
33358 code,
33359 category,
33360 messageText,
33361 file: mapping.node.getSourceFile(),
33362 componentFile: mapping.node.getSourceFile(),
33363 templateId,
33364 start: span.start.offset,
33365 length: span.end.offset - span.start.offset,
33366 relatedInformation,
33367 };
33368 }
33369 else if (mapping.type === 'indirect' || mapping.type === 'external') {
33370 // For indirect mappings (template was declared inline, but ngtsc couldn't map it directly
33371 // to a string constant in the decorator), the component's file name is given with a suffix
33372 // indicating it's not the TS file being displayed, but a template.
33373 // For external temoplates, the HTML filename is used.
33374 const componentSf = mapping.componentClass.getSourceFile();
33375 const componentName = mapping.componentClass.name.text;
33376 // TODO(alxhub): remove cast when TS in g3 supports this narrowing.
33377 const fileName = mapping.type === 'indirect' ?
33378 `${componentSf.fileName} (${componentName} template)` :
33379 mapping.templateUrl;
33380 // TODO(alxhub): investigate creating a fake `ts.SourceFile` here instead of invoking the TS
33381 // parser against the template (HTML is just really syntactically invalid TypeScript code ;).
33382 // Also investigate caching the file to avoid running the parser multiple times.
33383 const sf = ts$1.createSourceFile(fileName, mapping.template, ts$1.ScriptTarget.Latest, false, ts$1.ScriptKind.JSX);
33384 let relatedInformation = [];
33385 if (relatedMessage !== undefined) {
33386 relatedInformation.push({
33387 category: ts$1.DiagnosticCategory.Message,
33388 code: 0,
33389 file: sf,
33390 start: relatedMessage.span.start.offset,
33391 length: relatedMessage.span.end.offset - relatedMessage.span.start.offset,
33392 messageText: relatedMessage.text,
33393 });
33394 }
33395 relatedInformation.push({
33396 category: ts$1.DiagnosticCategory.Message,
33397 code: 0,
33398 file: componentSf,
33399 // mapping.node represents either the 'template' or 'templateUrl' expression. getStart()
33400 // and getEnd() are used because they don't include surrounding whitespace.
33401 start: mapping.node.getStart(),
33402 length: mapping.node.getEnd() - mapping.node.getStart(),
33403 messageText: `Error occurs in the template of component ${componentName}.`,
33404 });
33405 return {
33406 source: 'ngtsc',
33407 category,
33408 code,
33409 messageText,
33410 file: sf,
33411 componentFile: componentSf,
33412 templateId,
33413 start: span.start.offset,
33414 length: span.end.offset - span.start.offset,
33415 // Show a secondary message indicating the component whose template contains the error.
33416 relatedInformation,
33417 };
33418 }
33419 else {
33420 throw new Error(`Unexpected source mapping type: ${mapping.type}`);
33421 }
33422 }
33423
33424 /**
33425 * @license
33426 * Copyright Google LLC All Rights Reserved.
33427 *
33428 * Use of this source code is governed by an MIT-style license that can be
33429 * found in the LICENSE file at https://angular.io/license
33430 */
33431 const TEMPLATE_ID = Symbol('ngTemplateId');
33432 const NEXT_TEMPLATE_ID = Symbol('ngNextTemplateId');
33433 function getTemplateId(clazz) {
33434 const node = clazz;
33435 if (node[TEMPLATE_ID] === undefined) {
33436 node[TEMPLATE_ID] = allocateTemplateId(node.getSourceFile());
33437 }
33438 return node[TEMPLATE_ID];
33439 }
33440 function allocateTemplateId(sf) {
33441 if (sf[NEXT_TEMPLATE_ID] === undefined) {
33442 sf[NEXT_TEMPLATE_ID] = 1;
33443 }
33444 return (`tcb${sf[NEXT_TEMPLATE_ID]++}`);
33445 }
33446
33447 /**
33448 * @license
33449 * Copyright Google LLC All Rights Reserved.
33450 *
33451 * Use of this source code is governed by an MIT-style license that can be
33452 * found in the LICENSE file at https://angular.io/license
33453 */
33454 const REGISTRY = new DomElementSchemaRegistry();
33455 const REMOVE_XHTML_REGEX = /^:xhtml:/;
33456 /**
33457 * Checks non-Angular elements and properties against the `DomElementSchemaRegistry`, a schema
33458 * maintained by the Angular team via extraction from a browser IDL.
33459 */
33460 class RegistryDomSchemaChecker {
33461 constructor(resolver) {
33462 this.resolver = resolver;
33463 this._diagnostics = [];
33464 }
33465 get diagnostics() {
33466 return this._diagnostics;
33467 }
33468 checkElement(id, element, schemas) {
33469 // HTML elements inside an SVG `foreignObject` are declared in the `xhtml` namespace.
33470 // We need to strip it before handing it over to the registry because all HTML tag names
33471 // in the registry are without a namespace.
33472 const name = element.name.replace(REMOVE_XHTML_REGEX, '');
33473 if (!REGISTRY.hasElement(name, schemas)) {
33474 const mapping = this.resolver.getSourceMapping(id);
33475 let errorMsg = `'${name}' is not a known element:\n`;
33476 errorMsg +=
33477 `1. If '${name}' is an Angular component, then verify that it is part of this module.\n`;
33478 if (name.indexOf('-') > -1) {
33479 errorMsg += `2. If '${name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`;
33480 }
33481 else {
33482 errorMsg +=
33483 `2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
33484 }
33485 const diag = makeTemplateDiagnostic(id, mapping, element.startSourceSpan, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.SCHEMA_INVALID_ELEMENT), errorMsg);
33486 this._diagnostics.push(diag);
33487 }
33488 }
33489 checkProperty(id, element, name, span, schemas) {
33490 if (!REGISTRY.hasProperty(element.name, name, schemas)) {
33491 const mapping = this.resolver.getSourceMapping(id);
33492 let errorMsg = `Can't bind to '${name}' since it isn't a known property of '${element.name}'.`;
33493 if (element.name.startsWith('ng-')) {
33494 errorMsg +=
33495 `\n1. If '${name}' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component.` +
33496 `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
33497 }
33498 else if (element.name.indexOf('-') > -1) {
33499 errorMsg +=
33500 `\n1. If '${element.name}' is an Angular component and it has '${name}' input, then verify that it is part of this module.` +
33501 `\n2. If '${element
33502 .name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.` +
33503 `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
33504 }
33505 const diag = makeTemplateDiagnostic(id, mapping, span, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.SCHEMA_INVALID_ATTRIBUTE), errorMsg);
33506 this._diagnostics.push(diag);
33507 }
33508 }
33509 }
33510
33511 /**
33512 * @license
33513 * Copyright Google LLC All Rights Reserved.
33514 *
33515 * Use of this source code is governed by an MIT-style license that can be
33516 * found in the LICENSE file at https://angular.io/license
33517 */
33518 /**
33519 * A `Set` of `ts.SyntaxKind`s of `ts.Expression` which are safe to wrap in a `ts.AsExpression`
33520 * without needing to be wrapped in parentheses.
33521 *
33522 * For example, `foo.bar()` is a `ts.CallExpression`, and can be safely cast to `any` with
33523 * `foo.bar() as any`. however, `foo !== bar` is a `ts.BinaryExpression`, and attempting to cast
33524 * without the parentheses yields the expression `foo !== bar as any`. This is semantically
33525 * equivalent to `foo !== (bar as any)`, which is not what was intended. Thus,
33526 * `ts.BinaryExpression`s need to be wrapped in parentheses before casting.
33527 */
33528 //
33529 const SAFE_TO_CAST_WITHOUT_PARENS = new Set([
33530 // Expressions which are already parenthesized can be cast without further wrapping.
33531 ts$1.SyntaxKind.ParenthesizedExpression,
33532 // Expressions which form a single lexical unit leave no room for precedence issues with the cast.
33533 ts$1.SyntaxKind.Identifier,
33534 ts$1.SyntaxKind.CallExpression,
33535 ts$1.SyntaxKind.NonNullExpression,
33536 ts$1.SyntaxKind.ElementAccessExpression,
33537 ts$1.SyntaxKind.PropertyAccessExpression,
33538 ts$1.SyntaxKind.ArrayLiteralExpression,
33539 ts$1.SyntaxKind.ObjectLiteralExpression,
33540 // The same goes for various literals.
33541 ts$1.SyntaxKind.StringLiteral,
33542 ts$1.SyntaxKind.NumericLiteral,
33543 ts$1.SyntaxKind.TrueKeyword,
33544 ts$1.SyntaxKind.FalseKeyword,
33545 ts$1.SyntaxKind.NullKeyword,
33546 ts$1.SyntaxKind.UndefinedKeyword,
33547 ]);
33548 function tsCastToAny(expr) {
33549 // Wrap `expr` in parentheses if needed (see `SAFE_TO_CAST_WITHOUT_PARENS` above).
33550 if (!SAFE_TO_CAST_WITHOUT_PARENS.has(expr.kind)) {
33551 expr = ts$1.createParen(expr);
33552 }
33553 // The outer expression is always wrapped in parentheses.
33554 return ts$1.createParen(ts$1.createAsExpression(expr, ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword)));
33555 }
33556 /**
33557 * Create an expression which instantiates an element by its HTML tagName.
33558 *
33559 * Thanks to narrowing of `document.createElement()`, this expression will have its type inferred
33560 * based on the tag name, including for custom elements that have appropriate .d.ts definitions.
33561 */
33562 function tsCreateElement(tagName) {
33563 const createElement = ts$1.createPropertyAccess(
33564 /* expression */ ts$1.createIdentifier('document'), 'createElement');
33565 return ts$1.createCall(
33566 /* expression */ createElement,
33567 /* typeArguments */ undefined,
33568 /* argumentsArray */ [ts$1.createLiteral(tagName)]);
33569 }
33570 /**
33571 * Create a `ts.VariableStatement` which declares a variable without explicit initialization.
33572 *
33573 * The initializer `null!` is used to bypass strict variable initialization checks.
33574 *
33575 * Unlike with `tsCreateVariable`, the type of the variable is explicitly specified.
33576 */
33577 function tsDeclareVariable(id, type) {
33578 const decl = ts$1.createVariableDeclaration(
33579 /* name */ id,
33580 /* type */ type,
33581 /* initializer */ ts$1.createNonNullExpression(ts$1.createNull()));
33582 return ts$1.createVariableStatement(
33583 /* modifiers */ undefined,
33584 /* declarationList */ [decl]);
33585 }
33586 /**
33587 * Creates a `ts.TypeQueryNode` for a coerced input.
33588 *
33589 * For example: `typeof MatInput.ngAcceptInputType_value`, where MatInput is `typeName` and `value`
33590 * is the `coercedInputName`.
33591 *
33592 * @param typeName The `EntityName` of the Directive where the static coerced input is defined.
33593 * @param coercedInputName The field name of the coerced input.
33594 */
33595 function tsCreateTypeQueryForCoercedInput(typeName, coercedInputName) {
33596 return ts$1.createTypeQueryNode(ts$1.createQualifiedName(typeName, `ngAcceptInputType_${coercedInputName}`));
33597 }
33598 /**
33599 * Create a `ts.VariableStatement` that initializes a variable with a given expression.
33600 *
33601 * Unlike with `tsDeclareVariable`, the type of the variable is inferred from the initializer
33602 * expression.
33603 */
33604 function tsCreateVariable(id, initializer) {
33605 const decl = ts$1.createVariableDeclaration(
33606 /* name */ id,
33607 /* type */ undefined,
33608 /* initializer */ initializer);
33609 return ts$1.createVariableStatement(
33610 /* modifiers */ undefined,
33611 /* declarationList */ [decl]);
33612 }
33613 /**
33614 * Construct a `ts.CallExpression` that calls a method on a receiver.
33615 */
33616 function tsCallMethod(receiver, methodName, args = []) {
33617 const methodAccess = ts$1.createPropertyAccess(receiver, methodName);
33618 return ts$1.createCall(
33619 /* expression */ methodAccess,
33620 /* typeArguments */ undefined,
33621 /* argumentsArray */ args);
33622 }
33623 function checkIfClassIsExported(node) {
33624 // A class is exported if one of two conditions is met:
33625 // 1) it has the 'export' modifier.
33626 // 2) it's declared at the top level, and there is an export statement for the class.
33627 if (node.modifiers !== undefined &&
33628 node.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.ExportKeyword)) {
33629 // Condition 1 is true, the class has an 'export' keyword attached.
33630 return true;
33631 }
33632 else if (node.parent !== undefined && ts$1.isSourceFile(node.parent) &&
33633 checkIfFileHasExport(node.parent, node.name.text)) {
33634 // Condition 2 is true, the class is exported via an 'export {}' statement.
33635 return true;
33636 }
33637 return false;
33638 }
33639 function checkIfFileHasExport(sf, name) {
33640 for (const stmt of sf.statements) {
33641 if (ts$1.isExportDeclaration(stmt) && stmt.exportClause !== undefined &&
33642 ts$1.isNamedExports(stmt.exportClause)) {
33643 for (const element of stmt.exportClause.elements) {
33644 if (element.propertyName === undefined && element.name.text === name) {
33645 // The named declaration is directly exported.
33646 return true;
33647 }
33648 else if (element.propertyName !== undefined && element.propertyName.text == name) {
33649 // The named declaration is exported via an alias.
33650 return true;
33651 }
33652 }
33653 }
33654 }
33655 return false;
33656 }
33657 function checkIfGenericTypesAreUnbound(node) {
33658 if (node.typeParameters === undefined) {
33659 return true;
33660 }
33661 return node.typeParameters.every(param => param.constraint === undefined);
33662 }
33663 function isAccessExpression(node) {
33664 return ts$1.isPropertyAccessExpression(node) || ts$1.isElementAccessExpression(node);
33665 }
33666
33667 /**
33668 * @license
33669 * Copyright Google LLC All Rights Reserved.
33670 *
33671 * Use of this source code is governed by an MIT-style license that can be
33672 * found in the LICENSE file at https://angular.io/license
33673 */
33674 /**
33675 * Determines whether the provided type can be emitted, which means that it can be safely emitted
33676 * into a different location.
33677 *
33678 * If this function returns true, a `TypeEmitter` should be able to succeed. Vice versa, if this
33679 * function returns false, then using the `TypeEmitter` should not be attempted as it is known to
33680 * fail.
33681 */
33682 function canEmitType(type, resolver) {
33683 return canEmitTypeWorker(type);
33684 function canEmitTypeWorker(type) {
33685 return visitTypeNode(type, {
33686 visitTypeReferenceNode: type => canEmitTypeReference(type),
33687 visitArrayTypeNode: type => canEmitTypeWorker(type.elementType),
33688 visitKeywordType: () => true,
33689 visitLiteralType: () => true,
33690 visitOtherType: () => false,
33691 });
33692 }
33693 function canEmitTypeReference(type) {
33694 const reference = resolver(type);
33695 // If the type could not be resolved, it can not be emitted.
33696 if (reference === null) {
33697 return false;
33698 }
33699 // If the type is a reference without a owning module, consider the type not to be eligible for
33700 // emitting.
33701 if (reference instanceof Reference$1 && !reference.hasOwningModuleGuess) {
33702 return false;
33703 }
33704 // The type can be emitted if either it does not have any type arguments, or all of them can be
33705 // emitted.
33706 return type.typeArguments === undefined || type.typeArguments.every(canEmitTypeWorker);
33707 }
33708 }
33709 /**
33710 * Given a `ts.TypeNode`, this class derives an equivalent `ts.TypeNode` that has been emitted into
33711 * a different context.
33712 *
33713 * For example, consider the following code:
33714 *
33715 * ```
33716 * import {NgIterable} from '@angular/core';
33717 *
33718 * class NgForOf<T, U extends NgIterable<T>> {}
33719 * ```
33720 *
33721 * Here, the generic type parameters `T` and `U` can be emitted into a different context, as the
33722 * type reference to `NgIterable` originates from an absolute module import so that it can be
33723 * emitted anywhere, using that same module import. The process of emitting translates the
33724 * `NgIterable` type reference to a type reference that is valid in the context in which it is
33725 * emitted, for example:
33726 *
33727 * ```
33728 * import * as i0 from '@angular/core';
33729 * import * as i1 from '@angular/common';
33730 *
33731 * const _ctor1: <T, U extends i0.NgIterable<T>>(o: Pick<i1.NgForOf<T, U>, 'ngForOf'>):
33732 * i1.NgForOf<T, U>;
33733 * ```
33734 *
33735 * Notice how the type reference for `NgIterable` has been translated into a qualified name,
33736 * referring to the namespace import that was created.
33737 */
33738 class TypeEmitter {
33739 constructor(resolver, emitReference) {
33740 this.resolver = resolver;
33741 this.emitReference = emitReference;
33742 }
33743 emitType(type) {
33744 return visitTypeNode(type, {
33745 visitTypeReferenceNode: type => this.emitTypeReference(type),
33746 visitArrayTypeNode: type => ts$1.updateArrayTypeNode(type, this.emitType(type.elementType)),
33747 visitKeywordType: type => type,
33748 visitLiteralType: type => type,
33749 visitOtherType: () => {
33750 throw new Error('Unable to emit a complex type');
33751 },
33752 });
33753 }
33754 emitTypeReference(type) {
33755 // Determine the reference that the type corresponds with.
33756 const reference = this.resolver(type);
33757 if (reference === null) {
33758 throw new Error('Unable to emit an unresolved reference');
33759 }
33760 // Emit the type arguments, if any.
33761 let typeArguments = undefined;
33762 if (type.typeArguments !== undefined) {
33763 typeArguments = ts$1.createNodeArray(type.typeArguments.map(typeArg => this.emitType(typeArg)));
33764 }
33765 // Emit the type name.
33766 let typeName = type.typeName;
33767 if (reference instanceof Reference$1) {
33768 if (!reference.hasOwningModuleGuess) {
33769 throw new Error('A type reference to emit must be imported from an absolute module');
33770 }
33771 const emittedType = this.emitReference(reference);
33772 if (!ts$1.isTypeReferenceNode(emittedType)) {
33773 throw new Error(`Expected TypeReferenceNode for emitted reference, got ${ts$1.SyntaxKind[emittedType.kind]}`);
33774 }
33775 typeName = emittedType.typeName;
33776 }
33777 return ts$1.updateTypeReferenceNode(type, typeName, typeArguments);
33778 }
33779 }
33780 function visitTypeNode(type, visitor) {
33781 if (ts$1.isTypeReferenceNode(type)) {
33782 return visitor.visitTypeReferenceNode(type);
33783 }
33784 else if (ts$1.isArrayTypeNode(type)) {
33785 return visitor.visitArrayTypeNode(type);
33786 }
33787 else if (ts$1.isLiteralTypeNode(type)) {
33788 return visitor.visitLiteralType(type);
33789 }
33790 switch (type.kind) {
33791 case ts$1.SyntaxKind.AnyKeyword:
33792 case ts$1.SyntaxKind.UnknownKeyword:
33793 case ts$1.SyntaxKind.NumberKeyword:
33794 case ts$1.SyntaxKind.ObjectKeyword:
33795 case ts$1.SyntaxKind.BooleanKeyword:
33796 case ts$1.SyntaxKind.StringKeyword:
33797 case ts$1.SyntaxKind.UndefinedKeyword:
33798 case ts$1.SyntaxKind.NullKeyword:
33799 return visitor.visitKeywordType(type);
33800 default:
33801 return visitor.visitOtherType(type);
33802 }
33803 }
33804
33805 /**
33806 * @license
33807 * Copyright Google LLC All Rights Reserved.
33808 *
33809 * Use of this source code is governed by an MIT-style license that can be
33810 * found in the LICENSE file at https://angular.io/license
33811 */
33812 /**
33813 * See `TypeEmitter` for more information on the emitting process.
33814 */
33815 class TypeParameterEmitter {
33816 constructor(typeParameters, reflector) {
33817 this.typeParameters = typeParameters;
33818 this.reflector = reflector;
33819 }
33820 /**
33821 * Determines whether the type parameters can be emitted. If this returns true, then a call to
33822 * `emit` is known to succeed. Vice versa, if false is returned then `emit` should not be
33823 * called, as it would fail.
33824 */
33825 canEmit() {
33826 if (this.typeParameters === undefined) {
33827 return true;
33828 }
33829 return this.typeParameters.every(typeParam => {
33830 if (typeParam.constraint === undefined) {
33831 return true;
33832 }
33833 return canEmitType(typeParam.constraint, type => this.resolveTypeReference(type));
33834 });
33835 }
33836 /**
33837 * Emits the type parameters using the provided emitter function for `Reference`s.
33838 */
33839 emit(emitReference) {
33840 if (this.typeParameters === undefined) {
33841 return undefined;
33842 }
33843 const emitter = new TypeEmitter(type => this.resolveTypeReference(type), emitReference);
33844 return this.typeParameters.map(typeParam => {
33845 const constraint = typeParam.constraint !== undefined ? emitter.emitType(typeParam.constraint) : undefined;
33846 return ts$1.updateTypeParameterDeclaration(
33847 /* node */ typeParam,
33848 /* name */ typeParam.name,
33849 /* constraint */ constraint,
33850 /* defaultType */ typeParam.default);
33851 });
33852 }
33853 resolveTypeReference(type) {
33854 const target = ts$1.isIdentifier(type.typeName) ? type.typeName : type.typeName.right;
33855 const declaration = this.reflector.getDeclarationOfIdentifier(target);
33856 // If no declaration could be resolved or does not have a `ts.Declaration`, the type cannot be
33857 // resolved.
33858 if (declaration === null || declaration.node === null) {
33859 return null;
33860 }
33861 // If the declaration corresponds with a local type parameter, the type reference can be used
33862 // as is.
33863 if (this.isLocalTypeParameter(declaration.node)) {
33864 return type;
33865 }
33866 let owningModule = null;
33867 if (declaration.viaModule !== null) {
33868 owningModule = {
33869 specifier: declaration.viaModule,
33870 resolutionContext: type.getSourceFile().fileName,
33871 };
33872 }
33873 return new Reference$1(declaration.node, owningModule);
33874 }
33875 isLocalTypeParameter(decl) {
33876 // Checking for local type parameters only occurs during resolution of type parameters, so it is
33877 // guaranteed that type parameters are present.
33878 return this.typeParameters.some(param => param === decl);
33879 }
33880 }
33881
33882 /**
33883 * @license
33884 * Copyright Google LLC All Rights Reserved.
33885 *
33886 * Use of this source code is governed by an MIT-style license that can be
33887 * found in the LICENSE file at https://angular.io/license
33888 */
33889 function generateTypeCtorDeclarationFn(node, meta, nodeTypeRef, typeParams, reflector) {
33890 if (requiresInlineTypeCtor(node, reflector)) {
33891 throw new Error(`${node.name.text} requires an inline type constructor`);
33892 }
33893 const rawTypeArgs = typeParams !== undefined ? generateGenericArgs(typeParams) : undefined;
33894 const rawType = ts$1.createTypeReferenceNode(nodeTypeRef, rawTypeArgs);
33895 const initParam = constructTypeCtorParameter(node, meta, rawType);
33896 const typeParameters = typeParametersWithDefaultTypes(typeParams);
33897 if (meta.body) {
33898 const fnType = ts$1.createFunctionTypeNode(
33899 /* typeParameters */ typeParameters,
33900 /* parameters */ [initParam],
33901 /* type */ rawType);
33902 const decl = ts$1.createVariableDeclaration(
33903 /* name */ meta.fnName,
33904 /* type */ fnType,
33905 /* body */ ts$1.createNonNullExpression(ts$1.createNull()));
33906 const declList = ts$1.createVariableDeclarationList([decl], ts$1.NodeFlags.Const);
33907 return ts$1.createVariableStatement(
33908 /* modifiers */ undefined,
33909 /* declarationList */ declList);
33910 }
33911 else {
33912 return ts$1.createFunctionDeclaration(
33913 /* decorators */ undefined,
33914 /* modifiers */ [ts$1.createModifier(ts$1.SyntaxKind.DeclareKeyword)],
33915 /* asteriskToken */ undefined,
33916 /* name */ meta.fnName,
33917 /* typeParameters */ typeParameters,
33918 /* parameters */ [initParam],
33919 /* type */ rawType,
33920 /* body */ undefined);
33921 }
33922 }
33923 /**
33924 * Generate an inline type constructor for the given class and metadata.
33925 *
33926 * An inline type constructor is a specially shaped TypeScript static method, intended to be placed
33927 * within a directive class itself, that permits type inference of any generic type parameters of
33928 * the class from the types of expressions bound to inputs or outputs, and the types of elements
33929 * that match queries performed by the directive. It also catches any errors in the types of these
33930 * expressions. This method is never called at runtime, but is used in type-check blocks to
33931 * construct directive types.
33932 *
33933 * An inline type constructor for NgFor looks like:
33934 *
33935 * static ngTypeCtor<T>(init: Pick<NgForOf<T>, 'ngForOf'|'ngForTrackBy'|'ngForTemplate'>):
33936 * NgForOf<T>;
33937 *
33938 * A typical constructor would be:
33939 *
33940 * NgForOf.ngTypeCtor(init: {
33941 * ngForOf: ['foo', 'bar'],
33942 * ngForTrackBy: null as any,
33943 * ngForTemplate: null as any,
33944 * }); // Infers a type of NgForOf<string>.
33945 *
33946 * Any inputs declared on the type for which no property binding is present are assigned a value of
33947 * type `any`, to avoid producing any type errors for unset inputs.
33948 *
33949 * Inline type constructors are used when the type being created has bounded generic types which
33950 * make writing a declared type constructor (via `generateTypeCtorDeclarationFn`) difficult or
33951 * impossible.
33952 *
33953 * @param node the `ClassDeclaration<ts.ClassDeclaration>` for which a type constructor will be
33954 * generated.
33955 * @param meta additional metadata required to generate the type constructor.
33956 * @returns a `ts.MethodDeclaration` for the type constructor.
33957 */
33958 function generateInlineTypeCtor(node, meta) {
33959 // Build rawType, a `ts.TypeNode` of the class with its generic parameters passed through from
33960 // the definition without any type bounds. For example, if the class is
33961 // `FooDirective<T extends Bar>`, its rawType would be `FooDirective<T>`.
33962 const rawTypeArgs = node.typeParameters !== undefined ? generateGenericArgs(node.typeParameters) : undefined;
33963 const rawType = ts$1.createTypeReferenceNode(node.name, rawTypeArgs);
33964 const initParam = constructTypeCtorParameter(node, meta, rawType);
33965 // If this constructor is being generated into a .ts file, then it needs a fake body. The body
33966 // is set to a return of `null!`. If the type constructor is being generated into a .d.ts file,
33967 // it needs no body.
33968 let body = undefined;
33969 if (meta.body) {
33970 body = ts$1.createBlock([
33971 ts$1.createReturn(ts$1.createNonNullExpression(ts$1.createNull())),
33972 ]);
33973 }
33974 // Create the type constructor method declaration.
33975 return ts$1.createMethod(
33976 /* decorators */ undefined,
33977 /* modifiers */ [ts$1.createModifier(ts$1.SyntaxKind.StaticKeyword)],
33978 /* asteriskToken */ undefined,
33979 /* name */ meta.fnName,
33980 /* questionToken */ undefined,
33981 /* typeParameters */ typeParametersWithDefaultTypes(node.typeParameters),
33982 /* parameters */ [initParam],
33983 /* type */ rawType,
33984 /* body */ body);
33985 }
33986 function constructTypeCtorParameter(node, meta, rawType) {
33987 // initType is the type of 'init', the single argument to the type constructor method.
33988 // If the Directive has any inputs, its initType will be:
33989 //
33990 // Pick<rawType, 'inputA'|'inputB'>
33991 //
33992 // Pick here is used to select only those fields from which the generic type parameters of the
33993 // directive will be inferred.
33994 //
33995 // In the special case there are no inputs, initType is set to {}.
33996 let initType = null;
33997 const keys = meta.fields.inputs;
33998 const plainKeys = [];
33999 const coercedKeys = [];
34000 for (const key of keys) {
34001 if (!meta.coercedInputFields.has(key)) {
34002 plainKeys.push(ts$1.createLiteralTypeNode(ts$1.createStringLiteral(key)));
34003 }
34004 else {
34005 coercedKeys.push(ts$1.createPropertySignature(
34006 /* modifiers */ undefined,
34007 /* name */ key,
34008 /* questionToken */ undefined,
34009 /* type */ tsCreateTypeQueryForCoercedInput(rawType.typeName, key),
34010 /* initializer */ undefined));
34011 }
34012 }
34013 if (plainKeys.length > 0) {
34014 // Construct a union of all the field names.
34015 const keyTypeUnion = ts$1.createUnionTypeNode(plainKeys);
34016 // Construct the Pick<rawType, keyTypeUnion>.
34017 initType = ts$1.createTypeReferenceNode('Pick', [rawType, keyTypeUnion]);
34018 }
34019 if (coercedKeys.length > 0) {
34020 const coercedLiteral = ts$1.createTypeLiteralNode(coercedKeys);
34021 initType = initType !== null ? ts$1.createIntersectionTypeNode([initType, coercedLiteral]) :
34022 coercedLiteral;
34023 }
34024 if (initType === null) {
34025 // Special case - no inputs, outputs, or other fields which could influence the result type.
34026 initType = ts$1.createTypeLiteralNode([]);
34027 }
34028 // Create the 'init' parameter itself.
34029 return ts$1.createParameter(
34030 /* decorators */ undefined,
34031 /* modifiers */ undefined,
34032 /* dotDotDotToken */ undefined,
34033 /* name */ 'init',
34034 /* questionToken */ undefined,
34035 /* type */ initType,
34036 /* initializer */ undefined);
34037 }
34038 function generateGenericArgs(params) {
34039 return params.map(param => ts$1.createTypeReferenceNode(param.name, undefined));
34040 }
34041 function requiresInlineTypeCtor(node, host) {
34042 // The class requires an inline type constructor if it has generic type bounds that can not be
34043 // emitted into a different context.
34044 return !checkIfGenericTypeBoundsAreContextFree(node, host);
34045 }
34046 function checkIfGenericTypeBoundsAreContextFree(node, reflector) {
34047 // Generic type parameters are considered context free if they can be emitted into any context.
34048 return new TypeParameterEmitter(node.typeParameters, reflector).canEmit();
34049 }
34050 /**
34051 * Add a default `= any` to type parameters that don't have a default value already.
34052 *
34053 * TypeScript uses the default type of a type parameter whenever inference of that parameter fails.
34054 * This can happen when inferring a complex type from 'any'. For example, if `NgFor`'s inference is
34055 * done with the TCB code:
34056 *
34057 * ```
34058 * class NgFor<T> {
34059 * ngForOf: T[];
34060 * }
34061 *
34062 * declare function ctor<T>(o: Pick<NgFor<T>, 'ngForOf'|'ngForTrackBy'|'ngForTemplate'>): NgFor<T>;
34063 * ```
34064 *
34065 * An invocation looks like:
34066 *
34067 * ```
34068 * var _t1 = ctor({ngForOf: [1, 2], ngForTrackBy: null as any, ngForTemplate: null as any});
34069 * ```
34070 *
34071 * This correctly infers the type `NgFor<number>` for `_t1`, since `T` is inferred from the
34072 * assignment of type `number[]` to `ngForOf`'s type `T[]`. However, if `any` is passed instead:
34073 *
34074 * ```
34075 * var _t2 = ctor({ngForOf: [1, 2] as any, ngForTrackBy: null as any, ngForTemplate: null as any});
34076 * ```
34077 *
34078 * then inference for `T` fails (it cannot be inferred from `T[] = any`). In this case, `T` takes
34079 * the type `{}`, and so `_t2` is inferred as `NgFor<{}>`. This is obviously wrong.
34080 *
34081 * Adding a default type to the generic declaration in the constructor solves this problem, as the
34082 * default type will be used in the event that inference fails.
34083 *
34084 * ```
34085 * declare function ctor<T = any>(o: Pick<NgFor<T>, 'ngForOf'>): NgFor<T>;
34086 *
34087 * var _t3 = ctor({ngForOf: [1, 2] as any});
34088 * ```
34089 *
34090 * This correctly infers `T` as `any`, and therefore `_t3` as `NgFor<any>`.
34091 */
34092 function typeParametersWithDefaultTypes(params) {
34093 if (params === undefined) {
34094 return undefined;
34095 }
34096 return params.map(param => {
34097 if (param.default === undefined) {
34098 return ts$1.updateTypeParameterDeclaration(
34099 /* node */ param,
34100 /* name */ param.name,
34101 /* constraint */ param.constraint,
34102 /* defaultType */ ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
34103 }
34104 else {
34105 return param;
34106 }
34107 });
34108 }
34109
34110 /**
34111 * @license
34112 * Copyright Google LLC All Rights Reserved.
34113 *
34114 * Use of this source code is governed by an MIT-style license that can be
34115 * found in the LICENSE file at https://angular.io/license
34116 */
34117 /**
34118 * A context which hosts one or more Type Check Blocks (TCBs).
34119 *
34120 * An `Environment` supports the generation of TCBs by tracking necessary imports, declarations of
34121 * type constructors, and other statements beyond the type-checking code within the TCB itself.
34122 * Through method calls on `Environment`, the TCB generator can request `ts.Expression`s which
34123 * reference declarations in the `Environment` for these artifacts`.
34124 *
34125 * `Environment` can be used in a standalone fashion, or can be extended to support more specialized
34126 * usage.
34127 */
34128 class Environment {
34129 constructor(config, importManager, refEmitter, reflector, contextFile) {
34130 this.config = config;
34131 this.importManager = importManager;
34132 this.refEmitter = refEmitter;
34133 this.reflector = reflector;
34134 this.contextFile = contextFile;
34135 this.nextIds = {
34136 pipeInst: 1,
34137 typeCtor: 1,
34138 };
34139 this.typeCtors = new Map();
34140 this.typeCtorStatements = [];
34141 this.pipeInsts = new Map();
34142 this.pipeInstStatements = [];
34143 }
34144 /**
34145 * Get an expression referring to a type constructor for the given directive.
34146 *
34147 * Depending on the shape of the directive itself, this could be either a reference to a declared
34148 * type constructor, or to an inline type constructor.
34149 */
34150 typeCtorFor(dir) {
34151 const dirRef = dir.ref;
34152 const node = dirRef.node;
34153 if (this.typeCtors.has(node)) {
34154 return this.typeCtors.get(node);
34155 }
34156 if (requiresInlineTypeCtor(node, this.reflector)) {
34157 // The constructor has already been created inline, we just need to construct a reference to
34158 // it.
34159 const ref = this.reference(dirRef);
34160 const typeCtorExpr = ts$1.createPropertyAccess(ref, 'ngTypeCtor');
34161 this.typeCtors.set(node, typeCtorExpr);
34162 return typeCtorExpr;
34163 }
34164 else {
34165 const fnName = `_ctor${this.nextIds.typeCtor++}`;
34166 const nodeTypeRef = this.referenceType(dirRef);
34167 if (!ts$1.isTypeReferenceNode(nodeTypeRef)) {
34168 throw new Error(`Expected TypeReferenceNode from reference to ${dirRef.debugName}`);
34169 }
34170 const meta = {
34171 fnName,
34172 body: true,
34173 fields: {
34174 inputs: dir.inputs.classPropertyNames,
34175 outputs: dir.outputs.classPropertyNames,
34176 // TODO: support queries
34177 queries: dir.queries,
34178 },
34179 coercedInputFields: dir.coercedInputFields,
34180 };
34181 const typeParams = this.emitTypeParameters(node);
34182 const typeCtor = generateTypeCtorDeclarationFn(node, meta, nodeTypeRef.typeName, typeParams, this.reflector);
34183 this.typeCtorStatements.push(typeCtor);
34184 const fnId = ts$1.createIdentifier(fnName);
34185 this.typeCtors.set(node, fnId);
34186 return fnId;
34187 }
34188 }
34189 /*
34190 * Get an expression referring to an instance of the given pipe.
34191 */
34192 pipeInst(ref) {
34193 if (this.pipeInsts.has(ref.node)) {
34194 return this.pipeInsts.get(ref.node);
34195 }
34196 const pipeType = this.referenceType(ref);
34197 const pipeInstId = ts$1.createIdentifier(`_pipe${this.nextIds.pipeInst++}`);
34198 this.pipeInstStatements.push(tsDeclareVariable(pipeInstId, pipeType));
34199 this.pipeInsts.set(ref.node, pipeInstId);
34200 return pipeInstId;
34201 }
34202 /**
34203 * Generate a `ts.Expression` that references the given node.
34204 *
34205 * This may involve importing the node into the file if it's not declared there already.
34206 */
34207 reference(ref) {
34208 // Disable aliasing for imports generated in a template type-checking context, as there is no
34209 // guarantee that any alias re-exports exist in the .d.ts files. It's safe to use direct imports
34210 // in these cases as there is no strict dependency checking during the template type-checking
34211 // pass.
34212 const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing);
34213 // Use `translateExpression` to convert the `Expression` into a `ts.Expression`.
34214 return translateExpression(ngExpr, this.importManager);
34215 }
34216 /**
34217 * Generate a `ts.TypeNode` that references the given node as a type.
34218 *
34219 * This may involve importing the node into the file if it's not declared there already.
34220 */
34221 referenceType(ref) {
34222 const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing | ImportFlags.AllowTypeImports);
34223 // Create an `ExpressionType` from the `Expression` and translate it via `translateType`.
34224 // TODO(alxhub): support references to types with generic arguments in a clean way.
34225 return translateType(new ExpressionType(ngExpr), this.importManager);
34226 }
34227 emitTypeParameters(declaration) {
34228 const emitter = new TypeParameterEmitter(declaration.typeParameters, this.reflector);
34229 return emitter.emit(ref => this.referenceType(ref));
34230 }
34231 /**
34232 * Generate a `ts.TypeNode` that references a given type from the provided module.
34233 *
34234 * This will involve importing the type into the file, and will also add type parameters if
34235 * provided.
34236 */
34237 referenceExternalType(moduleName, name, typeParams) {
34238 const external = new ExternalExpr({ moduleName, name });
34239 return translateType(new ExpressionType(external, [ /* modifiers */], typeParams), this.importManager);
34240 }
34241 getPreludeStatements() {
34242 return [
34243 ...this.pipeInstStatements,
34244 ...this.typeCtorStatements,
34245 ];
34246 }
34247 }
34248
34249 /**
34250 * @license
34251 * Copyright Google LLC All Rights Reserved.
34252 *
34253 * Use of this source code is governed by an MIT-style license that can be
34254 * found in the LICENSE file at https://angular.io/license
34255 */
34256 class OutOfBandDiagnosticRecorderImpl {
34257 constructor(resolver) {
34258 this.resolver = resolver;
34259 this._diagnostics = [];
34260 /**
34261 * Tracks which `BindingPipe` nodes have already been recorded as invalid, so only one diagnostic
34262 * is ever produced per node.
34263 */
34264 this.recordedPipes = new Set();
34265 }
34266 get diagnostics() {
34267 return this._diagnostics;
34268 }
34269 missingReferenceTarget(templateId, ref) {
34270 const mapping = this.resolver.getSourceMapping(templateId);
34271 const value = ref.value.trim();
34272 const errorMsg = `No directive found with exportAs '${value}'.`;
34273 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, ref.valueSpan || ref.sourceSpan, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.MISSING_REFERENCE_TARGET), errorMsg));
34274 }
34275 missingPipe(templateId, ast) {
34276 if (this.recordedPipes.has(ast)) {
34277 return;
34278 }
34279 const mapping = this.resolver.getSourceMapping(templateId);
34280 const errorMsg = `No pipe found with name '${ast.name}'.`;
34281 const sourceSpan = this.resolver.toParseSourceSpan(templateId, ast.nameSpan);
34282 if (sourceSpan === null) {
34283 throw new Error(`Assertion failure: no SourceLocation found for usage of pipe '${ast.name}'.`);
34284 }
34285 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, sourceSpan, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.MISSING_PIPE), errorMsg));
34286 this.recordedPipes.add(ast);
34287 }
34288 illegalAssignmentToTemplateVar(templateId, assignment, target) {
34289 const mapping = this.resolver.getSourceMapping(templateId);
34290 const errorMsg = `Cannot use variable '${assignment
34291 .name}' as the left-hand side of an assignment expression. Template variables are read-only.`;
34292 const sourceSpan = this.resolver.toParseSourceSpan(templateId, assignment.sourceSpan);
34293 if (sourceSpan === null) {
34294 throw new Error(`Assertion failure: no SourceLocation found for property binding.`);
34295 }
34296 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, sourceSpan, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.WRITE_TO_READ_ONLY_VARIABLE), errorMsg, {
34297 text: `The variable ${assignment.name} is declared here.`,
34298 span: target.valueSpan || target.sourceSpan,
34299 }));
34300 }
34301 duplicateTemplateVar(templateId, variable, firstDecl) {
34302 const mapping = this.resolver.getSourceMapping(templateId);
34303 const errorMsg = `Cannot redeclare variable '${variable.name}' as it was previously declared elsewhere for the same template.`;
34304 // The allocation of the error here is pretty useless for variables declared in microsyntax,
34305 // since the sourceSpan refers to the entire microsyntax property, not a span for the specific
34306 // variable in question.
34307 //
34308 // TODO(alxhub): allocate to a tighter span once one is available.
34309 this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, variable.sourceSpan, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.DUPLICATE_VARIABLE_DECLARATION), errorMsg, {
34310 text: `The variable '${firstDecl.name}' was first declared here.`,
34311 span: firstDecl.sourceSpan,
34312 }));
34313 }
34314 requiresInlineTcb(templateId, node) {
34315 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.`));
34316 }
34317 requiresInlineTypeConstructors(templateId, node, directives) {
34318 let message;
34319 if (directives.length > 1) {
34320 message =
34321 `This component uses directives which require inline type constructors, which are not supported by the current environment.`;
34322 }
34323 else {
34324 message =
34325 `This component uses a directive which requires an inline type constructor, which is not supported by the current environment.`;
34326 }
34327 this._diagnostics.push(makeInlineDiagnostic(templateId, ErrorCode.INLINE_TYPE_CTOR_REQUIRED, node.name, message, directives.map(dir => makeRelatedInformation(dir.name, `Requires an inline type constructor.`))));
34328 }
34329 }
34330 function makeInlineDiagnostic(templateId, code, node, messageText, relatedInformation) {
34331 return Object.assign(Object.assign({}, makeDiagnostic(code, node, messageText, relatedInformation)), { componentFile: node.getSourceFile(), templateId });
34332 }
34333
34334 /**
34335 * @license
34336 * Copyright Google LLC All Rights Reserved.
34337 *
34338 * Use of this source code is governed by an MIT-style license that can be
34339 * found in the LICENSE file at https://angular.io/license
34340 */
34341 function requiresInlineTypeCheckBlock(node, usedPipes) {
34342 // In order to qualify for a declared TCB (not inline) two conditions must be met:
34343 // 1) the class must be exported
34344 // 2) it must not have constrained generic types
34345 if (!checkIfClassIsExported(node)) {
34346 // Condition 1 is false, the class is not exported.
34347 return true;
34348 }
34349 else if (!checkIfGenericTypesAreUnbound(node)) {
34350 // Condition 2 is false, the class has constrained generic types
34351 return true;
34352 }
34353 else if (Array.from(usedPipes.values())
34354 .some(pipeRef => !checkIfClassIsExported(pipeRef.node))) {
34355 // If one of the pipes used by the component is not exported, a non-inline TCB will not be able
34356 // to import it, so this requires an inline TCB.
34357 return true;
34358 }
34359 else {
34360 return false;
34361 }
34362 }
34363 /** Maps a shim position back to a template location. */
34364 function getTemplateMapping(shimSf, position, resolver, isDiagnosticRequest) {
34365 const node = getTokenAtPosition(shimSf, position);
34366 const sourceLocation = findSourceLocation(node, shimSf, isDiagnosticRequest);
34367 if (sourceLocation === null) {
34368 return null;
34369 }
34370 const mapping = resolver.getSourceMapping(sourceLocation.id);
34371 const span = resolver.toParseSourceSpan(sourceLocation.id, sourceLocation.span);
34372 if (span === null) {
34373 return null;
34374 }
34375 // TODO(atscott): Consider adding a context span by walking up from `node` until we get a
34376 // different span.
34377 return { sourceLocation, templateSourceMapping: mapping, span };
34378 }
34379 function findTypeCheckBlock(file, id, isDiagnosticRequest) {
34380 for (const stmt of file.statements) {
34381 if (ts$1.isFunctionDeclaration(stmt) && getTemplateId$1(stmt, file, isDiagnosticRequest) === id) {
34382 return stmt;
34383 }
34384 }
34385 return null;
34386 }
34387 /**
34388 * Traverses up the AST starting from the given node to extract the source location from comments
34389 * that have been emitted into the TCB. If the node does not exist within a TCB, or if an ignore
34390 * marker comment is found up the tree (and this is part of a diagnostic request), this function
34391 * returns null.
34392 */
34393 function findSourceLocation(node, sourceFile, isDiagnosticsRequest) {
34394 // Search for comments until the TCB's function declaration is encountered.
34395 while (node !== undefined && !ts$1.isFunctionDeclaration(node)) {
34396 if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticsRequest) {
34397 // There's an ignore marker on this node, so the diagnostic should not be reported.
34398 return null;
34399 }
34400 const span = readSpanComment(node, sourceFile);
34401 if (span !== null) {
34402 // Once the positional information has been extracted, search further up the TCB to extract
34403 // the unique id that is attached with the TCB's function declaration.
34404 const id = getTemplateId$1(node, sourceFile, isDiagnosticsRequest);
34405 if (id === null) {
34406 return null;
34407 }
34408 return { id, span };
34409 }
34410 node = node.parent;
34411 }
34412 return null;
34413 }
34414 function getTemplateId$1(node, sourceFile, isDiagnosticRequest) {
34415 // Walk up to the function declaration of the TCB, the file information is attached there.
34416 while (!ts$1.isFunctionDeclaration(node)) {
34417 if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticRequest) {
34418 // There's an ignore marker on this node, so the diagnostic should not be reported.
34419 return null;
34420 }
34421 node = node.parent;
34422 // Bail once we have reached the root.
34423 if (node === undefined) {
34424 return null;
34425 }
34426 }
34427 const start = node.getFullStart();
34428 return ts$1.forEachLeadingCommentRange(sourceFile.text, start, (pos, end, kind) => {
34429 if (kind !== ts$1.SyntaxKind.MultiLineCommentTrivia) {
34430 return null;
34431 }
34432 const commentText = sourceFile.text.substring(pos + 2, end - 2);
34433 return commentText;
34434 }) || null;
34435 }
34436
34437 /**
34438 * @license
34439 * Copyright Google LLC All Rights Reserved.
34440 *
34441 * Use of this source code is governed by an MIT-style license that can be
34442 * found in the LICENSE file at https://angular.io/license
34443 */
34444 /**
34445 * Wraps the node in parenthesis such that inserted span comments become attached to the proper
34446 * node. This is an alias for `ts.createParen` with the benefit that it signifies that the
34447 * inserted parenthesis are for diagnostic purposes, not for correctness of the rendered TCB code.
34448 *
34449 * Note that it is important that nodes and its attached comment are not wrapped into parenthesis
34450 * by default, as it prevents correct translation of e.g. diagnostics produced for incorrect method
34451 * arguments. Such diagnostics would then be produced for the parenthesised node whereas the
34452 * positional comment would be located within that node, resulting in a mismatch.
34453 */
34454 function wrapForDiagnostics(expr) {
34455 return ts$1.createParen(expr);
34456 }
34457 /**
34458 * Wraps the node in parenthesis such that inserted span comments become attached to the proper
34459 * node. This is an alias for `ts.createParen` with the benefit that it signifies that the
34460 * inserted parenthesis are for use by the type checker, not for correctness of the rendered TCB
34461 * code.
34462 */
34463 function wrapForTypeChecker(expr) {
34464 return ts$1.createParen(expr);
34465 }
34466 /**
34467 * Adds a synthetic comment to the expression that represents the parse span of the provided node.
34468 * This comment can later be retrieved as trivia of a node to recover original source locations.
34469 */
34470 function addParseSpanInfo(node, span) {
34471 let commentText;
34472 if (span instanceof AbsoluteSourceSpan) {
34473 commentText = `${span.start},${span.end}`;
34474 }
34475 else {
34476 commentText = `${span.start.offset},${span.end.offset}`;
34477 }
34478 ts$1.addSyntheticTrailingComment(node, ts$1.SyntaxKind.MultiLineCommentTrivia, commentText, /* hasTrailingNewLine */ false);
34479 }
34480 /**
34481 * Adds a synthetic comment to the function declaration that contains the template id
34482 * of the class declaration.
34483 */
34484 function addTemplateId(tcb, id) {
34485 ts$1.addSyntheticLeadingComment(tcb, ts$1.SyntaxKind.MultiLineCommentTrivia, id, true);
34486 }
34487 /**
34488 * Determines if the diagnostic should be reported. Some diagnostics are produced because of the
34489 * way TCBs are generated; those diagnostics should not be reported as type check errors of the
34490 * template.
34491 */
34492 function shouldReportDiagnostic(diagnostic) {
34493 const { code } = diagnostic;
34494 if (code === 6133 /* $var is declared but its value is never read. */) {
34495 return false;
34496 }
34497 else if (code === 6199 /* All variables are unused. */) {
34498 return false;
34499 }
34500 else if (code === 2695 /* Left side of comma operator is unused and has no side effects. */) {
34501 return false;
34502 }
34503 else if (code === 7006 /* Parameter '$event' implicitly has an 'any' type. */) {
34504 return false;
34505 }
34506 return true;
34507 }
34508 /**
34509 * Attempts to translate a TypeScript diagnostic produced during template type-checking to their
34510 * location of origin, based on the comments that are emitted in the TCB code.
34511 *
34512 * If the diagnostic could not be translated, `null` is returned to indicate that the diagnostic
34513 * should not be reported at all. This prevents diagnostics from non-TCB code in a user's source
34514 * file from being reported as type-check errors.
34515 */
34516 function translateDiagnostic(diagnostic, resolver) {
34517 if (diagnostic.file === undefined || diagnostic.start === undefined) {
34518 return null;
34519 }
34520 const fullMapping = getTemplateMapping(diagnostic.file, diagnostic.start, resolver, /*isDiagnosticsRequest*/ true);
34521 if (fullMapping === null) {
34522 return null;
34523 }
34524 const { sourceLocation, templateSourceMapping, span } = fullMapping;
34525 return makeTemplateDiagnostic(sourceLocation.id, templateSourceMapping, span, diagnostic.category, diagnostic.code, diagnostic.messageText);
34526 }
34527
34528 /**
34529 * @license
34530 * Copyright Google LLC All Rights Reserved.
34531 *
34532 * Use of this source code is governed by an MIT-style license that can be
34533 * found in the LICENSE file at https://angular.io/license
34534 */
34535 const NULL_AS_ANY = ts$1.createAsExpression(ts$1.createNull(), ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
34536 const UNDEFINED = ts$1.createIdentifier('undefined');
34537 const UNARY_OPS = new Map([
34538 ['+', ts$1.SyntaxKind.PlusToken],
34539 ['-', ts$1.SyntaxKind.MinusToken],
34540 ]);
34541 const BINARY_OPS = new Map([
34542 ['+', ts$1.SyntaxKind.PlusToken],
34543 ['-', ts$1.SyntaxKind.MinusToken],
34544 ['<', ts$1.SyntaxKind.LessThanToken],
34545 ['>', ts$1.SyntaxKind.GreaterThanToken],
34546 ['<=', ts$1.SyntaxKind.LessThanEqualsToken],
34547 ['>=', ts$1.SyntaxKind.GreaterThanEqualsToken],
34548 ['==', ts$1.SyntaxKind.EqualsEqualsToken],
34549 ['===', ts$1.SyntaxKind.EqualsEqualsEqualsToken],
34550 ['*', ts$1.SyntaxKind.AsteriskToken],
34551 ['/', ts$1.SyntaxKind.SlashToken],
34552 ['%', ts$1.SyntaxKind.PercentToken],
34553 ['!=', ts$1.SyntaxKind.ExclamationEqualsToken],
34554 ['!==', ts$1.SyntaxKind.ExclamationEqualsEqualsToken],
34555 ['||', ts$1.SyntaxKind.BarBarToken],
34556 ['&&', ts$1.SyntaxKind.AmpersandAmpersandToken],
34557 ['&', ts$1.SyntaxKind.AmpersandToken],
34558 ['|', ts$1.SyntaxKind.BarToken],
34559 ]);
34560 /**
34561 * Convert an `AST` to TypeScript code directly, without going through an intermediate `Expression`
34562 * AST.
34563 */
34564 function astToTypescript(ast, maybeResolve, config) {
34565 const translator = new AstTranslator(maybeResolve, config);
34566 return translator.translate(ast);
34567 }
34568 class AstTranslator {
34569 constructor(maybeResolve, config) {
34570 this.maybeResolve = maybeResolve;
34571 this.config = config;
34572 }
34573 translate(ast) {
34574 // Skip over an `ASTWithSource` as its `visit` method calls directly into its ast's `visit`,
34575 // which would prevent any custom resolution through `maybeResolve` for that node.
34576 if (ast instanceof ASTWithSource) {
34577 ast = ast.ast;
34578 }
34579 // The `EmptyExpr` doesn't have a dedicated method on `AstVisitor`, so it's special cased here.
34580 if (ast instanceof EmptyExpr) {
34581 return UNDEFINED;
34582 }
34583 // First attempt to let any custom resolution logic provide a translation for the given node.
34584 const resolved = this.maybeResolve(ast);
34585 if (resolved !== null) {
34586 return resolved;
34587 }
34588 return ast.visit(this);
34589 }
34590 visitUnary(ast) {
34591 const expr = this.translate(ast.expr);
34592 const op = UNARY_OPS.get(ast.operator);
34593 if (op === undefined) {
34594 throw new Error(`Unsupported Unary.operator: ${ast.operator}`);
34595 }
34596 const node = wrapForDiagnostics(ts$1.createPrefix(op, expr));
34597 addParseSpanInfo(node, ast.sourceSpan);
34598 return node;
34599 }
34600 visitBinary(ast) {
34601 const lhs = wrapForDiagnostics(this.translate(ast.left));
34602 const rhs = wrapForDiagnostics(this.translate(ast.right));
34603 const op = BINARY_OPS.get(ast.operation);
34604 if (op === undefined) {
34605 throw new Error(`Unsupported Binary.operation: ${ast.operation}`);
34606 }
34607 const node = ts$1.createBinary(lhs, op, rhs);
34608 addParseSpanInfo(node, ast.sourceSpan);
34609 return node;
34610 }
34611 visitChain(ast) {
34612 const elements = ast.expressions.map(expr => this.translate(expr));
34613 const node = wrapForDiagnostics(ts$1.createCommaList(elements));
34614 addParseSpanInfo(node, ast.sourceSpan);
34615 return node;
34616 }
34617 visitConditional(ast) {
34618 const condExpr = this.translate(ast.condition);
34619 const trueExpr = this.translate(ast.trueExp);
34620 // Wrap `falseExpr` in parens so that the trailing parse span info is not attributed to the
34621 // whole conditional.
34622 // In the following example, the last source span comment (5,6) could be seen as the
34623 // trailing comment for _either_ the whole conditional expression _or_ just the `falseExpr` that
34624 // is immediately before it:
34625 // `conditional /*1,2*/ ? trueExpr /*3,4*/ : falseExpr /*5,6*/`
34626 // This should be instead be `conditional /*1,2*/ ? trueExpr /*3,4*/ : (falseExpr /*5,6*/)`
34627 const falseExpr = wrapForTypeChecker(this.translate(ast.falseExp));
34628 const node = ts$1.createParen(ts$1.createConditional(condExpr, trueExpr, falseExpr));
34629 addParseSpanInfo(node, ast.sourceSpan);
34630 return node;
34631 }
34632 visitFunctionCall(ast) {
34633 const receiver = wrapForDiagnostics(this.translate(ast.target));
34634 const args = ast.args.map(expr => this.translate(expr));
34635 const node = ts$1.createCall(receiver, undefined, args);
34636 addParseSpanInfo(node, ast.sourceSpan);
34637 return node;
34638 }
34639 visitImplicitReceiver(ast) {
34640 throw new Error('Method not implemented.');
34641 }
34642 visitThisReceiver(ast) {
34643 throw new Error('Method not implemented.');
34644 }
34645 visitInterpolation(ast) {
34646 // Build up a chain of binary + operations to simulate the string concatenation of the
34647 // interpolation's expressions. The chain is started using an actual string literal to ensure
34648 // the type is inferred as 'string'.
34649 return ast.expressions.reduce((lhs, ast) => ts$1.createBinary(lhs, ts$1.SyntaxKind.PlusToken, wrapForTypeChecker(this.translate(ast))), ts$1.createLiteral(''));
34650 }
34651 visitKeyedRead(ast) {
34652 const receiver = wrapForDiagnostics(this.translate(ast.obj));
34653 const key = this.translate(ast.key);
34654 const node = ts$1.createElementAccess(receiver, key);
34655 addParseSpanInfo(node, ast.sourceSpan);
34656 return node;
34657 }
34658 visitKeyedWrite(ast) {
34659 const receiver = wrapForDiagnostics(this.translate(ast.obj));
34660 const left = ts$1.createElementAccess(receiver, this.translate(ast.key));
34661 // TODO(joost): annotate `left` with the span of the element access, which is not currently
34662 // available on `ast`.
34663 const right = wrapForTypeChecker(this.translate(ast.value));
34664 const node = wrapForDiagnostics(ts$1.createBinary(left, ts$1.SyntaxKind.EqualsToken, right));
34665 addParseSpanInfo(node, ast.sourceSpan);
34666 return node;
34667 }
34668 visitLiteralArray(ast) {
34669 const elements = ast.expressions.map(expr => this.translate(expr));
34670 const literal = ts$1.createArrayLiteral(elements);
34671 // If strictLiteralTypes is disabled, array literals are cast to `any`.
34672 const node = this.config.strictLiteralTypes ? literal : tsCastToAny(literal);
34673 addParseSpanInfo(node, ast.sourceSpan);
34674 return node;
34675 }
34676 visitLiteralMap(ast) {
34677 const properties = ast.keys.map(({ key }, idx) => {
34678 const value = this.translate(ast.values[idx]);
34679 return ts$1.createPropertyAssignment(ts$1.createStringLiteral(key), value);
34680 });
34681 const literal = ts$1.createObjectLiteral(properties, true);
34682 // If strictLiteralTypes is disabled, object literals are cast to `any`.
34683 const node = this.config.strictLiteralTypes ? literal : tsCastToAny(literal);
34684 addParseSpanInfo(node, ast.sourceSpan);
34685 return node;
34686 }
34687 visitLiteralPrimitive(ast) {
34688 let node;
34689 if (ast.value === undefined) {
34690 node = ts$1.createIdentifier('undefined');
34691 }
34692 else if (ast.value === null) {
34693 node = ts$1.createNull();
34694 }
34695 else {
34696 node = ts$1.createLiteral(ast.value);
34697 }
34698 addParseSpanInfo(node, ast.sourceSpan);
34699 return node;
34700 }
34701 visitMethodCall(ast) {
34702 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
34703 const method = ts$1.createPropertyAccess(receiver, ast.name);
34704 addParseSpanInfo(method, ast.nameSpan);
34705 const args = ast.args.map(expr => this.translate(expr));
34706 const node = ts$1.createCall(method, undefined, args);
34707 addParseSpanInfo(node, ast.sourceSpan);
34708 return node;
34709 }
34710 visitNonNullAssert(ast) {
34711 const expr = wrapForDiagnostics(this.translate(ast.expression));
34712 const node = ts$1.createNonNullExpression(expr);
34713 addParseSpanInfo(node, ast.sourceSpan);
34714 return node;
34715 }
34716 visitPipe(ast) {
34717 throw new Error('Method not implemented.');
34718 }
34719 visitPrefixNot(ast) {
34720 const expression = wrapForDiagnostics(this.translate(ast.expression));
34721 const node = ts$1.createLogicalNot(expression);
34722 addParseSpanInfo(node, ast.sourceSpan);
34723 return node;
34724 }
34725 visitPropertyRead(ast) {
34726 // This is a normal property read - convert the receiver to an expression and emit the correct
34727 // TypeScript expression to read the property.
34728 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
34729 const name = ts$1.createPropertyAccess(receiver, ast.name);
34730 addParseSpanInfo(name, ast.nameSpan);
34731 const node = wrapForDiagnostics(name);
34732 addParseSpanInfo(node, ast.sourceSpan);
34733 return node;
34734 }
34735 visitPropertyWrite(ast) {
34736 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
34737 const left = ts$1.createPropertyAccess(receiver, ast.name);
34738 addParseSpanInfo(left, ast.nameSpan);
34739 // TypeScript reports assignment errors on the entire lvalue expression. Annotate the lvalue of
34740 // the assignment with the sourceSpan, which includes receivers, rather than nameSpan for
34741 // consistency of the diagnostic location.
34742 // a.b.c = 1
34743 // ^^^^^^^^^ sourceSpan
34744 // ^ nameSpan
34745 const leftWithPath = wrapForDiagnostics(left);
34746 addParseSpanInfo(leftWithPath, ast.sourceSpan);
34747 // The right needs to be wrapped in parens as well or we cannot accurately match its
34748 // span to just the RHS. For example, the span in `e = $event /*0,10*/` is ambiguous.
34749 // It could refer to either the whole binary expression or just the RHS.
34750 // We should instead generate `e = ($event /*0,10*/)` so we know the span 0,10 matches RHS.
34751 const right = wrapForTypeChecker(this.translate(ast.value));
34752 const node = wrapForDiagnostics(ts$1.createBinary(leftWithPath, ts$1.SyntaxKind.EqualsToken, right));
34753 addParseSpanInfo(node, ast.sourceSpan);
34754 return node;
34755 }
34756 visitQuote(ast) {
34757 return NULL_AS_ANY;
34758 }
34759 visitSafeMethodCall(ast) {
34760 // See the comments in SafePropertyRead above for an explanation of the cases here.
34761 let node;
34762 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
34763 const args = ast.args.map(expr => this.translate(expr));
34764 if (this.config.strictSafeNavigationTypes) {
34765 // "a?.method(...)" becomes (null as any ? a!.method(...) : undefined)
34766 const method = ts$1.createPropertyAccess(ts$1.createNonNullExpression(receiver), ast.name);
34767 addParseSpanInfo(method, ast.nameSpan);
34768 const call = ts$1.createCall(method, undefined, args);
34769 node = ts$1.createParen(ts$1.createConditional(NULL_AS_ANY, call, UNDEFINED));
34770 }
34771 else if (VeSafeLhsInferenceBugDetector.veWillInferAnyFor(ast)) {
34772 // "a?.method(...)" becomes (a as any).method(...)
34773 const method = ts$1.createPropertyAccess(tsCastToAny(receiver), ast.name);
34774 addParseSpanInfo(method, ast.nameSpan);
34775 node = ts$1.createCall(method, undefined, args);
34776 }
34777 else {
34778 // "a?.method(...)" becomes (a!.method(...) as any)
34779 const method = ts$1.createPropertyAccess(ts$1.createNonNullExpression(receiver), ast.name);
34780 addParseSpanInfo(method, ast.nameSpan);
34781 node = tsCastToAny(ts$1.createCall(method, undefined, args));
34782 }
34783 addParseSpanInfo(node, ast.sourceSpan);
34784 return node;
34785 }
34786 visitSafePropertyRead(ast) {
34787 let node;
34788 const receiver = wrapForDiagnostics(this.translate(ast.receiver));
34789 // The form of safe property reads depends on whether strictness is in use.
34790 if (this.config.strictSafeNavigationTypes) {
34791 // Basically, the return here is either the type of the complete expression with a null-safe
34792 // property read, or `undefined`. So a ternary is used to create an "or" type:
34793 // "a?.b" becomes (null as any ? a!.b : undefined)
34794 // The type of this expression is (typeof a!.b) | undefined, which is exactly as desired.
34795 const expr = ts$1.createPropertyAccess(ts$1.createNonNullExpression(receiver), ast.name);
34796 addParseSpanInfo(expr, ast.nameSpan);
34797 node = ts$1.createParen(ts$1.createConditional(NULL_AS_ANY, expr, UNDEFINED));
34798 }
34799 else if (VeSafeLhsInferenceBugDetector.veWillInferAnyFor(ast)) {
34800 // Emulate a View Engine bug where 'any' is inferred for the left-hand side of the safe
34801 // navigation operation. With this bug, the type of the left-hand side is regarded as any.
34802 // Therefore, the left-hand side only needs repeating in the output (to validate it), and then
34803 // 'any' is used for the rest of the expression. This is done using a comma operator:
34804 // "a?.b" becomes (a as any).b, which will of course have type 'any'.
34805 node = ts$1.createPropertyAccess(tsCastToAny(receiver), ast.name);
34806 }
34807 else {
34808 // The View Engine bug isn't active, so check the entire type of the expression, but the final
34809 // result is still inferred as `any`.
34810 // "a?.b" becomes (a!.b as any)
34811 const expr = ts$1.createPropertyAccess(ts$1.createNonNullExpression(receiver), ast.name);
34812 addParseSpanInfo(expr, ast.nameSpan);
34813 node = tsCastToAny(expr);
34814 }
34815 addParseSpanInfo(node, ast.sourceSpan);
34816 return node;
34817 }
34818 }
34819 /**
34820 * Checks whether View Engine will infer a type of 'any' for the left-hand side of a safe navigation
34821 * operation.
34822 *
34823 * In View Engine's template type-checker, certain receivers of safe navigation operations will
34824 * cause a temporary variable to be allocated as part of the checking expression, to save the value
34825 * of the receiver and use it more than once in the expression. This temporary variable has type
34826 * 'any'. In practice, this means certain receivers cause View Engine to not check the full
34827 * expression, and other receivers will receive more complete checking.
34828 *
34829 * For compatibility, this logic is adapted from View Engine's expression_converter.ts so that the
34830 * Ivy checker can emulate this bug when needed.
34831 */
34832 class VeSafeLhsInferenceBugDetector {
34833 static veWillInferAnyFor(ast) {
34834 return ast.receiver.visit(VeSafeLhsInferenceBugDetector.SINGLETON);
34835 }
34836 visitUnary(ast) {
34837 return ast.expr.visit(this);
34838 }
34839 visitBinary(ast) {
34840 return ast.left.visit(this) || ast.right.visit(this);
34841 }
34842 visitChain(ast) {
34843 return false;
34844 }
34845 visitConditional(ast) {
34846 return ast.condition.visit(this) || ast.trueExp.visit(this) || ast.falseExp.visit(this);
34847 }
34848 visitFunctionCall(ast) {
34849 return true;
34850 }
34851 visitImplicitReceiver(ast) {
34852 return false;
34853 }
34854 visitThisReceiver(ast) {
34855 return false;
34856 }
34857 visitInterpolation(ast) {
34858 return ast.expressions.some(exp => exp.visit(this));
34859 }
34860 visitKeyedRead(ast) {
34861 return false;
34862 }
34863 visitKeyedWrite(ast) {
34864 return false;
34865 }
34866 visitLiteralArray(ast) {
34867 return true;
34868 }
34869 visitLiteralMap(ast) {
34870 return true;
34871 }
34872 visitLiteralPrimitive(ast) {
34873 return false;
34874 }
34875 visitMethodCall(ast) {
34876 return true;
34877 }
34878 visitPipe(ast) {
34879 return true;
34880 }
34881 visitPrefixNot(ast) {
34882 return ast.expression.visit(this);
34883 }
34884 visitNonNullAssert(ast) {
34885 return ast.expression.visit(this);
34886 }
34887 visitPropertyRead(ast) {
34888 return false;
34889 }
34890 visitPropertyWrite(ast) {
34891 return false;
34892 }
34893 visitQuote(ast) {
34894 return false;
34895 }
34896 visitSafeMethodCall(ast) {
34897 return true;
34898 }
34899 visitSafePropertyRead(ast) {
34900 return false;
34901 }
34902 }
34903 VeSafeLhsInferenceBugDetector.SINGLETON = new VeSafeLhsInferenceBugDetector();
34904
34905 /**
34906 * @license
34907 * Copyright Google LLC All Rights Reserved.
34908 *
34909 * Use of this source code is governed by an MIT-style license that can be
34910 * found in the LICENSE file at https://angular.io/license
34911 */
34912 /**
34913 * Visits a template and records any semantic errors within its expressions.
34914 */
34915 class ExpressionSemanticVisitor extends RecursiveAstVisitor {
34916 constructor(templateId, boundTarget, oob) {
34917 super();
34918 this.templateId = templateId;
34919 this.boundTarget = boundTarget;
34920 this.oob = oob;
34921 }
34922 visitPropertyWrite(ast, context) {
34923 super.visitPropertyWrite(ast, context);
34924 if (!(ast.receiver instanceof ImplicitReceiver)) {
34925 return;
34926 }
34927 const target = this.boundTarget.getExpressionTarget(ast);
34928 if (target instanceof Variable) {
34929 // Template variables are read-only.
34930 this.oob.illegalAssignmentToTemplateVar(this.templateId, ast, target);
34931 }
34932 }
34933 static visit(ast, id, boundTarget, oob) {
34934 ast.visit(new ExpressionSemanticVisitor(id, boundTarget, oob));
34935 }
34936 }
34937
34938 /**
34939 * @license
34940 * Copyright Google LLC All Rights Reserved.
34941 *
34942 * Use of this source code is governed by an MIT-style license that can be
34943 * found in the LICENSE file at https://angular.io/license
34944 */
34945 /**
34946 * Given a `ts.ClassDeclaration` for a component, and metadata regarding that component, compose a
34947 * "type check block" function.
34948 *
34949 * When passed through TypeScript's TypeChecker, type errors that arise within the type check block
34950 * function indicate issues in the template itself.
34951 *
34952 * As a side effect of generating a TCB for the component, `ts.Diagnostic`s may also be produced
34953 * directly for issues within the template which are identified during generation. These issues are
34954 * recorded in either the `domSchemaChecker` (which checks usage of DOM elements and bindings) as
34955 * well as the `oobRecorder` (which records errors when the type-checking code generator is unable
34956 * to sufficiently understand a template).
34957 *
34958 * @param env an `Environment` into which type-checking code will be generated.
34959 * @param ref a `Reference` to the component class which should be type-checked.
34960 * @param name a `ts.Identifier` to use for the generated `ts.FunctionDeclaration`.
34961 * @param meta metadata about the component's template and the function being generated.
34962 * @param domSchemaChecker used to check and record errors regarding improper usage of DOM elements
34963 * and bindings.
34964 * @param oobRecorder used to record errors regarding template elements which could not be correctly
34965 * translated into types during TCB generation.
34966 */
34967 function generateTypeCheckBlock(env, ref, name, meta, domSchemaChecker, oobRecorder) {
34968 const tcb = new Context$1(env, domSchemaChecker, oobRecorder, meta.id, meta.boundTarget, meta.pipes, meta.schemas);
34969 const scope = Scope$1.forNodes(tcb, null, tcb.boundTarget.target.template, /* guard */ null);
34970 const ctxRawType = env.referenceType(ref);
34971 if (!ts$1.isTypeReferenceNode(ctxRawType)) {
34972 throw new Error(`Expected TypeReferenceNode when referencing the ctx param for ${ref.debugName}`);
34973 }
34974 const paramList = [tcbCtxParam(ref.node, ctxRawType.typeName, env.config.useContextGenericType)];
34975 const scopeStatements = scope.render();
34976 const innerBody = ts$1.createBlock([
34977 ...env.getPreludeStatements(),
34978 ...scopeStatements,
34979 ]);
34980 // Wrap the body in an "if (true)" expression. This is unnecessary but has the effect of causing
34981 // the `ts.Printer` to format the type-check block nicely.
34982 const body = ts$1.createBlock([ts$1.createIf(ts$1.createTrue(), innerBody, undefined)]);
34983 const fnDecl = ts$1.createFunctionDeclaration(
34984 /* decorators */ undefined,
34985 /* modifiers */ undefined,
34986 /* asteriskToken */ undefined,
34987 /* name */ name,
34988 /* typeParameters */ env.config.useContextGenericType ? ref.node.typeParameters : undefined,
34989 /* parameters */ paramList,
34990 /* type */ undefined,
34991 /* body */ body);
34992 addTemplateId(fnDecl, meta.id);
34993 return fnDecl;
34994 }
34995 /**
34996 * A code generation operation that's involved in the construction of a Type Check Block.
34997 *
34998 * The generation of a TCB is non-linear. Bindings within a template may result in the need to
34999 * construct certain types earlier than they otherwise would be constructed. That is, if the
35000 * generation of a TCB for a template is broken down into specific operations (constructing a
35001 * directive, extracting a variable from a let- operation, etc), then it's possible for operations
35002 * earlier in the sequence to depend on operations which occur later in the sequence.
35003 *
35004 * `TcbOp` abstracts the different types of operations which are required to convert a template into
35005 * a TCB. This allows for two phases of processing for the template, where 1) a linear sequence of
35006 * `TcbOp`s is generated, and then 2) these operations are executed, not necessarily in linear
35007 * order.
35008 *
35009 * Each `TcbOp` may insert statements into the body of the TCB, and also optionally return a
35010 * `ts.Expression` which can be used to reference the operation's result.
35011 */
35012 class TcbOp {
35013 /**
35014 * Replacement value or operation used while this `TcbOp` is executing (i.e. to resolve circular
35015 * references during its execution).
35016 *
35017 * This is usually a `null!` expression (which asks TS to infer an appropriate type), but another
35018 * `TcbOp` can be returned in cases where additional code generation is necessary to deal with
35019 * circular references.
35020 */
35021 circularFallback() {
35022 return INFER_TYPE_FOR_CIRCULAR_OP_EXPR;
35023 }
35024 }
35025 /**
35026 * A `TcbOp` which creates an expression for a native DOM element (or web component) from a
35027 * `TmplAstElement`.
35028 *
35029 * Executing this operation returns a reference to the element variable.
35030 */
35031 class TcbElementOp extends TcbOp {
35032 constructor(tcb, scope, element) {
35033 super();
35034 this.tcb = tcb;
35035 this.scope = scope;
35036 this.element = element;
35037 }
35038 get optional() {
35039 // The statement generated by this operation is only used for type-inference of the DOM
35040 // element's type and won't report diagnostics by itself, so the operation is marked as optional
35041 // to avoid generating statements for DOM elements that are never referenced.
35042 return true;
35043 }
35044 execute() {
35045 const id = this.tcb.allocateId();
35046 // Add the declaration of the element using document.createElement.
35047 const initializer = tsCreateElement(this.element.name);
35048 addParseSpanInfo(initializer, this.element.startSourceSpan || this.element.sourceSpan);
35049 this.scope.addStatement(tsCreateVariable(id, initializer));
35050 return id;
35051 }
35052 }
35053 /**
35054 * A `TcbOp` which creates an expression for particular let- `TmplAstVariable` on a
35055 * `TmplAstTemplate`'s context.
35056 *
35057 * Executing this operation returns a reference to the variable variable (lol).
35058 */
35059 class TcbVariableOp extends TcbOp {
35060 constructor(tcb, scope, template, variable) {
35061 super();
35062 this.tcb = tcb;
35063 this.scope = scope;
35064 this.template = template;
35065 this.variable = variable;
35066 }
35067 get optional() {
35068 return false;
35069 }
35070 execute() {
35071 // Look for a context variable for the template.
35072 const ctx = this.scope.resolve(this.template);
35073 // Allocate an identifier for the TmplAstVariable, and initialize it to a read of the variable
35074 // on the template context.
35075 const id = this.tcb.allocateId();
35076 const initializer = ts$1.createPropertyAccess(
35077 /* expression */ ctx,
35078 /* name */ this.variable.value || '$implicit');
35079 addParseSpanInfo(id, this.variable.keySpan);
35080 // Declare the variable, and return its identifier.
35081 let variable;
35082 if (this.variable.valueSpan !== undefined) {
35083 addParseSpanInfo(initializer, this.variable.valueSpan);
35084 variable = tsCreateVariable(id, wrapForTypeChecker(initializer));
35085 }
35086 else {
35087 variable = tsCreateVariable(id, initializer);
35088 }
35089 addParseSpanInfo(variable.declarationList.declarations[0], this.variable.sourceSpan);
35090 this.scope.addStatement(variable);
35091 return id;
35092 }
35093 }
35094 /**
35095 * A `TcbOp` which generates a variable for a `TmplAstTemplate`'s context.
35096 *
35097 * Executing this operation returns a reference to the template's context variable.
35098 */
35099 class TcbTemplateContextOp extends TcbOp {
35100 constructor(tcb, scope) {
35101 super();
35102 this.tcb = tcb;
35103 this.scope = scope;
35104 // The declaration of the context variable is only needed when the context is actually referenced.
35105 this.optional = true;
35106 }
35107 execute() {
35108 // Allocate a template ctx variable and declare it with an 'any' type. The type of this variable
35109 // may be narrowed as a result of template guard conditions.
35110 const ctx = this.tcb.allocateId();
35111 const type = ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword);
35112 this.scope.addStatement(tsDeclareVariable(ctx, type));
35113 return ctx;
35114 }
35115 }
35116 /**
35117 * A `TcbOp` which descends into a `TmplAstTemplate`'s children and generates type-checking code for
35118 * them.
35119 *
35120 * This operation wraps the children's type-checking code in an `if` block, which may include one
35121 * or more type guard conditions that narrow types within the template body.
35122 */
35123 class TcbTemplateBodyOp extends TcbOp {
35124 constructor(tcb, scope, template) {
35125 super();
35126 this.tcb = tcb;
35127 this.scope = scope;
35128 this.template = template;
35129 }
35130 get optional() {
35131 return false;
35132 }
35133 execute() {
35134 // An `if` will be constructed, within which the template's children will be type checked. The
35135 // `if` is used for two reasons: it creates a new syntactic scope, isolating variables declared
35136 // in the template's TCB from the outer context, and it allows any directives on the templates
35137 // to perform type narrowing of either expressions or the template's context.
35138 //
35139 // The guard is the `if` block's condition. It's usually set to `true` but directives that exist
35140 // on the template can trigger extra guard expressions that serve to narrow types within the
35141 // `if`. `guard` is calculated by starting with `true` and adding other conditions as needed.
35142 // Collect these into `guards` by processing the directives.
35143 const directiveGuards = [];
35144 const directives = this.tcb.boundTarget.getDirectivesOfNode(this.template);
35145 if (directives !== null) {
35146 for (const dir of directives) {
35147 const dirInstId = this.scope.resolve(this.template, dir);
35148 const dirId = this.tcb.env.reference(dir.ref);
35149 // There are two kinds of guards. Template guards (ngTemplateGuards) allow type narrowing of
35150 // the expression passed to an @Input of the directive. Scan the directive to see if it has
35151 // any template guards, and generate them if needed.
35152 dir.ngTemplateGuards.forEach(guard => {
35153 // For each template guard function on the directive, look for a binding to that input.
35154 const boundInput = this.template.inputs.find(i => i.name === guard.inputName) ||
35155 this.template.templateAttrs.find((i) => i instanceof BoundAttribute && i.name === guard.inputName);
35156 if (boundInput !== undefined) {
35157 // If there is such a binding, generate an expression for it.
35158 const expr = tcbExpression(boundInput.value, this.tcb, this.scope);
35159 // The expression has already been checked in the type constructor invocation, so
35160 // it should be ignored when used within a template guard.
35161 markIgnoreDiagnostics(expr);
35162 if (guard.type === 'binding') {
35163 // Use the binding expression itself as guard.
35164 directiveGuards.push(expr);
35165 }
35166 else {
35167 // Call the guard function on the directive with the directive instance and that
35168 // expression.
35169 const guardInvoke = tsCallMethod(dirId, `ngTemplateGuard_${guard.inputName}`, [
35170 dirInstId,
35171 expr,
35172 ]);
35173 addParseSpanInfo(guardInvoke, boundInput.value.sourceSpan);
35174 directiveGuards.push(guardInvoke);
35175 }
35176 }
35177 });
35178 // The second kind of guard is a template context guard. This guard narrows the template
35179 // rendering context variable `ctx`.
35180 if (dir.hasNgTemplateContextGuard && this.tcb.env.config.applyTemplateContextGuards) {
35181 const ctx = this.scope.resolve(this.template);
35182 const guardInvoke = tsCallMethod(dirId, 'ngTemplateContextGuard', [dirInstId, ctx]);
35183 addParseSpanInfo(guardInvoke, this.template.sourceSpan);
35184 directiveGuards.push(guardInvoke);
35185 }
35186 }
35187 }
35188 // By default the guard is simply `true`.
35189 let guard = null;
35190 // If there are any guards from directives, use them instead.
35191 if (directiveGuards.length > 0) {
35192 // Pop the first value and use it as the initializer to reduce(). This way, a single guard
35193 // will be used on its own, but two or more will be combined into binary AND expressions.
35194 guard = directiveGuards.reduce((expr, dirGuard) => ts$1.createBinary(expr, ts$1.SyntaxKind.AmpersandAmpersandToken, dirGuard), directiveGuards.pop());
35195 }
35196 // Create a new Scope for the template. This constructs the list of operations for the template
35197 // children, as well as tracks bindings within the template.
35198 const tmplScope = Scope$1.forNodes(this.tcb, this.scope, this.template, guard);
35199 // Render the template's `Scope` into its statements.
35200 const statements = tmplScope.render();
35201 if (statements.length === 0) {
35202 // As an optimization, don't generate the scope's block if it has no statements. This is
35203 // beneficial for templates that contain for example `<span *ngIf="first"></span>`, in which
35204 // case there's no need to render the `NgIf` guard expression. This seems like a minor
35205 // improvement, however it reduces the number of flow-node antecedents that TypeScript needs
35206 // to keep into account for such cases, resulting in an overall reduction of
35207 // type-checking time.
35208 return null;
35209 }
35210 let tmplBlock = ts$1.createBlock(statements);
35211 if (guard !== null) {
35212 // The scope has a guard that needs to be applied, so wrap the template block into an `if`
35213 // statement containing the guard expression.
35214 tmplBlock = ts$1.createIf(/* expression */ guard, /* thenStatement */ tmplBlock);
35215 }
35216 this.scope.addStatement(tmplBlock);
35217 return null;
35218 }
35219 }
35220 /**
35221 * A `TcbOp` which renders a text binding (interpolation) into the TCB.
35222 *
35223 * Executing this operation returns nothing.
35224 */
35225 class TcbTextInterpolationOp extends TcbOp {
35226 constructor(tcb, scope, binding) {
35227 super();
35228 this.tcb = tcb;
35229 this.scope = scope;
35230 this.binding = binding;
35231 }
35232 get optional() {
35233 return false;
35234 }
35235 execute() {
35236 const expr = tcbExpression(this.binding.value, this.tcb, this.scope);
35237 this.scope.addStatement(ts$1.createExpressionStatement(expr));
35238 return null;
35239 }
35240 }
35241 /**
35242 * A `TcbOp` which constructs an instance of a directive _without_ setting any of its inputs. Inputs
35243 * are later set in the `TcbDirectiveInputsOp`. Type checking was found to be faster when done in
35244 * this way as opposed to `TcbDirectiveCtorOp` which is only necessary when the directive is
35245 * generic.
35246 *
35247 * Executing this operation returns a reference to the directive instance variable with its inferred
35248 * type.
35249 */
35250 class TcbDirectiveTypeOp extends TcbOp {
35251 constructor(tcb, scope, node, dir) {
35252 super();
35253 this.tcb = tcb;
35254 this.scope = scope;
35255 this.node = node;
35256 this.dir = dir;
35257 }
35258 get optional() {
35259 // The statement generated by this operation is only used to declare the directive's type and
35260 // won't report diagnostics by itself, so the operation is marked as optional to avoid
35261 // generating declarations for directives that don't have any inputs/outputs.
35262 return true;
35263 }
35264 execute() {
35265 const id = this.tcb.allocateId();
35266 const type = this.tcb.env.referenceType(this.dir.ref);
35267 addExpressionIdentifier(type, ExpressionIdentifier.DIRECTIVE);
35268 addParseSpanInfo(type, this.node.startSourceSpan || this.node.sourceSpan);
35269 this.scope.addStatement(tsDeclareVariable(id, type));
35270 return id;
35271 }
35272 }
35273 /**
35274 * A `TcbOp` which creates a variable for a local ref in a template.
35275 * The initializer for the variable is the variable expression for the directive, template, or
35276 * element the ref refers to. When the reference is used in the template, those TCB statements will
35277 * access this variable as well. For example:
35278 * ```
35279 * var _t1 = document.createElement('div');
35280 * var _t2 = _t1;
35281 * _t2.value
35282 * ```
35283 * This operation supports more fluent lookups for the `TemplateTypeChecker` when getting a symbol
35284 * for a reference. In most cases, this isn't essential; that is, the information for the symbol
35285 * could be gathered without this operation using the `BoundTarget`. However, for the case of
35286 * ng-template references, we will need this reference variable to not only provide a location in
35287 * the shim file, but also to narrow the variable to the correct `TemplateRef<T>` type rather than
35288 * `TemplateRef<any>` (this work is still TODO).
35289 *
35290 * Executing this operation returns a reference to the directive instance variable with its inferred
35291 * type.
35292 */
35293 class TcbReferenceOp extends TcbOp {
35294 constructor(tcb, scope, node, host, target) {
35295 super();
35296 this.tcb = tcb;
35297 this.scope = scope;
35298 this.node = node;
35299 this.host = host;
35300 this.target = target;
35301 // The statement generated by this operation is only used to for the Type Checker
35302 // so it can map a reference variable in the template directly to a node in the TCB.
35303 this.optional = true;
35304 }
35305 execute() {
35306 const id = this.tcb.allocateId();
35307 let initializer = this.target instanceof Template || this.target instanceof Element ?
35308 this.scope.resolve(this.target) :
35309 this.scope.resolve(this.host, this.target);
35310 // The reference is either to an element, an <ng-template> node, or to a directive on an
35311 // element or template.
35312 if ((this.target instanceof Element && !this.tcb.env.config.checkTypeOfDomReferences) ||
35313 !this.tcb.env.config.checkTypeOfNonDomReferences) {
35314 // References to DOM nodes are pinned to 'any' when `checkTypeOfDomReferences` is `false`.
35315 // References to `TemplateRef`s and directives are pinned to 'any' when
35316 // `checkTypeOfNonDomReferences` is `false`.
35317 initializer =
35318 ts$1.createAsExpression(initializer, ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
35319 }
35320 else if (this.target instanceof Template) {
35321 // Direct references to an <ng-template> node simply require a value of type
35322 // `TemplateRef<any>`. To get this, an expression of the form
35323 // `(_t1 as any as TemplateRef<any>)` is constructed.
35324 initializer =
35325 ts$1.createAsExpression(initializer, ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
35326 initializer = ts$1.createAsExpression(initializer, this.tcb.env.referenceExternalType('@angular/core', 'TemplateRef', [DYNAMIC_TYPE]));
35327 initializer = ts$1.createParen(initializer);
35328 }
35329 addParseSpanInfo(initializer, this.node.sourceSpan);
35330 addParseSpanInfo(id, this.node.keySpan);
35331 this.scope.addStatement(tsCreateVariable(id, initializer));
35332 return id;
35333 }
35334 }
35335 /**
35336 * A `TcbOp` which is used when the target of a reference is missing. This operation generates a
35337 * variable of type any for usages of the invalid reference to resolve to. The invalid reference
35338 * itself is recorded out-of-band.
35339 */
35340 class TcbInvalidReferenceOp extends TcbOp {
35341 constructor(tcb, scope) {
35342 super();
35343 this.tcb = tcb;
35344 this.scope = scope;
35345 // The declaration of a missing reference is only needed when the reference is resolved.
35346 this.optional = true;
35347 }
35348 execute() {
35349 const id = this.tcb.allocateId();
35350 this.scope.addStatement(tsCreateVariable(id, NULL_AS_ANY));
35351 return id;
35352 }
35353 }
35354 /**
35355 * A `TcbOp` which constructs an instance of a directive with types inferred from its inputs. The
35356 * inputs themselves are not checked here; checking of inputs is achieved in `TcbDirectiveInputsOp`.
35357 * Any errors reported in this statement are ignored, as the type constructor call is only present
35358 * for type-inference.
35359 *
35360 * When a Directive is generic, it is required that the TCB generates the instance using this method
35361 * in order to infer the type information correctly.
35362 *
35363 * Executing this operation returns a reference to the directive instance variable with its inferred
35364 * type.
35365 */
35366 class TcbDirectiveCtorOp extends TcbOp {
35367 constructor(tcb, scope, node, dir) {
35368 super();
35369 this.tcb = tcb;
35370 this.scope = scope;
35371 this.node = node;
35372 this.dir = dir;
35373 }
35374 get optional() {
35375 // The statement generated by this operation is only used to infer the directive's type and
35376 // won't report diagnostics by itself, so the operation is marked as optional.
35377 return true;
35378 }
35379 execute() {
35380 const id = this.tcb.allocateId();
35381 addExpressionIdentifier(id, ExpressionIdentifier.DIRECTIVE);
35382 addParseSpanInfo(id, this.node.startSourceSpan || this.node.sourceSpan);
35383 const genericInputs = new Map();
35384 const inputs = getBoundInputs(this.dir, this.node, this.tcb);
35385 for (const input of inputs) {
35386 // Skip text attributes if configured to do so.
35387 if (!this.tcb.env.config.checkTypeOfAttributes &&
35388 input.attribute instanceof TextAttribute) {
35389 continue;
35390 }
35391 for (const fieldName of input.fieldNames) {
35392 // Skip the field if an attribute has already been bound to it; we can't have a duplicate
35393 // key in the type constructor call.
35394 if (genericInputs.has(fieldName)) {
35395 continue;
35396 }
35397 const expression = translateInput(input.attribute, this.tcb, this.scope);
35398 genericInputs.set(fieldName, {
35399 type: 'binding',
35400 field: fieldName,
35401 expression,
35402 sourceSpan: input.attribute.sourceSpan
35403 });
35404 }
35405 }
35406 // Add unset directive inputs for each of the remaining unset fields.
35407 for (const [fieldName] of this.dir.inputs) {
35408 if (!genericInputs.has(fieldName)) {
35409 genericInputs.set(fieldName, { type: 'unset', field: fieldName });
35410 }
35411 }
35412 // Call the type constructor of the directive to infer a type, and assign the directive
35413 // instance.
35414 const typeCtor = tcbCallTypeCtor(this.dir, this.tcb, Array.from(genericInputs.values()));
35415 markIgnoreDiagnostics(typeCtor);
35416 this.scope.addStatement(tsCreateVariable(id, typeCtor));
35417 return id;
35418 }
35419 circularFallback() {
35420 return new TcbDirectiveCtorCircularFallbackOp(this.tcb, this.scope, this.node, this.dir);
35421 }
35422 }
35423 /**
35424 * A `TcbOp` which generates code to check input bindings on an element that correspond with the
35425 * members of a directive.
35426 *
35427 * Executing this operation returns nothing.
35428 */
35429 class TcbDirectiveInputsOp extends TcbOp {
35430 constructor(tcb, scope, node, dir) {
35431 super();
35432 this.tcb = tcb;
35433 this.scope = scope;
35434 this.node = node;
35435 this.dir = dir;
35436 }
35437 get optional() {
35438 return false;
35439 }
35440 execute() {
35441 let dirId = null;
35442 // TODO(joost): report duplicate properties
35443 const inputs = getBoundInputs(this.dir, this.node, this.tcb);
35444 for (const input of inputs) {
35445 // For bound inputs, the property is assigned the binding expression.
35446 let expr = translateInput(input.attribute, this.tcb, this.scope);
35447 if (!this.tcb.env.config.checkTypeOfInputBindings) {
35448 // If checking the type of bindings is disabled, cast the resulting expression to 'any'
35449 // before the assignment.
35450 expr = tsCastToAny(expr);
35451 }
35452 else if (!this.tcb.env.config.strictNullInputBindings) {
35453 // If strict null checks are disabled, erase `null` and `undefined` from the type by
35454 // wrapping the expression in a non-null assertion.
35455 expr = ts$1.createNonNullExpression(expr);
35456 }
35457 let assignment = wrapForDiagnostics(expr);
35458 for (const fieldName of input.fieldNames) {
35459 let target;
35460 if (this.dir.coercedInputFields.has(fieldName)) {
35461 // The input has a coercion declaration which should be used instead of assigning the
35462 // expression into the input field directly. To achieve this, a variable is declared
35463 // with a type of `typeof Directive.ngAcceptInputType_fieldName` which is then used as
35464 // target of the assignment.
35465 const dirTypeRef = this.tcb.env.referenceType(this.dir.ref);
35466 if (!ts$1.isTypeReferenceNode(dirTypeRef)) {
35467 throw new Error(`Expected TypeReferenceNode from reference to ${this.dir.ref.debugName}`);
35468 }
35469 const id = this.tcb.allocateId();
35470 const type = tsCreateTypeQueryForCoercedInput(dirTypeRef.typeName, fieldName);
35471 this.scope.addStatement(tsDeclareVariable(id, type));
35472 target = id;
35473 }
35474 else if (this.dir.undeclaredInputFields.has(fieldName)) {
35475 // If no coercion declaration is present nor is the field declared (i.e. the input is
35476 // declared in a `@Directive` or `@Component` decorator's `inputs` property) there is no
35477 // assignment target available, so this field is skipped.
35478 continue;
35479 }
35480 else if (!this.tcb.env.config.honorAccessModifiersForInputBindings &&
35481 this.dir.restrictedInputFields.has(fieldName)) {
35482 // If strict checking of access modifiers is disabled and the field is restricted
35483 // (i.e. private/protected/readonly), generate an assignment into a temporary variable
35484 // that has the type of the field. This achieves type-checking but circumvents the access
35485 // modifiers.
35486 if (dirId === null) {
35487 dirId = this.scope.resolve(this.node, this.dir);
35488 }
35489 const id = this.tcb.allocateId();
35490 const dirTypeRef = this.tcb.env.referenceType(this.dir.ref);
35491 if (!ts$1.isTypeReferenceNode(dirTypeRef)) {
35492 throw new Error(`Expected TypeReferenceNode from reference to ${this.dir.ref.debugName}`);
35493 }
35494 const type = ts$1.createIndexedAccessTypeNode(ts$1.createTypeQueryNode(dirId), ts$1.createLiteralTypeNode(ts$1.createStringLiteral(fieldName)));
35495 const temp = tsDeclareVariable(id, type);
35496 this.scope.addStatement(temp);
35497 target = id;
35498 }
35499 else {
35500 if (dirId === null) {
35501 dirId = this.scope.resolve(this.node, this.dir);
35502 }
35503 // To get errors assign directly to the fields on the instance, using property access
35504 // when possible. String literal fields may not be valid JS identifiers so we use
35505 // literal element access instead for those cases.
35506 target = this.dir.stringLiteralInputFields.has(fieldName) ?
35507 ts$1.createElementAccess(dirId, ts$1.createStringLiteral(fieldName)) :
35508 ts$1.createPropertyAccess(dirId, ts$1.createIdentifier(fieldName));
35509 }
35510 if (input.attribute.keySpan !== undefined) {
35511 addParseSpanInfo(target, input.attribute.keySpan);
35512 }
35513 // Finally the assignment is extended by assigning it into the target expression.
35514 assignment = ts$1.createBinary(target, ts$1.SyntaxKind.EqualsToken, assignment);
35515 }
35516 addParseSpanInfo(assignment, input.attribute.sourceSpan);
35517 // Ignore diagnostics for text attributes if configured to do so.
35518 if (!this.tcb.env.config.checkTypeOfAttributes &&
35519 input.attribute instanceof TextAttribute) {
35520 markIgnoreDiagnostics(assignment);
35521 }
35522 this.scope.addStatement(ts$1.createExpressionStatement(assignment));
35523 }
35524 return null;
35525 }
35526 }
35527 /**
35528 * A `TcbOp` which is used to generate a fallback expression if the inference of a directive type
35529 * via `TcbDirectiveCtorOp` requires a reference to its own type. This can happen using a template
35530 * reference:
35531 *
35532 * ```html
35533 * <some-cmp #ref [prop]="ref.foo"></some-cmp>
35534 * ```
35535 *
35536 * In this case, `TcbDirectiveCtorCircularFallbackOp` will add a second inference of the directive
35537 * type to the type-check block, this time calling the directive's type constructor without any
35538 * input expressions. This infers the widest possible supertype for the directive, which is used to
35539 * resolve any recursive references required to infer the real type.
35540 */
35541 class TcbDirectiveCtorCircularFallbackOp extends TcbOp {
35542 constructor(tcb, scope, node, dir) {
35543 super();
35544 this.tcb = tcb;
35545 this.scope = scope;
35546 this.node = node;
35547 this.dir = dir;
35548 }
35549 get optional() {
35550 return false;
35551 }
35552 execute() {
35553 const id = this.tcb.allocateId();
35554 const typeCtor = this.tcb.env.typeCtorFor(this.dir);
35555 const circularPlaceholder = ts$1.createCall(typeCtor, /* typeArguments */ undefined, [ts$1.createNonNullExpression(ts$1.createNull())]);
35556 this.scope.addStatement(tsCreateVariable(id, circularPlaceholder));
35557 return id;
35558 }
35559 }
35560 /**
35561 * A `TcbOp` which feeds elements and unclaimed properties to the `DomSchemaChecker`.
35562 *
35563 * The DOM schema is not checked via TCB code generation. Instead, the `DomSchemaChecker` ingests
35564 * elements and property bindings and accumulates synthetic `ts.Diagnostic`s out-of-band. These are
35565 * later merged with the diagnostics generated from the TCB.
35566 *
35567 * For convenience, the TCB iteration of the template is used to drive the `DomSchemaChecker` via
35568 * the `TcbDomSchemaCheckerOp`.
35569 */
35570 class TcbDomSchemaCheckerOp extends TcbOp {
35571 constructor(tcb, element, checkElement, claimedInputs) {
35572 super();
35573 this.tcb = tcb;
35574 this.element = element;
35575 this.checkElement = checkElement;
35576 this.claimedInputs = claimedInputs;
35577 }
35578 get optional() {
35579 return false;
35580 }
35581 execute() {
35582 if (this.checkElement) {
35583 this.tcb.domSchemaChecker.checkElement(this.tcb.id, this.element, this.tcb.schemas);
35584 }
35585 // TODO(alxhub): this could be more efficient.
35586 for (const binding of this.element.inputs) {
35587 if (binding.type === 0 /* Property */ && this.claimedInputs.has(binding.name)) {
35588 // Skip this binding as it was claimed by a directive.
35589 continue;
35590 }
35591 if (binding.type === 0 /* Property */) {
35592 if (binding.name !== 'style' && binding.name !== 'class') {
35593 // A direct binding to a property.
35594 const propertyName = ATTR_TO_PROP[binding.name] || binding.name;
35595 this.tcb.domSchemaChecker.checkProperty(this.tcb.id, this.element, propertyName, binding.sourceSpan, this.tcb.schemas);
35596 }
35597 }
35598 }
35599 return null;
35600 }
35601 }
35602 /**
35603 * Mapping between attributes names that don't correspond to their element property names.
35604 * Note: this mapping has to be kept in sync with the equally named mapping in the runtime.
35605 */
35606 const ATTR_TO_PROP = {
35607 'class': 'className',
35608 'for': 'htmlFor',
35609 'formaction': 'formAction',
35610 'innerHtml': 'innerHTML',
35611 'readonly': 'readOnly',
35612 'tabindex': 'tabIndex',
35613 };
35614 /**
35615 * A `TcbOp` which generates code to check "unclaimed inputs" - bindings on an element which were
35616 * not attributed to any directive or component, and are instead processed against the HTML element
35617 * itself.
35618 *
35619 * Currently, only the expressions of these bindings are checked. The targets of the bindings are
35620 * checked against the DOM schema via a `TcbDomSchemaCheckerOp`.
35621 *
35622 * Executing this operation returns nothing.
35623 */
35624 class TcbUnclaimedInputsOp extends TcbOp {
35625 constructor(tcb, scope, element, claimedInputs) {
35626 super();
35627 this.tcb = tcb;
35628 this.scope = scope;
35629 this.element = element;
35630 this.claimedInputs = claimedInputs;
35631 }
35632 get optional() {
35633 return false;
35634 }
35635 execute() {
35636 // `this.inputs` contains only those bindings not matched by any directive. These bindings go to
35637 // the element itself.
35638 let elId = null;
35639 // TODO(alxhub): this could be more efficient.
35640 for (const binding of this.element.inputs) {
35641 if (binding.type === 0 /* Property */ && this.claimedInputs.has(binding.name)) {
35642 // Skip this binding as it was claimed by a directive.
35643 continue;
35644 }
35645 let expr = tcbExpression(binding.value, this.tcb, this.scope);
35646 if (!this.tcb.env.config.checkTypeOfInputBindings) {
35647 // If checking the type of bindings is disabled, cast the resulting expression to 'any'
35648 // before the assignment.
35649 expr = tsCastToAny(expr);
35650 }
35651 else if (!this.tcb.env.config.strictNullInputBindings) {
35652 // If strict null checks are disabled, erase `null` and `undefined` from the type by
35653 // wrapping the expression in a non-null assertion.
35654 expr = ts$1.createNonNullExpression(expr);
35655 }
35656 if (this.tcb.env.config.checkTypeOfDomBindings && binding.type === 0 /* Property */) {
35657 if (binding.name !== 'style' && binding.name !== 'class') {
35658 if (elId === null) {
35659 elId = this.scope.resolve(this.element);
35660 }
35661 // A direct binding to a property.
35662 const propertyName = ATTR_TO_PROP[binding.name] || binding.name;
35663 const prop = ts$1.createElementAccess(elId, ts$1.createStringLiteral(propertyName));
35664 const stmt = ts$1.createBinary(prop, ts$1.SyntaxKind.EqualsToken, wrapForDiagnostics(expr));
35665 addParseSpanInfo(stmt, binding.sourceSpan);
35666 this.scope.addStatement(ts$1.createExpressionStatement(stmt));
35667 }
35668 else {
35669 this.scope.addStatement(ts$1.createExpressionStatement(expr));
35670 }
35671 }
35672 else {
35673 // A binding to an animation, attribute, class or style. For now, only validate the right-
35674 // hand side of the expression.
35675 // TODO: properly check class and style bindings.
35676 this.scope.addStatement(ts$1.createExpressionStatement(expr));
35677 }
35678 }
35679 return null;
35680 }
35681 }
35682 /**
35683 * A `TcbOp` which generates code to check event bindings on an element that correspond with the
35684 * outputs of a directive.
35685 *
35686 * Executing this operation returns nothing.
35687 */
35688 class TcbDirectiveOutputsOp extends TcbOp {
35689 constructor(tcb, scope, node, dir) {
35690 super();
35691 this.tcb = tcb;
35692 this.scope = scope;
35693 this.node = node;
35694 this.dir = dir;
35695 }
35696 get optional() {
35697 return false;
35698 }
35699 execute() {
35700 let dirId = null;
35701 const outputs = this.dir.outputs;
35702 for (const output of this.node.outputs) {
35703 if (output.type !== 0 /* Regular */ || !outputs.hasBindingPropertyName(output.name)) {
35704 continue;
35705 }
35706 // TODO(alxhub): consider supporting multiple fields with the same property name for outputs.
35707 const field = outputs.getByBindingPropertyName(output.name)[0].classPropertyName;
35708 if (dirId === null) {
35709 dirId = this.scope.resolve(this.node, this.dir);
35710 }
35711 const outputField = ts$1.createElementAccess(dirId, ts$1.createStringLiteral(field));
35712 addParseSpanInfo(outputField, output.keySpan);
35713 if (this.tcb.env.config.checkTypeOfOutputEvents) {
35714 // For strict checking of directive events, generate a call to the `subscribe` method
35715 // on the directive's output field to let type information flow into the handler function's
35716 // `$event` parameter.
35717 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 0 /* Infer */);
35718 const subscribeFn = ts$1.createPropertyAccess(outputField, 'subscribe');
35719 const call = ts$1.createCall(subscribeFn, /* typeArguments */ undefined, [handler]);
35720 addParseSpanInfo(call, output.sourceSpan);
35721 this.scope.addStatement(ts$1.createExpressionStatement(call));
35722 }
35723 else {
35724 // If strict checking of directive events is disabled:
35725 //
35726 // * We still generate the access to the output field as a statement in the TCB so consumers
35727 // of the `TemplateTypeChecker` can still find the node for the class member for the
35728 // output.
35729 // * Emit a handler function where the `$event` parameter has an explicit `any` type.
35730 this.scope.addStatement(ts$1.createExpressionStatement(outputField));
35731 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 1 /* Any */);
35732 this.scope.addStatement(ts$1.createExpressionStatement(handler));
35733 }
35734 ExpressionSemanticVisitor.visit(output.handler, this.tcb.id, this.tcb.boundTarget, this.tcb.oobRecorder);
35735 }
35736 return null;
35737 }
35738 }
35739 /**
35740 * A `TcbOp` which generates code to check "unclaimed outputs" - event bindings on an element which
35741 * were not attributed to any directive or component, and are instead processed against the HTML
35742 * element itself.
35743 *
35744 * Executing this operation returns nothing.
35745 */
35746 class TcbUnclaimedOutputsOp extends TcbOp {
35747 constructor(tcb, scope, element, claimedOutputs) {
35748 super();
35749 this.tcb = tcb;
35750 this.scope = scope;
35751 this.element = element;
35752 this.claimedOutputs = claimedOutputs;
35753 }
35754 get optional() {
35755 return false;
35756 }
35757 execute() {
35758 let elId = null;
35759 // TODO(alxhub): this could be more efficient.
35760 for (const output of this.element.outputs) {
35761 if (this.claimedOutputs.has(output.name)) {
35762 // Skip this event handler as it was claimed by a directive.
35763 continue;
35764 }
35765 if (output.type === 1 /* Animation */) {
35766 // Animation output bindings always have an `$event` parameter of type `AnimationEvent`.
35767 const eventType = this.tcb.env.config.checkTypeOfAnimationEvents ?
35768 this.tcb.env.referenceExternalType('@angular/animations', 'AnimationEvent') :
35769 1 /* Any */;
35770 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, eventType);
35771 this.scope.addStatement(ts$1.createExpressionStatement(handler));
35772 }
35773 else if (this.tcb.env.config.checkTypeOfDomEvents) {
35774 // If strict checking of DOM events is enabled, generate a call to `addEventListener` on
35775 // the element instance so that TypeScript's type inference for
35776 // `HTMLElement.addEventListener` using `HTMLElementEventMap` to infer an accurate type for
35777 // `$event` depending on the event name. For unknown event names, TypeScript resorts to the
35778 // base `Event` type.
35779 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 0 /* Infer */);
35780 if (elId === null) {
35781 elId = this.scope.resolve(this.element);
35782 }
35783 const propertyAccess = ts$1.createPropertyAccess(elId, 'addEventListener');
35784 addParseSpanInfo(propertyAccess, output.keySpan);
35785 const call = ts$1.createCall(
35786 /* expression */ propertyAccess,
35787 /* typeArguments */ undefined,
35788 /* arguments */ [ts$1.createStringLiteral(output.name), handler]);
35789 addParseSpanInfo(call, output.sourceSpan);
35790 this.scope.addStatement(ts$1.createExpressionStatement(call));
35791 }
35792 else {
35793 // If strict checking of DOM inputs is disabled, emit a handler function where the `$event`
35794 // parameter has an explicit `any` type.
35795 const handler = tcbCreateEventHandler(output, this.tcb, this.scope, 1 /* Any */);
35796 this.scope.addStatement(ts$1.createExpressionStatement(handler));
35797 }
35798 ExpressionSemanticVisitor.visit(output.handler, this.tcb.id, this.tcb.boundTarget, this.tcb.oobRecorder);
35799 }
35800 return null;
35801 }
35802 }
35803 /**
35804 * A `TcbOp` which generates a completion point for the component context.
35805 *
35806 * This completion point looks like `ctx. ;` in the TCB output, and does not produce diagnostics.
35807 * TypeScript autocompletion APIs can be used at this completion point (after the '.') to produce
35808 * autocompletion results of properties and methods from the template's component context.
35809 */
35810 class TcbComponentContextCompletionOp extends TcbOp {
35811 constructor(scope) {
35812 super();
35813 this.scope = scope;
35814 this.optional = false;
35815 }
35816 execute() {
35817 const ctx = ts$1.createIdentifier('ctx');
35818 const ctxDot = ts$1.createPropertyAccess(ctx, '');
35819 markIgnoreDiagnostics(ctxDot);
35820 addExpressionIdentifier(ctxDot, ExpressionIdentifier.COMPONENT_COMPLETION);
35821 this.scope.addStatement(ts$1.createExpressionStatement(ctxDot));
35822 return null;
35823 }
35824 }
35825 /**
35826 * Value used to break a circular reference between `TcbOp`s.
35827 *
35828 * This value is returned whenever `TcbOp`s have a circular dependency. The expression is a non-null
35829 * assertion of the null value (in TypeScript, the expression `null!`). This construction will infer
35830 * the least narrow type for whatever it's assigned to.
35831 */
35832 const INFER_TYPE_FOR_CIRCULAR_OP_EXPR = ts$1.createNonNullExpression(ts$1.createNull());
35833 /**
35834 * Overall generation context for the type check block.
35835 *
35836 * `Context` handles operations during code generation which are global with respect to the whole
35837 * block. It's responsible for variable name allocation and management of any imports needed. It
35838 * also contains the template metadata itself.
35839 */
35840 class Context$1 {
35841 constructor(env, domSchemaChecker, oobRecorder, id, boundTarget, pipes, schemas) {
35842 this.env = env;
35843 this.domSchemaChecker = domSchemaChecker;
35844 this.oobRecorder = oobRecorder;
35845 this.id = id;
35846 this.boundTarget = boundTarget;
35847 this.pipes = pipes;
35848 this.schemas = schemas;
35849 this.nextId = 1;
35850 }
35851 /**
35852 * Allocate a new variable name for use within the `Context`.
35853 *
35854 * Currently this uses a monotonically increasing counter, but in the future the variable name
35855 * might change depending on the type of data being stored.
35856 */
35857 allocateId() {
35858 return ts$1.createIdentifier(`_t${this.nextId++}`);
35859 }
35860 getPipeByName(name) {
35861 if (!this.pipes.has(name)) {
35862 return null;
35863 }
35864 return this.pipes.get(name);
35865 }
35866 }
35867 /**
35868 * Local scope within the type check block for a particular template.
35869 *
35870 * The top-level template and each nested `<ng-template>` have their own `Scope`, which exist in a
35871 * hierarchy. The structure of this hierarchy mirrors the syntactic scopes in the generated type
35872 * check block, where each nested template is encased in an `if` structure.
35873 *
35874 * As a template's `TcbOp`s are executed in a given `Scope`, statements are added via
35875 * `addStatement()`. When this processing is complete, the `Scope` can be turned into a `ts.Block`
35876 * via `renderToBlock()`.
35877 *
35878 * If a `TcbOp` requires the output of another, it can call `resolve()`.
35879 */
35880 class Scope$1 {
35881 constructor(tcb, parent = null, guard = null) {
35882 this.tcb = tcb;
35883 this.parent = parent;
35884 this.guard = guard;
35885 /**
35886 * A queue of operations which need to be performed to generate the TCB code for this scope.
35887 *
35888 * This array can contain either a `TcbOp` which has yet to be executed, or a `ts.Expression|null`
35889 * representing the memoized result of executing the operation. As operations are executed, their
35890 * results are written into the `opQueue`, overwriting the original operation.
35891 *
35892 * If an operation is in the process of being executed, it is temporarily overwritten here with
35893 * `INFER_TYPE_FOR_CIRCULAR_OP_EXPR`. This way, if a cycle is encountered where an operation
35894 * depends transitively on its own result, the inner operation will infer the least narrow type
35895 * that fits instead. This has the same semantics as TypeScript itself when types are referenced
35896 * circularly.
35897 */
35898 this.opQueue = [];
35899 /**
35900 * A map of `TmplAstElement`s to the index of their `TcbElementOp` in the `opQueue`
35901 */
35902 this.elementOpMap = new Map();
35903 /**
35904 * A map of maps which tracks the index of `TcbDirectiveCtorOp`s in the `opQueue` for each
35905 * directive on a `TmplAstElement` or `TmplAstTemplate` node.
35906 */
35907 this.directiveOpMap = new Map();
35908 /**
35909 * A map of `TmplAstReference`s to the index of their `TcbReferenceOp` in the `opQueue`
35910 */
35911 this.referenceOpMap = new Map();
35912 /**
35913 * Map of immediately nested <ng-template>s (within this `Scope`) represented by `TmplAstTemplate`
35914 * nodes to the index of their `TcbTemplateContextOp`s in the `opQueue`.
35915 */
35916 this.templateCtxOpMap = new Map();
35917 /**
35918 * Map of variables declared on the template that created this `Scope` (represented by
35919 * `TmplAstVariable` nodes) to the index of their `TcbVariableOp`s in the `opQueue`.
35920 */
35921 this.varMap = new Map();
35922 /**
35923 * Statements for this template.
35924 *
35925 * Executing the `TcbOp`s in the `opQueue` populates this array.
35926 */
35927 this.statements = [];
35928 }
35929 /**
35930 * Constructs a `Scope` given either a `TmplAstTemplate` or a list of `TmplAstNode`s.
35931 *
35932 * @param tcb the overall context of TCB generation.
35933 * @param parent the `Scope` of the parent template (if any) or `null` if this is the root
35934 * `Scope`.
35935 * @param templateOrNodes either a `TmplAstTemplate` representing the template for which to
35936 * calculate the `Scope`, or a list of nodes if no outer template object is available.
35937 * @param guard an expression that is applied to this scope for type narrowing purposes.
35938 */
35939 static forNodes(tcb, parent, templateOrNodes, guard) {
35940 const scope = new Scope$1(tcb, parent, guard);
35941 if (parent === null && tcb.env.config.enableTemplateTypeChecker) {
35942 // Add an autocompletion point for the component context.
35943 scope.opQueue.push(new TcbComponentContextCompletionOp(scope));
35944 }
35945 let children;
35946 // If given an actual `TmplAstTemplate` instance, then process any additional information it
35947 // has.
35948 if (templateOrNodes instanceof Template) {
35949 // The template's variable declarations need to be added as `TcbVariableOp`s.
35950 const varMap = new Map();
35951 for (const v of templateOrNodes.variables) {
35952 // Validate that variables on the `TmplAstTemplate` are only declared once.
35953 if (!varMap.has(v.name)) {
35954 varMap.set(v.name, v);
35955 }
35956 else {
35957 const firstDecl = varMap.get(v.name);
35958 tcb.oobRecorder.duplicateTemplateVar(tcb.id, v, firstDecl);
35959 }
35960 const opIndex = scope.opQueue.push(new TcbVariableOp(tcb, scope, templateOrNodes, v)) - 1;
35961 scope.varMap.set(v, opIndex);
35962 }
35963 children = templateOrNodes.children;
35964 }
35965 else {
35966 children = templateOrNodes;
35967 }
35968 for (const node of children) {
35969 scope.appendNode(node);
35970 }
35971 return scope;
35972 }
35973 /**
35974 * Look up a `ts.Expression` representing the value of some operation in the current `Scope`,
35975 * including any parent scope(s). This method always returns a mutable clone of the
35976 * `ts.Expression` with the comments cleared.
35977 *
35978 * @param node a `TmplAstNode` of the operation in question. The lookup performed will depend on
35979 * the type of this node:
35980 *
35981 * Assuming `directive` is not present, then `resolve` will return:
35982 *
35983 * * `TmplAstElement` - retrieve the expression for the element DOM node
35984 * * `TmplAstTemplate` - retrieve the template context variable
35985 * * `TmplAstVariable` - retrieve a template let- variable
35986 * * `TmplAstReference` - retrieve variable created for the local ref
35987 *
35988 * @param directive if present, a directive type on a `TmplAstElement` or `TmplAstTemplate` to
35989 * look up instead of the default for an element or template node.
35990 */
35991 resolve(node, directive) {
35992 // Attempt to resolve the operation locally.
35993 const res = this.resolveLocal(node, directive);
35994 if (res !== null) {
35995 // We want to get a clone of the resolved expression and clear the trailing comments
35996 // so they don't continue to appear in every place the expression is used.
35997 // As an example, this would otherwise produce:
35998 // var _t1 /**T:DIR*/ /*1,2*/ = _ctor1();
35999 // _t1 /**T:DIR*/ /*1,2*/.input = 'value';
36000 //
36001 // In addition, returning a clone prevents the consumer of `Scope#resolve` from
36002 // attaching comments at the declaration site.
36003 const clone = ts$1.getMutableClone(res);
36004 ts$1.setSyntheticTrailingComments(clone, []);
36005 return clone;
36006 }
36007 else if (this.parent !== null) {
36008 // Check with the parent.
36009 return this.parent.resolve(node, directive);
36010 }
36011 else {
36012 throw new Error(`Could not resolve ${node} / ${directive}`);
36013 }
36014 }
36015 /**
36016 * Add a statement to this scope.
36017 */
36018 addStatement(stmt) {
36019 this.statements.push(stmt);
36020 }
36021 /**
36022 * Get the statements.
36023 */
36024 render() {
36025 for (let i = 0; i < this.opQueue.length; i++) {
36026 // Optional statements cannot be skipped when we are generating the TCB for use
36027 // by the TemplateTypeChecker.
36028 const skipOptional = !this.tcb.env.config.enableTemplateTypeChecker;
36029 this.executeOp(i, skipOptional);
36030 }
36031 return this.statements;
36032 }
36033 /**
36034 * Returns an expression of all template guards that apply to this scope, including those of
36035 * parent scopes. If no guards have been applied, null is returned.
36036 */
36037 guards() {
36038 let parentGuards = null;
36039 if (this.parent !== null) {
36040 // Start with the guards from the parent scope, if present.
36041 parentGuards = this.parent.guards();
36042 }
36043 if (this.guard === null) {
36044 // This scope does not have a guard, so return the parent's guards as is.
36045 return parentGuards;
36046 }
36047 else if (parentGuards === null) {
36048 // There's no guards from the parent scope, so this scope's guard represents all available
36049 // guards.
36050 return this.guard;
36051 }
36052 else {
36053 // Both the parent scope and this scope provide a guard, so create a combination of the two.
36054 // It is important that the parent guard is used as left operand, given that it may provide
36055 // narrowing that is required for this scope's guard to be valid.
36056 return ts$1.createBinary(parentGuards, ts$1.SyntaxKind.AmpersandAmpersandToken, this.guard);
36057 }
36058 }
36059 resolveLocal(ref, directive) {
36060 if (ref instanceof Reference && this.referenceOpMap.has(ref)) {
36061 return this.resolveOp(this.referenceOpMap.get(ref));
36062 }
36063 else if (ref instanceof Variable && this.varMap.has(ref)) {
36064 // Resolving a context variable for this template.
36065 // Execute the `TcbVariableOp` associated with the `TmplAstVariable`.
36066 return this.resolveOp(this.varMap.get(ref));
36067 }
36068 else if (ref instanceof Template && directive === undefined &&
36069 this.templateCtxOpMap.has(ref)) {
36070 // Resolving the context of the given sub-template.
36071 // Execute the `TcbTemplateContextOp` for the template.
36072 return this.resolveOp(this.templateCtxOpMap.get(ref));
36073 }
36074 else if ((ref instanceof Element || ref instanceof Template) &&
36075 directive !== undefined && this.directiveOpMap.has(ref)) {
36076 // Resolving a directive on an element or sub-template.
36077 const dirMap = this.directiveOpMap.get(ref);
36078 if (dirMap.has(directive)) {
36079 return this.resolveOp(dirMap.get(directive));
36080 }
36081 else {
36082 return null;
36083 }
36084 }
36085 else if (ref instanceof Element && this.elementOpMap.has(ref)) {
36086 // Resolving the DOM node of an element in this template.
36087 return this.resolveOp(this.elementOpMap.get(ref));
36088 }
36089 else {
36090 return null;
36091 }
36092 }
36093 /**
36094 * Like `executeOp`, but assert that the operation actually returned `ts.Expression`.
36095 */
36096 resolveOp(opIndex) {
36097 const res = this.executeOp(opIndex, /* skipOptional */ false);
36098 if (res === null) {
36099 throw new Error(`Error resolving operation, got null`);
36100 }
36101 return res;
36102 }
36103 /**
36104 * Execute a particular `TcbOp` in the `opQueue`.
36105 *
36106 * This method replaces the operation in the `opQueue` with the result of execution (once done)
36107 * and also protects against a circular dependency from the operation to itself by temporarily
36108 * setting the operation's result to a special expression.
36109 */
36110 executeOp(opIndex, skipOptional) {
36111 const op = this.opQueue[opIndex];
36112 if (!(op instanceof TcbOp)) {
36113 return op;
36114 }
36115 if (skipOptional && op.optional) {
36116 return null;
36117 }
36118 // Set the result of the operation in the queue to its circular fallback. If executing this
36119 // operation results in a circular dependency, this will prevent an infinite loop and allow for
36120 // the resolution of such cycles.
36121 this.opQueue[opIndex] = op.circularFallback();
36122 const res = op.execute();
36123 // Once the operation has finished executing, it's safe to cache the real result.
36124 this.opQueue[opIndex] = res;
36125 return res;
36126 }
36127 appendNode(node) {
36128 if (node instanceof Element) {
36129 const opIndex = this.opQueue.push(new TcbElementOp(this.tcb, this, node)) - 1;
36130 this.elementOpMap.set(node, opIndex);
36131 this.appendDirectivesAndInputsOfNode(node);
36132 this.appendOutputsOfNode(node);
36133 for (const child of node.children) {
36134 this.appendNode(child);
36135 }
36136 this.checkAndAppendReferencesOfNode(node);
36137 }
36138 else if (node instanceof Template) {
36139 // Template children are rendered in a child scope.
36140 this.appendDirectivesAndInputsOfNode(node);
36141 this.appendOutputsOfNode(node);
36142 const ctxIndex = this.opQueue.push(new TcbTemplateContextOp(this.tcb, this)) - 1;
36143 this.templateCtxOpMap.set(node, ctxIndex);
36144 if (this.tcb.env.config.checkTemplateBodies) {
36145 this.opQueue.push(new TcbTemplateBodyOp(this.tcb, this, node));
36146 }
36147 else if (this.tcb.env.config.alwaysCheckSchemaInTemplateBodies) {
36148 this.appendDeepSchemaChecks(node.children);
36149 }
36150 this.checkAndAppendReferencesOfNode(node);
36151 }
36152 else if (node instanceof BoundText) {
36153 this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, node));
36154 }
36155 else if (node instanceof Icu) {
36156 this.appendIcuExpressions(node);
36157 }
36158 }
36159 checkAndAppendReferencesOfNode(node) {
36160 for (const ref of node.references) {
36161 const target = this.tcb.boundTarget.getReferenceTarget(ref);
36162 let ctxIndex;
36163 if (target === null) {
36164 // The reference is invalid if it doesn't have a target, so report it as an error.
36165 this.tcb.oobRecorder.missingReferenceTarget(this.tcb.id, ref);
36166 // Any usages of the invalid reference will be resolved to a variable of type any.
36167 ctxIndex = this.opQueue.push(new TcbInvalidReferenceOp(this.tcb, this)) - 1;
36168 }
36169 else if (target instanceof Template || target instanceof Element) {
36170 ctxIndex = this.opQueue.push(new TcbReferenceOp(this.tcb, this, ref, node, target)) - 1;
36171 }
36172 else {
36173 ctxIndex =
36174 this.opQueue.push(new TcbReferenceOp(this.tcb, this, ref, node, target.directive)) - 1;
36175 }
36176 this.referenceOpMap.set(ref, ctxIndex);
36177 }
36178 }
36179 appendDirectivesAndInputsOfNode(node) {
36180 // Collect all the inputs on the element.
36181 const claimedInputs = new Set();
36182 const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
36183 if (directives === null || directives.length === 0) {
36184 // If there are no directives, then all inputs are unclaimed inputs, so queue an operation
36185 // to add them if needed.
36186 if (node instanceof Element) {
36187 this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node, claimedInputs));
36188 this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, /* checkElement */ true, claimedInputs));
36189 }
36190 return;
36191 }
36192 const dirMap = new Map();
36193 for (const dir of directives) {
36194 const directiveOp = dir.isGeneric ? new TcbDirectiveCtorOp(this.tcb, this, node, dir) :
36195 new TcbDirectiveTypeOp(this.tcb, this, node, dir);
36196 const dirIndex = this.opQueue.push(directiveOp) - 1;
36197 dirMap.set(dir, dirIndex);
36198 this.opQueue.push(new TcbDirectiveInputsOp(this.tcb, this, node, dir));
36199 }
36200 this.directiveOpMap.set(node, dirMap);
36201 // After expanding the directives, we might need to queue an operation to check any unclaimed
36202 // inputs.
36203 if (node instanceof Element) {
36204 // Go through the directives and remove any inputs that it claims from `elementInputs`.
36205 for (const dir of directives) {
36206 for (const propertyName of dir.inputs.propertyNames) {
36207 claimedInputs.add(propertyName);
36208 }
36209 }
36210 this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node, claimedInputs));
36211 // If there are no directives which match this element, then it's a "plain" DOM element (or a
36212 // web component), and should be checked against the DOM schema. If any directives match,
36213 // we must assume that the element could be custom (either a component, or a directive like
36214 // <router-outlet>) and shouldn't validate the element name itself.
36215 const checkElement = directives.length === 0;
36216 this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, checkElement, claimedInputs));
36217 }
36218 }
36219 appendOutputsOfNode(node) {
36220 // Collect all the outputs on the element.
36221 const claimedOutputs = new Set();
36222 const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
36223 if (directives === null || directives.length === 0) {
36224 // If there are no directives, then all outputs are unclaimed outputs, so queue an operation
36225 // to add them if needed.
36226 if (node instanceof Element) {
36227 this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, claimedOutputs));
36228 }
36229 return;
36230 }
36231 // Queue operations for all directives to check the relevant outputs for a directive.
36232 for (const dir of directives) {
36233 this.opQueue.push(new TcbDirectiveOutputsOp(this.tcb, this, node, dir));
36234 }
36235 // After expanding the directives, we might need to queue an operation to check any unclaimed
36236 // outputs.
36237 if (node instanceof Element) {
36238 // Go through the directives and register any outputs that it claims in `claimedOutputs`.
36239 for (const dir of directives) {
36240 for (const outputProperty of dir.outputs.propertyNames) {
36241 claimedOutputs.add(outputProperty);
36242 }
36243 }
36244 this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, claimedOutputs));
36245 }
36246 }
36247 appendDeepSchemaChecks(nodes) {
36248 for (const node of nodes) {
36249 if (!(node instanceof Element || node instanceof Template)) {
36250 continue;
36251 }
36252 if (node instanceof Element) {
36253 const claimedInputs = new Set();
36254 const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
36255 let hasDirectives;
36256 if (directives === null || directives.length === 0) {
36257 hasDirectives = false;
36258 }
36259 else {
36260 hasDirectives = true;
36261 for (const dir of directives) {
36262 for (const propertyName of dir.inputs.propertyNames) {
36263 claimedInputs.add(propertyName);
36264 }
36265 }
36266 }
36267 this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, !hasDirectives, claimedInputs));
36268 }
36269 this.appendDeepSchemaChecks(node.children);
36270 }
36271 }
36272 appendIcuExpressions(node) {
36273 for (const variable of Object.values(node.vars)) {
36274 this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, variable));
36275 }
36276 for (const placeholder of Object.values(node.placeholders)) {
36277 if (placeholder instanceof BoundText) {
36278 this.opQueue.push(new TcbTextInterpolationOp(this.tcb, this, placeholder));
36279 }
36280 }
36281 }
36282 }
36283 /**
36284 * Create the `ctx` parameter to the top-level TCB function.
36285 *
36286 * This is a parameter with a type equivalent to the component type, with all generic type
36287 * parameters listed (without their generic bounds).
36288 */
36289 function tcbCtxParam(node, name, useGenericType) {
36290 let typeArguments = undefined;
36291 // Check if the component is generic, and pass generic type parameters if so.
36292 if (node.typeParameters !== undefined) {
36293 if (useGenericType) {
36294 typeArguments =
36295 node.typeParameters.map(param => ts$1.createTypeReferenceNode(param.name, undefined));
36296 }
36297 else {
36298 typeArguments =
36299 node.typeParameters.map(() => ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
36300 }
36301 }
36302 const type = ts$1.createTypeReferenceNode(name, typeArguments);
36303 return ts$1.createParameter(
36304 /* decorators */ undefined,
36305 /* modifiers */ undefined,
36306 /* dotDotDotToken */ undefined,
36307 /* name */ 'ctx',
36308 /* questionToken */ undefined,
36309 /* type */ type,
36310 /* initializer */ undefined);
36311 }
36312 /**
36313 * Process an `AST` expression and convert it into a `ts.Expression`, generating references to the
36314 * correct identifiers in the current scope.
36315 */
36316 function tcbExpression(ast, tcb, scope) {
36317 const translator = new TcbExpressionTranslator(tcb, scope);
36318 return translator.translate(ast);
36319 }
36320 class TcbExpressionTranslator {
36321 constructor(tcb, scope) {
36322 this.tcb = tcb;
36323 this.scope = scope;
36324 }
36325 translate(ast) {
36326 // `astToTypescript` actually does the conversion. A special resolver `tcbResolve` is passed
36327 // which interprets specific expression nodes that interact with the `ImplicitReceiver`. These
36328 // nodes actually refer to identifiers within the current scope.
36329 return astToTypescript(ast, ast => this.resolve(ast), this.tcb.env.config);
36330 }
36331 /**
36332 * Resolve an `AST` expression within the given scope.
36333 *
36334 * Some `AST` expressions refer to top-level concepts (references, variables, the component
36335 * context). This method assists in resolving those.
36336 */
36337 resolve(ast) {
36338 if (ast instanceof PropertyRead && ast.receiver instanceof ImplicitReceiver) {
36339 // Try to resolve a bound target for this expression. If no such target is available, then
36340 // the expression is referencing the top-level component context. In that case, `null` is
36341 // returned here to let it fall through resolution so it will be caught when the
36342 // `ImplicitReceiver` is resolved in the branch below.
36343 return this.resolveTarget(ast);
36344 }
36345 else if (ast instanceof PropertyWrite && ast.receiver instanceof ImplicitReceiver) {
36346 const target = this.resolveTarget(ast);
36347 if (target === null) {
36348 return null;
36349 }
36350 const expr = this.translate(ast.value);
36351 const result = ts$1.createParen(ts$1.createBinary(target, ts$1.SyntaxKind.EqualsToken, expr));
36352 addParseSpanInfo(result, ast.sourceSpan);
36353 return result;
36354 }
36355 else if (ast instanceof ImplicitReceiver) {
36356 // AST instances representing variables and references look very similar to property reads
36357 // or method calls from the component context: both have the shape
36358 // PropertyRead(ImplicitReceiver, 'propName') or MethodCall(ImplicitReceiver, 'methodName').
36359 //
36360 // `translate` will first try to `resolve` the outer PropertyRead/MethodCall. If this works,
36361 // it's because the `BoundTarget` found an expression target for the whole expression, and
36362 // therefore `translate` will never attempt to `resolve` the ImplicitReceiver of that
36363 // PropertyRead/MethodCall.
36364 //
36365 // Therefore if `resolve` is called on an `ImplicitReceiver`, it's because no outer
36366 // PropertyRead/MethodCall resolved to a variable or reference, and therefore this is a
36367 // property read or method call on the component context itself.
36368 return ts$1.createIdentifier('ctx');
36369 }
36370 else if (ast instanceof BindingPipe) {
36371 const expr = this.translate(ast.exp);
36372 const pipeRef = this.tcb.getPipeByName(ast.name);
36373 let pipe;
36374 if (pipeRef === null) {
36375 // No pipe by that name exists in scope. Record this as an error.
36376 this.tcb.oobRecorder.missingPipe(this.tcb.id, ast);
36377 // Use an 'any' value to at least allow the rest of the expression to be checked.
36378 pipe = NULL_AS_ANY;
36379 }
36380 else if (this.tcb.env.config.checkTypeOfPipes) {
36381 // Use a variable declared as the pipe's type.
36382 pipe = this.tcb.env.pipeInst(pipeRef);
36383 }
36384 else {
36385 // Use an 'any' value when not checking the type of the pipe.
36386 pipe = ts$1.createAsExpression(this.tcb.env.pipeInst(pipeRef), ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
36387 }
36388 const args = ast.args.map(arg => this.translate(arg));
36389 const methodAccess = ts$1.createPropertyAccess(pipe, 'transform');
36390 addParseSpanInfo(methodAccess, ast.nameSpan);
36391 const result = ts$1.createCall(
36392 /* expression */ methodAccess,
36393 /* typeArguments */ undefined,
36394 /* argumentsArray */ [expr, ...args]);
36395 addParseSpanInfo(result, ast.sourceSpan);
36396 return result;
36397 }
36398 else if (ast instanceof MethodCall && ast.receiver instanceof ImplicitReceiver &&
36399 !(ast.receiver instanceof ThisReceiver)) {
36400 // Resolve the special `$any(expr)` syntax to insert a cast of the argument to type `any`.
36401 // `$any(expr)` -> `expr as any`
36402 if (ast.name === '$any' && ast.args.length === 1) {
36403 const expr = this.translate(ast.args[0]);
36404 const exprAsAny = ts$1.createAsExpression(expr, ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
36405 const result = ts$1.createParen(exprAsAny);
36406 addParseSpanInfo(result, ast.sourceSpan);
36407 return result;
36408 }
36409 // Attempt to resolve a bound target for the method, and generate the method call if a target
36410 // could be resolved. If no target is available, then the method is referencing the top-level
36411 // component context, in which case `null` is returned to let the `ImplicitReceiver` being
36412 // resolved to the component context.
36413 const receiver = this.resolveTarget(ast);
36414 if (receiver === null) {
36415 return null;
36416 }
36417 const method = wrapForDiagnostics(receiver);
36418 addParseSpanInfo(method, ast.nameSpan);
36419 const args = ast.args.map(arg => this.translate(arg));
36420 const node = ts$1.createCall(method, undefined, args);
36421 addParseSpanInfo(node, ast.sourceSpan);
36422 return node;
36423 }
36424 else {
36425 // This AST isn't special after all.
36426 return null;
36427 }
36428 }
36429 /**
36430 * Attempts to resolve a bound target for a given expression, and translates it into the
36431 * appropriate `ts.Expression` that represents the bound target. If no target is available,
36432 * `null` is returned.
36433 */
36434 resolveTarget(ast) {
36435 const binding = this.tcb.boundTarget.getExpressionTarget(ast);
36436 if (binding === null) {
36437 return null;
36438 }
36439 const expr = this.scope.resolve(binding);
36440 addParseSpanInfo(expr, ast.sourceSpan);
36441 return expr;
36442 }
36443 }
36444 /**
36445 * Call the type constructor of a directive instance on a given template node, inferring a type for
36446 * the directive instance from any bound inputs.
36447 */
36448 function tcbCallTypeCtor(dir, tcb, inputs) {
36449 const typeCtor = tcb.env.typeCtorFor(dir);
36450 // Construct an array of `ts.PropertyAssignment`s for each of the directive's inputs.
36451 const members = inputs.map(input => {
36452 const propertyName = ts$1.createStringLiteral(input.field);
36453 if (input.type === 'binding') {
36454 // For bound inputs, the property is assigned the binding expression.
36455 let expr = input.expression;
36456 if (!tcb.env.config.checkTypeOfInputBindings) {
36457 // If checking the type of bindings is disabled, cast the resulting expression to 'any'
36458 // before the assignment.
36459 expr = tsCastToAny(expr);
36460 }
36461 else if (!tcb.env.config.strictNullInputBindings) {
36462 // If strict null checks are disabled, erase `null` and `undefined` from the type by
36463 // wrapping the expression in a non-null assertion.
36464 expr = ts$1.createNonNullExpression(expr);
36465 }
36466 const assignment = ts$1.createPropertyAssignment(propertyName, wrapForDiagnostics(expr));
36467 addParseSpanInfo(assignment, input.sourceSpan);
36468 return assignment;
36469 }
36470 else {
36471 // A type constructor is required to be called with all input properties, so any unset
36472 // inputs are simply assigned a value of type `any` to ignore them.
36473 return ts$1.createPropertyAssignment(propertyName, NULL_AS_ANY);
36474 }
36475 });
36476 // Call the `ngTypeCtor` method on the directive class, with an object literal argument created
36477 // from the matched inputs.
36478 return ts$1.createCall(
36479 /* expression */ typeCtor,
36480 /* typeArguments */ undefined,
36481 /* argumentsArray */ [ts$1.createObjectLiteral(members)]);
36482 }
36483 function getBoundInputs(directive, node, tcb) {
36484 const boundInputs = [];
36485 const processAttribute = (attr) => {
36486 // Skip non-property bindings.
36487 if (attr instanceof BoundAttribute && attr.type !== 0 /* Property */) {
36488 return;
36489 }
36490 // Skip the attribute if the directive does not have an input for it.
36491 const inputs = directive.inputs.getByBindingPropertyName(attr.name);
36492 if (inputs === null) {
36493 return;
36494 }
36495 const fieldNames = inputs.map(input => input.classPropertyName);
36496 boundInputs.push({ attribute: attr, fieldNames });
36497 };
36498 node.inputs.forEach(processAttribute);
36499 node.attributes.forEach(processAttribute);
36500 if (node instanceof Template) {
36501 node.templateAttrs.forEach(processAttribute);
36502 }
36503 return boundInputs;
36504 }
36505 /**
36506 * Translates the given attribute binding to a `ts.Expression`.
36507 */
36508 function translateInput(attr, tcb, scope) {
36509 if (attr instanceof BoundAttribute) {
36510 // Produce an expression representing the value of the binding.
36511 return tcbExpression(attr.value, tcb, scope);
36512 }
36513 else {
36514 // For regular attributes with a static string value, use the represented string literal.
36515 return ts$1.createStringLiteral(attr.value);
36516 }
36517 }
36518 const EVENT_PARAMETER = '$event';
36519 /**
36520 * Creates an arrow function to be used as handler function for event bindings. The handler
36521 * function has a single parameter `$event` and the bound event's handler `AST` represented as a
36522 * TypeScript expression as its body.
36523 *
36524 * When `eventType` is set to `Infer`, the `$event` parameter will not have an explicit type. This
36525 * allows for the created handler function to have its `$event` parameter's type inferred based on
36526 * how it's used, to enable strict type checking of event bindings. When set to `Any`, the `$event`
36527 * parameter will have an explicit `any` type, effectively disabling strict type checking of event
36528 * bindings. Alternatively, an explicit type can be passed for the `$event` parameter.
36529 */
36530 function tcbCreateEventHandler(event, tcb, scope, eventType) {
36531 const handler = tcbEventHandlerExpression(event.handler, tcb, scope);
36532 let eventParamType;
36533 if (eventType === 0 /* Infer */) {
36534 eventParamType = undefined;
36535 }
36536 else if (eventType === 1 /* Any */) {
36537 eventParamType = ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword);
36538 }
36539 else {
36540 eventParamType = eventType;
36541 }
36542 // Obtain all guards that have been applied to the scope and its parents, as they have to be
36543 // repeated within the handler function for their narrowing to be in effect within the handler.
36544 const guards = scope.guards();
36545 let body = ts$1.createExpressionStatement(handler);
36546 if (guards !== null) {
36547 // Wrap the body in an `if` statement containing all guards that have to be applied.
36548 body = ts$1.createIf(guards, body);
36549 }
36550 const eventParam = ts$1.createParameter(
36551 /* decorators */ undefined,
36552 /* modifiers */ undefined,
36553 /* dotDotDotToken */ undefined,
36554 /* name */ EVENT_PARAMETER,
36555 /* questionToken */ undefined,
36556 /* type */ eventParamType);
36557 addExpressionIdentifier(eventParam, ExpressionIdentifier.EVENT_PARAMETER);
36558 return ts$1.createFunctionExpression(
36559 /* modifier */ undefined,
36560 /* asteriskToken */ undefined,
36561 /* name */ undefined,
36562 /* typeParameters */ undefined,
36563 /* parameters */ [eventParam],
36564 /* type */ ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword),
36565 /* body */ ts$1.createBlock([body]));
36566 }
36567 /**
36568 * Similar to `tcbExpression`, this function converts the provided `AST` expression into a
36569 * `ts.Expression`, with special handling of the `$event` variable that can be used within event
36570 * bindings.
36571 */
36572 function tcbEventHandlerExpression(ast, tcb, scope) {
36573 const translator = new TcbEventHandlerTranslator(tcb, scope);
36574 return translator.translate(ast);
36575 }
36576 class TcbEventHandlerTranslator extends TcbExpressionTranslator {
36577 resolve(ast) {
36578 // Recognize a property read on the implicit receiver corresponding with the event parameter
36579 // that is available in event bindings. Since this variable is a parameter of the handler
36580 // function that the converted expression becomes a child of, just create a reference to the
36581 // parameter by its name.
36582 if (ast instanceof PropertyRead && ast.receiver instanceof ImplicitReceiver &&
36583 !(ast.receiver instanceof ThisReceiver) && ast.name === EVENT_PARAMETER) {
36584 const event = ts$1.createIdentifier(EVENT_PARAMETER);
36585 addParseSpanInfo(event, ast.nameSpan);
36586 return event;
36587 }
36588 return super.resolve(ast);
36589 }
36590 }
36591
36592 /**
36593 * @license
36594 * Copyright Google LLC All Rights Reserved.
36595 *
36596 * Use of this source code is governed by an MIT-style license that can be
36597 * found in the LICENSE file at https://angular.io/license
36598 */
36599 /**
36600 * An `Environment` representing the single type-checking file into which most (if not all) Type
36601 * Check Blocks (TCBs) will be generated.
36602 *
36603 * The `TypeCheckFile` hosts multiple TCBs and allows the sharing of declarations (e.g. type
36604 * constructors) between them. Rather than return such declarations via `getPreludeStatements()`, it
36605 * hoists them to the top of the generated `ts.SourceFile`.
36606 */
36607 class TypeCheckFile extends Environment {
36608 constructor(fileName, config, refEmitter, reflector, compilerHost) {
36609 super(config, new ImportManager(new NoopImportRewriter(), 'i'), refEmitter, reflector, ts$1.createSourceFile(compilerHost.getCanonicalFileName(fileName), '', ts$1.ScriptTarget.Latest, true));
36610 this.fileName = fileName;
36611 this.nextTcbId = 1;
36612 this.tcbStatements = [];
36613 }
36614 addTypeCheckBlock(ref, meta, domSchemaChecker, oobRecorder) {
36615 const fnId = ts$1.createIdentifier(`_tcb${this.nextTcbId++}`);
36616 const fn = generateTypeCheckBlock(this, ref, fnId, meta, domSchemaChecker, oobRecorder);
36617 this.tcbStatements.push(fn);
36618 }
36619 render() {
36620 let source = this.importManager.getAllImports(this.contextFile.fileName)
36621 .map(i => `import * as ${i.qualifier} from '${i.specifier}';`)
36622 .join('\n') +
36623 '\n\n';
36624 const printer = ts$1.createPrinter();
36625 source += '\n';
36626 for (const stmt of this.pipeInstStatements) {
36627 source += printer.printNode(ts$1.EmitHint.Unspecified, stmt, this.contextFile) + '\n';
36628 }
36629 for (const stmt of this.typeCtorStatements) {
36630 source += printer.printNode(ts$1.EmitHint.Unspecified, stmt, this.contextFile) + '\n';
36631 }
36632 source += '\n';
36633 for (const stmt of this.tcbStatements) {
36634 source += printer.printNode(ts$1.EmitHint.Unspecified, stmt, this.contextFile) + '\n';
36635 }
36636 // Ensure the template type-checking file is an ES module. Otherwise, it's interpreted as some
36637 // kind of global namespace in TS, which forces a full re-typecheck of the user's program that
36638 // is somehow more expensive than the initial parse.
36639 source += '\nexport const IS_A_MODULE = true;\n';
36640 return source;
36641 }
36642 getPreludeStatements() {
36643 return [];
36644 }
36645 }
36646
36647 /**
36648 * @license
36649 * Copyright Google LLC All Rights Reserved.
36650 *
36651 * Use of this source code is governed by an MIT-style license that can be
36652 * found in the LICENSE file at https://angular.io/license
36653 */
36654 /**
36655 * How a type-checking context should handle operations which would require inlining.
36656 */
36657 var InliningMode;
36658 (function (InliningMode) {
36659 /**
36660 * Use inlining operations when required.
36661 */
36662 InliningMode[InliningMode["InlineOps"] = 0] = "InlineOps";
36663 /**
36664 * Produce diagnostics if an operation would require inlining.
36665 */
36666 InliningMode[InliningMode["Error"] = 1] = "Error";
36667 })(InliningMode || (InliningMode = {}));
36668 /**
36669 * A template type checking context for a program.
36670 *
36671 * The `TypeCheckContext` allows registration of components and their templates which need to be
36672 * type checked.
36673 */
36674 class TypeCheckContextImpl {
36675 constructor(config, compilerHost, componentMappingStrategy, refEmitter, reflector, host, inlining) {
36676 this.config = config;
36677 this.compilerHost = compilerHost;
36678 this.componentMappingStrategy = componentMappingStrategy;
36679 this.refEmitter = refEmitter;
36680 this.reflector = reflector;
36681 this.host = host;
36682 this.inlining = inlining;
36683 this.fileMap = new Map();
36684 /**
36685 * A `Map` of `ts.SourceFile`s that the context has seen to the operations (additions of methods
36686 * or type-check blocks) that need to be eventually performed on that file.
36687 */
36688 this.opMap = new Map();
36689 /**
36690 * Tracks when an a particular class has a pending type constructor patching operation already
36691 * queued.
36692 */
36693 this.typeCtorPending = new Set();
36694 }
36695 /**
36696 * Register a template to potentially be type-checked.
36697 *
36698 * Implements `TypeCheckContext.addTemplate`.
36699 */
36700 addTemplate(ref, binder, template, pipes, schemas, sourceMapping, file, parseErrors) {
36701 if (!this.host.shouldCheckComponent(ref.node)) {
36702 return;
36703 }
36704 const fileData = this.dataForFile(ref.node.getSourceFile());
36705 const shimData = this.pendingShimForComponent(ref.node);
36706 const templateId = fileData.sourceManager.getTemplateId(ref.node);
36707 const templateDiagnostics = [];
36708 if (parseErrors !== null) {
36709 templateDiagnostics.push(...this.getTemplateDiagnostics(parseErrors, templateId, sourceMapping));
36710 }
36711 // Accumulate a list of any directives which could not have type constructors generated due to
36712 // unsupported inlining operations.
36713 let missingInlines = [];
36714 const boundTarget = binder.bind({ template });
36715 // Get all of the directives used in the template and record type constructors for all of them.
36716 for (const dir of boundTarget.getUsedDirectives()) {
36717 const dirRef = dir.ref;
36718 const dirNode = dirRef.node;
36719 if (dir.isGeneric && requiresInlineTypeCtor(dirNode, this.reflector)) {
36720 if (this.inlining === InliningMode.Error) {
36721 missingInlines.push(dirNode);
36722 continue;
36723 }
36724 // Add a type constructor operation for the directive.
36725 this.addInlineTypeCtor(fileData, dirNode.getSourceFile(), dirRef, {
36726 fnName: 'ngTypeCtor',
36727 // The constructor should have a body if the directive comes from a .ts file, but not if
36728 // it comes from a .d.ts file. .d.ts declarations don't have bodies.
36729 body: !dirNode.getSourceFile().isDeclarationFile,
36730 fields: {
36731 inputs: dir.inputs.classPropertyNames,
36732 outputs: dir.outputs.classPropertyNames,
36733 // TODO(alxhub): support queries
36734 queries: dir.queries,
36735 },
36736 coercedInputFields: dir.coercedInputFields,
36737 });
36738 }
36739 }
36740 shimData.templates.set(templateId, {
36741 template,
36742 boundTarget,
36743 templateDiagnostics,
36744 });
36745 const tcbRequiresInline = requiresInlineTypeCheckBlock(ref.node, pipes);
36746 // If inlining is not supported, but is required for either the TCB or one of its directive
36747 // dependencies, then exit here with an error.
36748 if (this.inlining === InliningMode.Error && (tcbRequiresInline || missingInlines.length > 0)) {
36749 // This template cannot be supported because the underlying strategy does not support inlining
36750 // and inlining would be required.
36751 // Record diagnostics to indicate the issues with this template.
36752 if (tcbRequiresInline) {
36753 shimData.oobRecorder.requiresInlineTcb(templateId, ref.node);
36754 }
36755 if (missingInlines.length > 0) {
36756 shimData.oobRecorder.requiresInlineTypeConstructors(templateId, ref.node, missingInlines);
36757 }
36758 // Checking this template would be unsupported, so don't try.
36759 return;
36760 }
36761 const meta = {
36762 id: fileData.sourceManager.captureSource(ref.node, sourceMapping, file),
36763 boundTarget,
36764 pipes,
36765 schemas,
36766 };
36767 if (tcbRequiresInline) {
36768 // This class didn't meet the requirements for external type checking, so generate an inline
36769 // TCB for the class.
36770 this.addInlineTypeCheckBlock(fileData, shimData, ref, meta);
36771 }
36772 else {
36773 // The class can be type-checked externally as normal.
36774 shimData.file.addTypeCheckBlock(ref, meta, shimData.domSchemaChecker, shimData.oobRecorder);
36775 }
36776 }
36777 /**
36778 * Record a type constructor for the given `node` with the given `ctorMetadata`.
36779 */
36780 addInlineTypeCtor(fileData, sf, ref, ctorMeta) {
36781 if (this.typeCtorPending.has(ref.node)) {
36782 return;
36783 }
36784 this.typeCtorPending.add(ref.node);
36785 // Lazily construct the operation map.
36786 if (!this.opMap.has(sf)) {
36787 this.opMap.set(sf, []);
36788 }
36789 const ops = this.opMap.get(sf);
36790 // Push a `TypeCtorOp` into the operation queue for the source file.
36791 ops.push(new TypeCtorOp(ref, ctorMeta));
36792 fileData.hasInlines = true;
36793 }
36794 /**
36795 * Transform a `ts.SourceFile` into a version that includes type checking code.
36796 *
36797 * If this particular `ts.SourceFile` requires changes, the text representing its new contents
36798 * will be returned. Otherwise, a `null` return indicates no changes were necessary.
36799 */
36800 transform(sf) {
36801 // If there are no operations pending for this particular file, return `null` to indicate no
36802 // changes.
36803 if (!this.opMap.has(sf)) {
36804 return null;
36805 }
36806 // Imports may need to be added to the file to support type-checking of directives used in the
36807 // template within it.
36808 const importManager = new ImportManager(new NoopImportRewriter(), '_i');
36809 // Each Op has a splitPoint index into the text where it needs to be inserted. Split the
36810 // original source text into chunks at these split points, where code will be inserted between
36811 // the chunks.
36812 const ops = this.opMap.get(sf).sort(orderOps);
36813 const textParts = splitStringAtPoints(sf.text, ops.map(op => op.splitPoint));
36814 // Use a `ts.Printer` to generate source code.
36815 const printer = ts$1.createPrinter({ omitTrailingSemicolon: true });
36816 // Begin with the intial section of the code text.
36817 let code = textParts[0];
36818 // Process each operation and use the printer to generate source code for it, inserting it into
36819 // the source code in between the original chunks.
36820 ops.forEach((op, idx) => {
36821 const text = op.execute(importManager, sf, this.refEmitter, printer);
36822 code += '\n\n' + text + textParts[idx + 1];
36823 });
36824 // Write out the imports that need to be added to the beginning of the file.
36825 let imports = importManager.getAllImports(sf.fileName)
36826 .map(i => `import * as ${i.qualifier} from '${i.specifier}';`)
36827 .join('\n');
36828 code = imports + '\n' + code;
36829 return code;
36830 }
36831 finalize() {
36832 // First, build the map of updates to source files.
36833 const updates = new Map();
36834 for (const originalSf of this.opMap.keys()) {
36835 const newText = this.transform(originalSf);
36836 if (newText !== null) {
36837 updates.set(absoluteFromSourceFile(originalSf), newText);
36838 }
36839 }
36840 // Then go through each input file that has pending code generation operations.
36841 for (const [sfPath, pendingFileData] of this.fileMap) {
36842 // For each input file, consider generation operations for each of its shims.
36843 for (const pendingShimData of pendingFileData.shimData.values()) {
36844 this.host.recordShimData(sfPath, {
36845 genesisDiagnostics: [
36846 ...pendingShimData.domSchemaChecker.diagnostics,
36847 ...pendingShimData.oobRecorder.diagnostics,
36848 ],
36849 hasInlines: pendingFileData.hasInlines,
36850 path: pendingShimData.file.fileName,
36851 templates: pendingShimData.templates,
36852 });
36853 updates.set(pendingShimData.file.fileName, pendingShimData.file.render());
36854 }
36855 }
36856 return updates;
36857 }
36858 addInlineTypeCheckBlock(fileData, shimData, ref, tcbMeta) {
36859 const sf = ref.node.getSourceFile();
36860 if (!this.opMap.has(sf)) {
36861 this.opMap.set(sf, []);
36862 }
36863 const ops = this.opMap.get(sf);
36864 ops.push(new TcbOp$1(ref, tcbMeta, this.config, this.reflector, shimData.domSchemaChecker, shimData.oobRecorder));
36865 fileData.hasInlines = true;
36866 }
36867 pendingShimForComponent(node) {
36868 const fileData = this.dataForFile(node.getSourceFile());
36869 const shimPath = this.componentMappingStrategy.shimPathForComponent(node);
36870 if (!fileData.shimData.has(shimPath)) {
36871 fileData.shimData.set(shimPath, {
36872 domSchemaChecker: new RegistryDomSchemaChecker(fileData.sourceManager),
36873 oobRecorder: new OutOfBandDiagnosticRecorderImpl(fileData.sourceManager),
36874 file: new TypeCheckFile(shimPath, this.config, this.refEmitter, this.reflector, this.compilerHost),
36875 templates: new Map(),
36876 });
36877 }
36878 return fileData.shimData.get(shimPath);
36879 }
36880 dataForFile(sf) {
36881 const sfPath = absoluteFromSourceFile(sf);
36882 if (!this.fileMap.has(sfPath)) {
36883 const data = {
36884 hasInlines: false,
36885 sourceManager: this.host.getSourceManager(sfPath),
36886 shimData: new Map(),
36887 };
36888 this.fileMap.set(sfPath, data);
36889 }
36890 return this.fileMap.get(sfPath);
36891 }
36892 getTemplateDiagnostics(parseErrors, templateId, sourceMapping) {
36893 return parseErrors.map(error => {
36894 const span = error.span;
36895 if (span.start.offset === span.end.offset) {
36896 // Template errors can contain zero-length spans, if the error occurs at a single point.
36897 // However, TypeScript does not handle displaying a zero-length diagnostic very well, so
36898 // increase the ending offset by 1 for such errors, to ensure the position is shown in the
36899 // diagnostic.
36900 span.end.offset++;
36901 }
36902 return makeTemplateDiagnostic(templateId, sourceMapping, span, ts$1.DiagnosticCategory.Error, ngErrorCode(ErrorCode.TEMPLATE_PARSE_ERROR), error.msg);
36903 });
36904 }
36905 }
36906 /**
36907 * A type check block operation which produces type check code for a particular component.
36908 */
36909 class TcbOp$1 {
36910 constructor(ref, meta, config, reflector, domSchemaChecker, oobRecorder) {
36911 this.ref = ref;
36912 this.meta = meta;
36913 this.config = config;
36914 this.reflector = reflector;
36915 this.domSchemaChecker = domSchemaChecker;
36916 this.oobRecorder = oobRecorder;
36917 }
36918 /**
36919 * Type check blocks are inserted immediately after the end of the component class.
36920 */
36921 get splitPoint() {
36922 return this.ref.node.end + 1;
36923 }
36924 execute(im, sf, refEmitter, printer) {
36925 const env = new Environment(this.config, im, refEmitter, this.reflector, sf);
36926 const fnName = ts$1.createIdentifier(`_tcb_${this.ref.node.pos}`);
36927 const fn = generateTypeCheckBlock(env, this.ref, fnName, this.meta, this.domSchemaChecker, this.oobRecorder);
36928 return printer.printNode(ts$1.EmitHint.Unspecified, fn, sf);
36929 }
36930 }
36931 /**
36932 * A type constructor operation which produces type constructor code for a particular directive.
36933 */
36934 class TypeCtorOp {
36935 constructor(ref, meta) {
36936 this.ref = ref;
36937 this.meta = meta;
36938 }
36939 /**
36940 * Type constructor operations are inserted immediately before the end of the directive class.
36941 */
36942 get splitPoint() {
36943 return this.ref.node.end - 1;
36944 }
36945 execute(im, sf, refEmitter, printer) {
36946 const tcb = generateInlineTypeCtor(this.ref.node, this.meta);
36947 return printer.printNode(ts$1.EmitHint.Unspecified, tcb, sf);
36948 }
36949 }
36950 /**
36951 * Compare two operations and return their split point ordering.
36952 */
36953 function orderOps(op1, op2) {
36954 return op1.splitPoint - op2.splitPoint;
36955 }
36956 /**
36957 * Split a string into chunks at any number of split points.
36958 */
36959 function splitStringAtPoints(str, points) {
36960 const splits = [];
36961 let start = 0;
36962 for (let i = 0; i < points.length; i++) {
36963 const point = points[i];
36964 splits.push(str.substring(start, point));
36965 start = point;
36966 }
36967 splits.push(str.substring(start));
36968 return splits;
36969 }
36970
36971 /**
36972 * @license
36973 * Copyright Google LLC All Rights Reserved.
36974 *
36975 * Use of this source code is governed by an MIT-style license that can be
36976 * found in the LICENSE file at https://angular.io/license
36977 */
36978 const LF_CHAR = 10;
36979 const CR_CHAR = 13;
36980 const LINE_SEP_CHAR = 8232;
36981 const PARAGRAPH_CHAR = 8233;
36982 /** Gets the line and character for the given position from the line starts map. */
36983 function getLineAndCharacterFromPosition(lineStartsMap, position) {
36984 const lineIndex = findClosestLineStartPosition(lineStartsMap, position);
36985 return { character: position - lineStartsMap[lineIndex], line: lineIndex };
36986 }
36987 /**
36988 * Computes the line start map of the given text. This can be used in order to
36989 * retrieve the line and character of a given text position index.
36990 */
36991 function computeLineStartsMap(text) {
36992 const result = [0];
36993 let pos = 0;
36994 while (pos < text.length) {
36995 const char = text.charCodeAt(pos++);
36996 // Handles the "CRLF" line break. In that case we peek the character
36997 // after the "CR" and check if it is a line feed.
36998 if (char === CR_CHAR) {
36999 if (text.charCodeAt(pos) === LF_CHAR) {
37000 pos++;
37001 }
37002 result.push(pos);
37003 }
37004 else if (char === LF_CHAR || char === LINE_SEP_CHAR || char === PARAGRAPH_CHAR) {
37005 result.push(pos);
37006 }
37007 }
37008 result.push(pos);
37009 return result;
37010 }
37011 /** Finds the closest line start for the given position. */
37012 function findClosestLineStartPosition(linesMap, position, low = 0, high = linesMap.length - 1) {
37013 while (low <= high) {
37014 const pivotIdx = Math.floor((low + high) / 2);
37015 const pivotEl = linesMap[pivotIdx];
37016 if (pivotEl === position) {
37017 return pivotIdx;
37018 }
37019 else if (position > pivotEl) {
37020 low = pivotIdx + 1;
37021 }
37022 else {
37023 high = pivotIdx - 1;
37024 }
37025 }
37026 // In case there was no exact match, return the closest "lower" line index. We also
37027 // subtract the index by one because want the index of the previous line start.
37028 return low - 1;
37029 }
37030
37031 /**
37032 * @license
37033 * Copyright Google LLC All Rights Reserved.
37034 *
37035 * Use of this source code is governed by an MIT-style license that can be
37036 * found in the LICENSE file at https://angular.io/license
37037 */
37038 /**
37039 * Represents the source of a template that was processed during type-checking. This information is
37040 * used when translating parse offsets in diagnostics back to their original line/column location.
37041 */
37042 class TemplateSource {
37043 constructor(mapping, file) {
37044 this.mapping = mapping;
37045 this.file = file;
37046 this.lineStarts = null;
37047 }
37048 toParseSourceSpan(start, end) {
37049 const startLoc = this.toParseLocation(start);
37050 const endLoc = this.toParseLocation(end);
37051 return new ParseSourceSpan(startLoc, endLoc);
37052 }
37053 toParseLocation(position) {
37054 const lineStarts = this.acquireLineStarts();
37055 const { line, character } = getLineAndCharacterFromPosition(lineStarts, position);
37056 return new ParseLocation(this.file, position, line, character);
37057 }
37058 acquireLineStarts() {
37059 if (this.lineStarts === null) {
37060 this.lineStarts = computeLineStartsMap(this.file.content);
37061 }
37062 return this.lineStarts;
37063 }
37064 }
37065 /**
37066 * Assigns IDs to templates and keeps track of their origins.
37067 *
37068 * Implements `TemplateSourceResolver` to resolve the source of a template based on these IDs.
37069 */
37070 class TemplateSourceManager {
37071 constructor() {
37072 /**
37073 * This map keeps track of all template sources that have been type-checked by the id that is
37074 * attached to a TCB's function declaration as leading trivia. This enables translation of
37075 * diagnostics produced for TCB code to their source location in the template.
37076 */
37077 this.templateSources = new Map();
37078 }
37079 getTemplateId(node) {
37080 return getTemplateId(node);
37081 }
37082 captureSource(node, mapping, file) {
37083 const id = getTemplateId(node);
37084 this.templateSources.set(id, new TemplateSource(mapping, file));
37085 return id;
37086 }
37087 getSourceMapping(id) {
37088 if (!this.templateSources.has(id)) {
37089 throw new Error(`Unexpected unknown template ID: ${id}`);
37090 }
37091 return this.templateSources.get(id).mapping;
37092 }
37093 toParseSourceSpan(id, span) {
37094 if (!this.templateSources.has(id)) {
37095 return null;
37096 }
37097 const templateSource = this.templateSources.get(id);
37098 return templateSource.toParseSourceSpan(span.start, span.end);
37099 }
37100 }
37101
37102 /**
37103 * @license
37104 * Copyright Google LLC All Rights Reserved.
37105 *
37106 * Use of this source code is governed by an MIT-style license that can be
37107 * found in the LICENSE file at https://angular.io/license
37108 */
37109 /**
37110 * Generates and caches `Symbol`s for various template structures for a given component.
37111 *
37112 * The `SymbolBuilder` internally caches the `Symbol`s it creates, and must be destroyed and
37113 * replaced if the component's template changes.
37114 */
37115 class SymbolBuilder {
37116 constructor(shimPath, typeCheckBlock, templateData, componentScopeReader,
37117 // The `ts.TypeChecker` depends on the current type-checking program, and so must be requested
37118 // on-demand instead of cached.
37119 getTypeChecker) {
37120 this.shimPath = shimPath;
37121 this.typeCheckBlock = typeCheckBlock;
37122 this.templateData = templateData;
37123 this.componentScopeReader = componentScopeReader;
37124 this.getTypeChecker = getTypeChecker;
37125 this.symbolCache = new Map();
37126 }
37127 getSymbol(node) {
37128 if (this.symbolCache.has(node)) {
37129 return this.symbolCache.get(node);
37130 }
37131 let symbol = null;
37132 if (node instanceof BoundAttribute || node instanceof TextAttribute) {
37133 // TODO(atscott): input and output bindings only return the first directive match but should
37134 // return a list of bindings for all of them.
37135 symbol = this.getSymbolOfInputBinding(node);
37136 }
37137 else if (node instanceof BoundEvent) {
37138 symbol = this.getSymbolOfBoundEvent(node);
37139 }
37140 else if (node instanceof Element) {
37141 symbol = this.getSymbolOfElement(node);
37142 }
37143 else if (node instanceof Template) {
37144 symbol = this.getSymbolOfAstTemplate(node);
37145 }
37146 else if (node instanceof Variable) {
37147 symbol = this.getSymbolOfVariable(node);
37148 }
37149 else if (node instanceof Reference) {
37150 symbol = this.getSymbolOfReference(node);
37151 }
37152 else if (node instanceof BindingPipe) {
37153 symbol = this.getSymbolOfPipe(node);
37154 }
37155 else if (node instanceof AST) {
37156 symbol = this.getSymbolOfTemplateExpression(node);
37157 }
37158 this.symbolCache.set(node, symbol);
37159 return symbol;
37160 }
37161 getSymbolOfAstTemplate(template) {
37162 const directives = this.getDirectivesOfNode(template);
37163 return { kind: SymbolKind.Template, directives, templateNode: template };
37164 }
37165 getSymbolOfElement(element) {
37166 var _a;
37167 const elementSourceSpan = (_a = element.startSourceSpan) !== null && _a !== void 0 ? _a : element.sourceSpan;
37168 const node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: elementSourceSpan, filter: ts$1.isVariableDeclaration });
37169 if (node === null) {
37170 return null;
37171 }
37172 const symbolFromDeclaration = this.getSymbolOfTsNode(node);
37173 if (symbolFromDeclaration === null || symbolFromDeclaration.tsSymbol === null) {
37174 return null;
37175 }
37176 const directives = this.getDirectivesOfNode(element);
37177 // All statements in the TCB are `Expression`s that optionally include more information.
37178 // An `ElementSymbol` uses the information returned for the variable declaration expression,
37179 // adds the directives for the element, and updates the `kind` to be `SymbolKind.Element`.
37180 return Object.assign(Object.assign({}, symbolFromDeclaration), { kind: SymbolKind.Element, directives, templateNode: element });
37181 }
37182 getDirectivesOfNode(element) {
37183 var _a;
37184 const elementSourceSpan = (_a = element.startSourceSpan) !== null && _a !== void 0 ? _a : element.sourceSpan;
37185 const tcbSourceFile = this.typeCheckBlock.getSourceFile();
37186 // directives could be either:
37187 // - var _t1: TestDir /*T:D*/ = (null!);
37188 // - var _t1 /*T:D*/ = _ctor1({});
37189 const isDirectiveDeclaration = (node) => (ts$1.isTypeNode(node) || ts$1.isIdentifier(node)) && ts$1.isVariableDeclaration(node.parent) &&
37190 hasExpressionIdentifier(tcbSourceFile, node, ExpressionIdentifier.DIRECTIVE);
37191 const nodes = findAllMatchingNodes(this.typeCheckBlock, { withSpan: elementSourceSpan, filter: isDirectiveDeclaration });
37192 return nodes
37193 .map(node => {
37194 var _a;
37195 const symbol = this.getSymbolOfTsNode(node.parent);
37196 if (symbol === null || symbol.tsSymbol === null ||
37197 symbol.tsSymbol.valueDeclaration === undefined ||
37198 !ts$1.isClassDeclaration(symbol.tsSymbol.valueDeclaration)) {
37199 return null;
37200 }
37201 const meta = this.getDirectiveMeta(element, symbol.tsSymbol.valueDeclaration);
37202 if (meta === null) {
37203 return null;
37204 }
37205 const ngModule = this.getDirectiveModule(symbol.tsSymbol.valueDeclaration);
37206 if (meta.selector === null) {
37207 return null;
37208 }
37209 const isComponent = (_a = meta.isComponent) !== null && _a !== void 0 ? _a : null;
37210 const directiveSymbol = Object.assign(Object.assign({}, symbol), { tsSymbol: symbol.tsSymbol, selector: meta.selector, isComponent,
37211 ngModule, kind: SymbolKind.Directive, isStructural: meta.isStructural });
37212 return directiveSymbol;
37213 })
37214 .filter((d) => d !== null);
37215 }
37216 getDirectiveMeta(host, directiveDeclaration) {
37217 var _a;
37218 const directives = this.templateData.boundTarget.getDirectivesOfNode(host);
37219 if (directives === null) {
37220 return null;
37221 }
37222 return (_a = directives.find(m => m.ref.node === directiveDeclaration)) !== null && _a !== void 0 ? _a : null;
37223 }
37224 getDirectiveModule(declaration) {
37225 const scope = this.componentScopeReader.getScopeForComponent(declaration);
37226 if (scope === null) {
37227 return null;
37228 }
37229 return scope.ngModule;
37230 }
37231 getSymbolOfBoundEvent(eventBinding) {
37232 const consumer = this.templateData.boundTarget.getConsumerOfBinding(eventBinding);
37233 if (consumer === null) {
37234 return null;
37235 }
37236 // Outputs in the TCB look like one of the two:
37237 // * _t1["outputField"].subscribe(handler);
37238 // * _t1.addEventListener(handler);
37239 // Even with strict null checks disabled, we still produce the access as a separate statement
37240 // so that it can be found here.
37241 let expectedAccess;
37242 if (consumer instanceof Template || consumer instanceof Element) {
37243 expectedAccess = 'addEventListener';
37244 }
37245 else {
37246 const bindingPropertyNames = consumer.outputs.getByBindingPropertyName(eventBinding.name);
37247 if (bindingPropertyNames === null || bindingPropertyNames.length === 0) {
37248 return null;
37249 }
37250 // Note that we only get the expectedAccess text from a single consumer of the binding. If
37251 // there are multiple consumers (not supported in the `boundTarget` API) and one of them has
37252 // an alias, it will not get matched here.
37253 expectedAccess = bindingPropertyNames[0].classPropertyName;
37254 }
37255 function filter(n) {
37256 if (!isAccessExpression(n)) {
37257 return false;
37258 }
37259 if (ts$1.isPropertyAccessExpression(n)) {
37260 return n.name.getText() === expectedAccess;
37261 }
37262 else {
37263 return ts$1.isStringLiteral(n.argumentExpression) &&
37264 n.argumentExpression.text === expectedAccess;
37265 }
37266 }
37267 const outputFieldAccesses = findAllMatchingNodes(this.typeCheckBlock, { withSpan: eventBinding.keySpan, filter });
37268 const bindings = [];
37269 for (const outputFieldAccess of outputFieldAccesses) {
37270 if (consumer instanceof Template || consumer instanceof Element) {
37271 if (!ts$1.isPropertyAccessExpression(outputFieldAccess)) {
37272 continue;
37273 }
37274 const addEventListener = outputFieldAccess.name;
37275 const tsSymbol = this.getTypeChecker().getSymbolAtLocation(addEventListener);
37276 const tsType = this.getTypeChecker().getTypeAtLocation(addEventListener);
37277 const positionInShimFile = this.getShimPositionForNode(addEventListener);
37278 const target = this.getSymbol(consumer);
37279 if (target === null || tsSymbol === undefined) {
37280 continue;
37281 }
37282 bindings.push({
37283 kind: SymbolKind.Binding,
37284 tsSymbol,
37285 tsType,
37286 target,
37287 shimLocation: { shimPath: this.shimPath, positionInShimFile },
37288 });
37289 }
37290 else {
37291 if (!ts$1.isElementAccessExpression(outputFieldAccess)) {
37292 continue;
37293 }
37294 const tsSymbol = this.getTypeChecker().getSymbolAtLocation(outputFieldAccess.argumentExpression);
37295 if (tsSymbol === undefined) {
37296 continue;
37297 }
37298 const target = this.getDirectiveSymbolForAccessExpression(outputFieldAccess, consumer);
37299 if (target === null) {
37300 continue;
37301 }
37302 const positionInShimFile = this.getShimPositionForNode(outputFieldAccess);
37303 const tsType = this.getTypeChecker().getTypeAtLocation(outputFieldAccess);
37304 bindings.push({
37305 kind: SymbolKind.Binding,
37306 tsSymbol,
37307 tsType,
37308 target,
37309 shimLocation: { shimPath: this.shimPath, positionInShimFile },
37310 });
37311 }
37312 }
37313 if (bindings.length === 0) {
37314 return null;
37315 }
37316 return { kind: SymbolKind.Output, bindings };
37317 }
37318 getSymbolOfInputBinding(binding) {
37319 const consumer = this.templateData.boundTarget.getConsumerOfBinding(binding);
37320 if (consumer === null) {
37321 return null;
37322 }
37323 if (consumer instanceof Element || consumer instanceof Template) {
37324 const host = this.getSymbol(consumer);
37325 return host !== null ? { kind: SymbolKind.DomBinding, host } : null;
37326 }
37327 const nodes = findAllMatchingNodes(this.typeCheckBlock, { withSpan: binding.sourceSpan, filter: isAssignment });
37328 const bindings = [];
37329 for (const node of nodes) {
37330 if (!isAccessExpression(node.left)) {
37331 continue;
37332 }
37333 const symbolInfo = this.getSymbolOfTsNode(node.left);
37334 if (symbolInfo === null || symbolInfo.tsSymbol === null) {
37335 continue;
37336 }
37337 const target = this.getDirectiveSymbolForAccessExpression(node.left, consumer);
37338 if (target === null) {
37339 continue;
37340 }
37341 bindings.push(Object.assign(Object.assign({}, symbolInfo), { tsSymbol: symbolInfo.tsSymbol, kind: SymbolKind.Binding, target }));
37342 }
37343 if (bindings.length === 0) {
37344 return null;
37345 }
37346 return { kind: SymbolKind.Input, bindings };
37347 }
37348 getDirectiveSymbolForAccessExpression(node, { isComponent, selector, isStructural }) {
37349 var _a;
37350 // In either case, `_t1["index"]` or `_t1.index`, `node.expression` is _t1.
37351 // The retrieved symbol for _t1 will be the variable declaration.
37352 const tsSymbol = this.getTypeChecker().getSymbolAtLocation(node.expression);
37353 if (tsSymbol === undefined || tsSymbol.declarations.length === 0 || selector === null) {
37354 return null;
37355 }
37356 const [declaration] = tsSymbol.declarations;
37357 if (!ts$1.isVariableDeclaration(declaration) ||
37358 !hasExpressionIdentifier(
37359 // The expression identifier could be on the type (for regular directives) or the name
37360 // (for generic directives and the ctor op).
37361 declaration.getSourceFile(), (_a = declaration.type) !== null && _a !== void 0 ? _a : declaration.name, ExpressionIdentifier.DIRECTIVE)) {
37362 return null;
37363 }
37364 const symbol = this.getSymbolOfTsNode(declaration);
37365 if (symbol === null || symbol.tsSymbol === null ||
37366 symbol.tsSymbol.valueDeclaration === undefined ||
37367 !ts$1.isClassDeclaration(symbol.tsSymbol.valueDeclaration)) {
37368 return null;
37369 }
37370 const ngModule = this.getDirectiveModule(symbol.tsSymbol.valueDeclaration);
37371 return {
37372 kind: SymbolKind.Directive,
37373 tsSymbol: symbol.tsSymbol,
37374 tsType: symbol.tsType,
37375 shimLocation: symbol.shimLocation,
37376 isComponent,
37377 isStructural,
37378 selector,
37379 ngModule,
37380 };
37381 }
37382 getSymbolOfVariable(variable) {
37383 const node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: variable.sourceSpan, filter: ts$1.isVariableDeclaration });
37384 if (node === null || node.initializer === undefined) {
37385 return null;
37386 }
37387 const expressionSymbol = this.getSymbolOfTsNode(node.initializer);
37388 if (expressionSymbol === null) {
37389 return null;
37390 }
37391 return {
37392 tsType: expressionSymbol.tsType,
37393 tsSymbol: expressionSymbol.tsSymbol,
37394 initializerLocation: expressionSymbol.shimLocation,
37395 kind: SymbolKind.Variable,
37396 declaration: variable,
37397 localVarLocation: {
37398 shimPath: this.shimPath,
37399 positionInShimFile: this.getShimPositionForNode(node.name),
37400 }
37401 };
37402 }
37403 getSymbolOfReference(ref) {
37404 const target = this.templateData.boundTarget.getReferenceTarget(ref);
37405 // Find the node for the reference declaration, i.e. `var _t2 = _t1;`
37406 let node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: ref.sourceSpan, filter: ts$1.isVariableDeclaration });
37407 if (node === null || target === null || node.initializer === undefined) {
37408 return null;
37409 }
37410 // Get the original declaration for the references variable, with the exception of template refs
37411 // which are of the form var _t3 = (_t2 as any as i2.TemplateRef<any>)
37412 // TODO(atscott): Consider adding an `ExpressionIdentifier` to tag variable declaration
37413 // initializers as invalid for symbol retrieval.
37414 const originalDeclaration = ts$1.isParenthesizedExpression(node.initializer) &&
37415 ts$1.isAsExpression(node.initializer.expression) ?
37416 this.getTypeChecker().getSymbolAtLocation(node.name) :
37417 this.getTypeChecker().getSymbolAtLocation(node.initializer);
37418 if (originalDeclaration === undefined || originalDeclaration.valueDeclaration === undefined) {
37419 return null;
37420 }
37421 const symbol = this.getSymbolOfTsNode(originalDeclaration.valueDeclaration);
37422 if (symbol === null || symbol.tsSymbol === null) {
37423 return null;
37424 }
37425 const referenceVarShimLocation = {
37426 shimPath: this.shimPath,
37427 positionInShimFile: this.getShimPositionForNode(node),
37428 };
37429 if (target instanceof Template || target instanceof Element) {
37430 return {
37431 kind: SymbolKind.Reference,
37432 tsSymbol: symbol.tsSymbol,
37433 tsType: symbol.tsType,
37434 target,
37435 declaration: ref,
37436 targetLocation: symbol.shimLocation,
37437 referenceVarLocation: referenceVarShimLocation,
37438 };
37439 }
37440 else {
37441 if (!ts$1.isClassDeclaration(target.directive.ref.node)) {
37442 return null;
37443 }
37444 return {
37445 kind: SymbolKind.Reference,
37446 tsSymbol: symbol.tsSymbol,
37447 tsType: symbol.tsType,
37448 declaration: ref,
37449 target: target.directive.ref.node,
37450 targetLocation: symbol.shimLocation,
37451 referenceVarLocation: referenceVarShimLocation,
37452 };
37453 }
37454 }
37455 getSymbolOfPipe(expression) {
37456 const node = findFirstMatchingNode(this.typeCheckBlock, { withSpan: expression.sourceSpan, filter: ts$1.isCallExpression });
37457 if (node === null || !ts$1.isPropertyAccessExpression(node.expression)) {
37458 return null;
37459 }
37460 const methodAccess = node.expression;
37461 // Find the node for the pipe variable from the transform property access. This will be one of
37462 // two forms: `_pipe1.transform` or `(_pipe1 as any).transform`.
37463 const pipeVariableNode = ts$1.isParenthesizedExpression(methodAccess.expression) &&
37464 ts$1.isAsExpression(methodAccess.expression.expression) ?
37465 methodAccess.expression.expression.expression :
37466 methodAccess.expression;
37467 const pipeDeclaration = this.getTypeChecker().getSymbolAtLocation(pipeVariableNode);
37468 if (pipeDeclaration === undefined || pipeDeclaration.valueDeclaration === undefined) {
37469 return null;
37470 }
37471 const pipeInstance = this.getSymbolOfTsNode(pipeDeclaration.valueDeclaration);
37472 if (pipeInstance === null || pipeInstance.tsSymbol === null) {
37473 return null;
37474 }
37475 const symbolInfo = this.getSymbolOfTsNode(methodAccess);
37476 if (symbolInfo === null) {
37477 return null;
37478 }
37479 return Object.assign(Object.assign({ kind: SymbolKind.Pipe }, symbolInfo), { classSymbol: Object.assign(Object.assign({}, pipeInstance), { tsSymbol: pipeInstance.tsSymbol }) });
37480 }
37481 getSymbolOfTemplateExpression(expression) {
37482 if (expression instanceof ASTWithSource) {
37483 expression = expression.ast;
37484 }
37485 const expressionTarget = this.templateData.boundTarget.getExpressionTarget(expression);
37486 if (expressionTarget !== null) {
37487 return this.getSymbol(expressionTarget);
37488 }
37489 // The `name` part of a `PropertyWrite` and `MethodCall` does not have its own
37490 // AST so there is no way to retrieve a `Symbol` for just the `name` via a specific node.
37491 const withSpan = (expression instanceof PropertyWrite || expression instanceof MethodCall) ?
37492 expression.nameSpan :
37493 expression.sourceSpan;
37494 let node = findFirstMatchingNode(this.typeCheckBlock, { withSpan, filter: (n) => true });
37495 if (node === null) {
37496 return null;
37497 }
37498 while (ts$1.isParenthesizedExpression(node)) {
37499 node = node.expression;
37500 }
37501 // - If we have safe property read ("a?.b") we want to get the Symbol for b, the `whenTrue`
37502 // expression.
37503 // - If our expression is a pipe binding ("a | test:b:c"), we want the Symbol for the
37504 // `transform` on the pipe.
37505 // - Otherwise, we retrieve the symbol for the node itself with no special considerations
37506 if ((expression instanceof SafePropertyRead || expression instanceof SafeMethodCall) &&
37507 ts$1.isConditionalExpression(node)) {
37508 const whenTrueSymbol = (expression instanceof SafeMethodCall && ts$1.isCallExpression(node.whenTrue)) ?
37509 this.getSymbolOfTsNode(node.whenTrue.expression) :
37510 this.getSymbolOfTsNode(node.whenTrue);
37511 if (whenTrueSymbol === null) {
37512 return null;
37513 }
37514 return Object.assign(Object.assign({}, whenTrueSymbol), { kind: SymbolKind.Expression,
37515 // Rather than using the type of only the `whenTrue` part of the expression, we should
37516 // still get the type of the whole conditional expression to include `|undefined`.
37517 tsType: this.getTypeChecker().getTypeAtLocation(node) });
37518 }
37519 else {
37520 const symbolInfo = this.getSymbolOfTsNode(node);
37521 return symbolInfo === null ? null : Object.assign(Object.assign({}, symbolInfo), { kind: SymbolKind.Expression });
37522 }
37523 }
37524 getSymbolOfTsNode(node) {
37525 var _a;
37526 while (ts$1.isParenthesizedExpression(node)) {
37527 node = node.expression;
37528 }
37529 let tsSymbol;
37530 if (ts$1.isPropertyAccessExpression(node)) {
37531 tsSymbol = this.getTypeChecker().getSymbolAtLocation(node.name);
37532 }
37533 else if (ts$1.isElementAccessExpression(node)) {
37534 tsSymbol = this.getTypeChecker().getSymbolAtLocation(node.argumentExpression);
37535 }
37536 else {
37537 tsSymbol = this.getTypeChecker().getSymbolAtLocation(node);
37538 }
37539 const positionInShimFile = this.getShimPositionForNode(node);
37540 const type = this.getTypeChecker().getTypeAtLocation(node);
37541 return {
37542 // If we could not find a symbol, fall back to the symbol on the type for the node.
37543 // Some nodes won't have a "symbol at location" but will have a symbol for the type.
37544 // Examples of this would be literals and `document.createElement('div')`.
37545 tsSymbol: (_a = tsSymbol !== null && tsSymbol !== void 0 ? tsSymbol : type.symbol) !== null && _a !== void 0 ? _a : null,
37546 tsType: type,
37547 shimLocation: { shimPath: this.shimPath, positionInShimFile },
37548 };
37549 }
37550 getShimPositionForNode(node) {
37551 if (ts$1.isTypeReferenceNode(node)) {
37552 return this.getShimPositionForNode(node.typeName);
37553 }
37554 else if (ts$1.isQualifiedName(node)) {
37555 return node.right.getStart();
37556 }
37557 else if (ts$1.isPropertyAccessExpression(node)) {
37558 return node.name.getStart();
37559 }
37560 else if (ts$1.isElementAccessExpression(node)) {
37561 return node.argumentExpression.getStart();
37562 }
37563 else {
37564 return node.getStart();
37565 }
37566 }
37567 }
37568
37569 /**
37570 * @license
37571 * Copyright Google LLC All Rights Reserved.
37572 *
37573 * Use of this source code is governed by an MIT-style license that can be
37574 * found in the LICENSE file at https://angular.io/license
37575 */
37576 const REGISTRY$1 = new DomElementSchemaRegistry();
37577 /**
37578 * Primary template type-checking engine, which performs type-checking using a
37579 * `TypeCheckingProgramStrategy` for type-checking program maintenance, and the
37580 * `ProgramTypeCheckAdapter` for generation of template type-checking code.
37581 */
37582 class TemplateTypeCheckerImpl {
37583 constructor(originalProgram, typeCheckingStrategy, typeCheckAdapter, config, refEmitter, reflector, compilerHost, priorBuild, componentScopeReader, typeCheckScopeRegistry) {
37584 this.originalProgram = originalProgram;
37585 this.typeCheckingStrategy = typeCheckingStrategy;
37586 this.typeCheckAdapter = typeCheckAdapter;
37587 this.config = config;
37588 this.refEmitter = refEmitter;
37589 this.reflector = reflector;
37590 this.compilerHost = compilerHost;
37591 this.priorBuild = priorBuild;
37592 this.componentScopeReader = componentScopeReader;
37593 this.typeCheckScopeRegistry = typeCheckScopeRegistry;
37594 this.state = new Map();
37595 /**
37596 * Stores the `CompletionEngine` which powers autocompletion for each component class.
37597 *
37598 * Must be invalidated whenever the component's template or the `ts.Program` changes. Invalidation
37599 * on template changes is performed within this `TemplateTypeCheckerImpl` instance. When the
37600 * `ts.Program` changes, the `TemplateTypeCheckerImpl` as a whole is destroyed and replaced.
37601 */
37602 this.completionCache = new Map();
37603 /**
37604 * Stores the `SymbolBuilder` which creates symbols for each component class.
37605 *
37606 * Must be invalidated whenever the component's template or the `ts.Program` changes. Invalidation
37607 * on template changes is performed within this `TemplateTypeCheckerImpl` instance. When the
37608 * `ts.Program` changes, the `TemplateTypeCheckerImpl` as a whole is destroyed and replaced.
37609 */
37610 this.symbolBuilderCache = new Map();
37611 /**
37612 * Stores directives and pipes that are in scope for each component.
37613 *
37614 * Unlike other caches, the scope of a component is not affected by its template. It will be
37615 * destroyed when the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is
37616 * destroyed and replaced.
37617 */
37618 this.scopeCache = new Map();
37619 /**
37620 * Stores potential element tags for each component (a union of DOM tags as well as directive
37621 * tags).
37622 *
37623 * Unlike other caches, the scope of a component is not affected by its template. It will be
37624 * destroyed when the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is
37625 * destroyed and replaced.
37626 */
37627 this.elementTagCache = new Map();
37628 this.isComplete = false;
37629 }
37630 getTemplate(component) {
37631 const { data } = this.getLatestComponentState(component);
37632 if (data === null) {
37633 return null;
37634 }
37635 return data.template;
37636 }
37637 getLatestComponentState(component) {
37638 this.ensureShimForComponent(component);
37639 const sf = component.getSourceFile();
37640 const sfPath = absoluteFromSourceFile(sf);
37641 const shimPath = this.typeCheckingStrategy.shimPathForComponent(component);
37642 const fileRecord = this.getFileData(sfPath);
37643 if (!fileRecord.shimData.has(shimPath)) {
37644 return { data: null, tcb: null, shimPath };
37645 }
37646 const templateId = fileRecord.sourceManager.getTemplateId(component);
37647 const shimRecord = fileRecord.shimData.get(shimPath);
37648 const id = fileRecord.sourceManager.getTemplateId(component);
37649 const program = this.typeCheckingStrategy.getProgram();
37650 const shimSf = getSourceFileOrNull(program, shimPath);
37651 if (shimSf === null || !fileRecord.shimData.has(shimPath)) {
37652 throw new Error(`Error: no shim file in program: ${shimPath}`);
37653 }
37654 let tcb = findTypeCheckBlock(shimSf, id, /*isDiagnosticsRequest*/ false);
37655 if (tcb === null) {
37656 // Try for an inline block.
37657 const inlineSf = getSourceFileOrError(program, sfPath);
37658 tcb = findTypeCheckBlock(inlineSf, id, /*isDiagnosticsRequest*/ false);
37659 }
37660 let data = null;
37661 if (shimRecord.templates.has(templateId)) {
37662 data = shimRecord.templates.get(templateId);
37663 }
37664 return { data, tcb, shimPath };
37665 }
37666 isTrackedTypeCheckFile(filePath) {
37667 return this.getFileAndShimRecordsForPath(filePath) !== null;
37668 }
37669 getFileAndShimRecordsForPath(shimPath) {
37670 for (const fileRecord of this.state.values()) {
37671 if (fileRecord.shimData.has(shimPath)) {
37672 return { fileRecord, shimRecord: fileRecord.shimData.get(shimPath) };
37673 }
37674 }
37675 return null;
37676 }
37677 getTemplateMappingAtShimLocation({ shimPath, positionInShimFile }) {
37678 const records = this.getFileAndShimRecordsForPath(absoluteFrom(shimPath));
37679 if (records === null) {
37680 return null;
37681 }
37682 const { fileRecord } = records;
37683 const shimSf = this.typeCheckingStrategy.getProgram().getSourceFile(absoluteFrom(shimPath));
37684 if (shimSf === undefined) {
37685 return null;
37686 }
37687 return getTemplateMapping(shimSf, positionInShimFile, fileRecord.sourceManager, /*isDiagnosticsRequest*/ false);
37688 }
37689 generateAllTypeCheckBlocks() {
37690 this.ensureAllShimsForAllFiles();
37691 }
37692 /**
37693 * Retrieve type-checking and template parse diagnostics from the given `ts.SourceFile` using the
37694 * most recent type-checking program.
37695 */
37696 getDiagnosticsForFile(sf, optimizeFor) {
37697 switch (optimizeFor) {
37698 case OptimizeFor.WholeProgram:
37699 this.ensureAllShimsForAllFiles();
37700 break;
37701 case OptimizeFor.SingleFile:
37702 this.ensureAllShimsForOneFile(sf);
37703 break;
37704 }
37705 const sfPath = absoluteFromSourceFile(sf);
37706 const fileRecord = this.state.get(sfPath);
37707 const typeCheckProgram = this.typeCheckingStrategy.getProgram();
37708 const diagnostics = [];
37709 if (fileRecord.hasInlines) {
37710 const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
37711 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
37712 }
37713 for (const [shimPath, shimRecord] of fileRecord.shimData) {
37714 const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
37715 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
37716 diagnostics.push(...shimRecord.genesisDiagnostics);
37717 for (const templateData of shimRecord.templates.values()) {
37718 diagnostics.push(...templateData.templateDiagnostics);
37719 }
37720 }
37721 return diagnostics.filter((diag) => diag !== null);
37722 }
37723 getDiagnosticsForComponent(component) {
37724 this.ensureShimForComponent(component);
37725 const sf = component.getSourceFile();
37726 const sfPath = absoluteFromSourceFile(sf);
37727 const shimPath = this.typeCheckingStrategy.shimPathForComponent(component);
37728 const fileRecord = this.getFileData(sfPath);
37729 if (!fileRecord.shimData.has(shimPath)) {
37730 return [];
37731 }
37732 const templateId = fileRecord.sourceManager.getTemplateId(component);
37733 const shimRecord = fileRecord.shimData.get(shimPath);
37734 const typeCheckProgram = this.typeCheckingStrategy.getProgram();
37735 const diagnostics = [];
37736 if (shimRecord.hasInlines) {
37737 const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
37738 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
37739 }
37740 const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
37741 diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
37742 diagnostics.push(...shimRecord.genesisDiagnostics);
37743 for (const templateData of shimRecord.templates.values()) {
37744 diagnostics.push(...templateData.templateDiagnostics);
37745 }
37746 return diagnostics.filter((diag) => diag !== null && diag.templateId === templateId);
37747 }
37748 getTypeCheckBlock(component) {
37749 return this.getLatestComponentState(component).tcb;
37750 }
37751 getGlobalCompletions(context, component) {
37752 const engine = this.getOrCreateCompletionEngine(component);
37753 if (engine === null) {
37754 return null;
37755 }
37756 return engine.getGlobalCompletions(context);
37757 }
37758 getExpressionCompletionLocation(ast, component) {
37759 const engine = this.getOrCreateCompletionEngine(component);
37760 if (engine === null) {
37761 return null;
37762 }
37763 return engine.getExpressionCompletionLocation(ast);
37764 }
37765 invalidateClass(clazz) {
37766 this.completionCache.delete(clazz);
37767 this.symbolBuilderCache.delete(clazz);
37768 this.scopeCache.delete(clazz);
37769 this.elementTagCache.delete(clazz);
37770 const sf = clazz.getSourceFile();
37771 const sfPath = absoluteFromSourceFile(sf);
37772 const shimPath = this.typeCheckingStrategy.shimPathForComponent(clazz);
37773 const fileData = this.getFileData(sfPath);
37774 const templateId = fileData.sourceManager.getTemplateId(clazz);
37775 fileData.shimData.delete(shimPath);
37776 fileData.isComplete = false;
37777 this.isComplete = false;
37778 }
37779 getOrCreateCompletionEngine(component) {
37780 if (this.completionCache.has(component)) {
37781 return this.completionCache.get(component);
37782 }
37783 const { tcb, data, shimPath } = this.getLatestComponentState(component);
37784 if (tcb === null || data === null) {
37785 return null;
37786 }
37787 const engine = new CompletionEngine(tcb, data, shimPath);
37788 this.completionCache.set(component, engine);
37789 return engine;
37790 }
37791 maybeAdoptPriorResultsForFile(sf) {
37792 const sfPath = absoluteFromSourceFile(sf);
37793 if (this.state.has(sfPath)) {
37794 const existingResults = this.state.get(sfPath);
37795 if (existingResults.isComplete) {
37796 // All data for this file has already been generated, so no need to adopt anything.
37797 return;
37798 }
37799 }
37800 const previousResults = this.priorBuild.priorTypeCheckingResultsFor(sf);
37801 if (previousResults === null || !previousResults.isComplete) {
37802 return;
37803 }
37804 this.state.set(sfPath, previousResults);
37805 }
37806 ensureAllShimsForAllFiles() {
37807 if (this.isComplete) {
37808 return;
37809 }
37810 const host = new WholeProgramTypeCheckingHost(this);
37811 const ctx = this.newContext(host);
37812 for (const sf of this.originalProgram.getSourceFiles()) {
37813 if (sf.isDeclarationFile || isShim(sf)) {
37814 continue;
37815 }
37816 this.maybeAdoptPriorResultsForFile(sf);
37817 const sfPath = absoluteFromSourceFile(sf);
37818 const fileData = this.getFileData(sfPath);
37819 if (fileData.isComplete) {
37820 continue;
37821 }
37822 this.typeCheckAdapter.typeCheck(sf, ctx);
37823 fileData.isComplete = true;
37824 }
37825 this.updateFromContext(ctx);
37826 this.isComplete = true;
37827 }
37828 ensureAllShimsForOneFile(sf) {
37829 this.maybeAdoptPriorResultsForFile(sf);
37830 const sfPath = absoluteFromSourceFile(sf);
37831 const fileData = this.getFileData(sfPath);
37832 if (fileData.isComplete) {
37833 // All data for this file is present and accounted for already.
37834 return;
37835 }
37836 const host = new SingleFileTypeCheckingHost(sfPath, fileData, this.typeCheckingStrategy, this);
37837 const ctx = this.newContext(host);
37838 this.typeCheckAdapter.typeCheck(sf, ctx);
37839 fileData.isComplete = true;
37840 this.updateFromContext(ctx);
37841 }
37842 ensureShimForComponent(component) {
37843 const sf = component.getSourceFile();
37844 const sfPath = absoluteFromSourceFile(sf);
37845 this.maybeAdoptPriorResultsForFile(sf);
37846 const fileData = this.getFileData(sfPath);
37847 const shimPath = this.typeCheckingStrategy.shimPathForComponent(component);
37848 if (fileData.shimData.has(shimPath)) {
37849 // All data for this component is available.
37850 return;
37851 }
37852 const host = new SingleShimTypeCheckingHost(sfPath, fileData, this.typeCheckingStrategy, this, shimPath);
37853 const ctx = this.newContext(host);
37854 this.typeCheckAdapter.typeCheck(sf, ctx);
37855 this.updateFromContext(ctx);
37856 }
37857 newContext(host) {
37858 const inlining = this.typeCheckingStrategy.supportsInlineOperations ? InliningMode.InlineOps :
37859 InliningMode.Error;
37860 return new TypeCheckContextImpl(this.config, this.compilerHost, this.typeCheckingStrategy, this.refEmitter, this.reflector, host, inlining);
37861 }
37862 /**
37863 * Remove any shim data that depends on inline operations applied to the type-checking program.
37864 *
37865 * This can be useful if new inlines need to be applied, and it's not possible to guarantee that
37866 * they won't overwrite or corrupt existing inlines that are used by such shims.
37867 */
37868 clearAllShimDataUsingInlines() {
37869 for (const fileData of this.state.values()) {
37870 if (!fileData.hasInlines) {
37871 continue;
37872 }
37873 for (const [shimFile, shimData] of fileData.shimData.entries()) {
37874 if (shimData.hasInlines) {
37875 fileData.shimData.delete(shimFile);
37876 }
37877 }
37878 fileData.hasInlines = false;
37879 fileData.isComplete = false;
37880 this.isComplete = false;
37881 }
37882 }
37883 updateFromContext(ctx) {
37884 const updates = ctx.finalize();
37885 this.typeCheckingStrategy.updateFiles(updates, UpdateMode.Incremental);
37886 this.priorBuild.recordSuccessfulTypeCheck(this.state);
37887 }
37888 getFileData(path) {
37889 if (!this.state.has(path)) {
37890 this.state.set(path, {
37891 hasInlines: false,
37892 sourceManager: new TemplateSourceManager(),
37893 isComplete: false,
37894 shimData: new Map(),
37895 });
37896 }
37897 return this.state.get(path);
37898 }
37899 getSymbolOfNode(node, component) {
37900 const builder = this.getOrCreateSymbolBuilder(component);
37901 if (builder === null) {
37902 return null;
37903 }
37904 return builder.getSymbol(node);
37905 }
37906 getOrCreateSymbolBuilder(component) {
37907 if (this.symbolBuilderCache.has(component)) {
37908 return this.symbolBuilderCache.get(component);
37909 }
37910 const { tcb, data, shimPath } = this.getLatestComponentState(component);
37911 if (tcb === null || data === null) {
37912 return null;
37913 }
37914 const builder = new SymbolBuilder(shimPath, tcb, data, this.componentScopeReader, () => this.typeCheckingStrategy.getProgram().getTypeChecker());
37915 this.symbolBuilderCache.set(component, builder);
37916 return builder;
37917 }
37918 getDirectivesInScope(component) {
37919 const data = this.getScopeData(component);
37920 if (data === null) {
37921 return null;
37922 }
37923 return data.directives;
37924 }
37925 getPipesInScope(component) {
37926 const data = this.getScopeData(component);
37927 if (data === null) {
37928 return null;
37929 }
37930 return data.pipes;
37931 }
37932 getDirectiveMetadata(dir) {
37933 if (!isNamedClassDeclaration(dir)) {
37934 return null;
37935 }
37936 return this.typeCheckScopeRegistry.getTypeCheckDirectiveMetadata(new Reference$1(dir));
37937 }
37938 getPotentialElementTags(component) {
37939 if (this.elementTagCache.has(component)) {
37940 return this.elementTagCache.get(component);
37941 }
37942 const tagMap = new Map();
37943 for (const tag of REGISTRY$1.allKnownElementNames()) {
37944 tagMap.set(tag, null);
37945 }
37946 const scope = this.getScopeData(component);
37947 if (scope !== null) {
37948 for (const directive of scope.directives) {
37949 for (const selector of CssSelector.parse(directive.selector)) {
37950 if (selector.element === null || tagMap.has(selector.element)) {
37951 // Skip this directive if it doesn't match an element tag, or if another directive has
37952 // already been included with the same element name.
37953 continue;
37954 }
37955 tagMap.set(selector.element, directive);
37956 }
37957 }
37958 }
37959 this.elementTagCache.set(component, tagMap);
37960 return tagMap;
37961 }
37962 getPotentialDomBindings(tagName) {
37963 const attributes = REGISTRY$1.allKnownAttributesOfElement(tagName);
37964 return attributes.map(attribute => ({
37965 attribute,
37966 property: REGISTRY$1.getMappedPropName(attribute),
37967 }));
37968 }
37969 getScopeData(component) {
37970 if (this.scopeCache.has(component)) {
37971 return this.scopeCache.get(component);
37972 }
37973 if (!isNamedClassDeclaration(component)) {
37974 throw new Error(`AssertionError: components must have names`);
37975 }
37976 const scope = this.componentScopeReader.getScopeForComponent(component);
37977 if (scope === null) {
37978 return null;
37979 }
37980 const data = {
37981 directives: [],
37982 pipes: [],
37983 isPoisoned: scope.compilation.isPoisoned,
37984 };
37985 const typeChecker = this.typeCheckingStrategy.getProgram().getTypeChecker();
37986 for (const dir of scope.compilation.directives) {
37987 if (dir.selector === null) {
37988 // Skip this directive, it can't be added to a template anyway.
37989 continue;
37990 }
37991 const tsSymbol = typeChecker.getSymbolAtLocation(dir.ref.node.name);
37992 if (tsSymbol === undefined) {
37993 continue;
37994 }
37995 let ngModule = null;
37996 const moduleScopeOfDir = this.componentScopeReader.getScopeForComponent(dir.ref.node);
37997 if (moduleScopeOfDir !== null) {
37998 ngModule = moduleScopeOfDir.ngModule;
37999 }
38000 data.directives.push({
38001 isComponent: dir.isComponent,
38002 isStructural: dir.isStructural,
38003 selector: dir.selector,
38004 tsSymbol,
38005 ngModule,
38006 });
38007 }
38008 for (const pipe of scope.compilation.pipes) {
38009 const tsSymbol = typeChecker.getSymbolAtLocation(pipe.ref.node.name);
38010 if (tsSymbol === undefined) {
38011 continue;
38012 }
38013 data.pipes.push({
38014 name: pipe.name,
38015 tsSymbol,
38016 });
38017 }
38018 this.scopeCache.set(component, data);
38019 return data;
38020 }
38021 }
38022 function convertDiagnostic(diag, sourceResolver) {
38023 if (!shouldReportDiagnostic(diag)) {
38024 return null;
38025 }
38026 return translateDiagnostic(diag, sourceResolver);
38027 }
38028 /**
38029 * Drives a `TypeCheckContext` to generate type-checking code for every component in the program.
38030 */
38031 class WholeProgramTypeCheckingHost {
38032 constructor(impl) {
38033 this.impl = impl;
38034 }
38035 getSourceManager(sfPath) {
38036 return this.impl.getFileData(sfPath).sourceManager;
38037 }
38038 shouldCheckComponent(node) {
38039 const fileData = this.impl.getFileData(absoluteFromSourceFile(node.getSourceFile()));
38040 const shimPath = this.impl.typeCheckingStrategy.shimPathForComponent(node);
38041 // The component needs to be checked unless the shim which would contain it already exists.
38042 return !fileData.shimData.has(shimPath);
38043 }
38044 recordShimData(sfPath, data) {
38045 const fileData = this.impl.getFileData(sfPath);
38046 fileData.shimData.set(data.path, data);
38047 if (data.hasInlines) {
38048 fileData.hasInlines = true;
38049 }
38050 }
38051 recordComplete(sfPath) {
38052 this.impl.getFileData(sfPath).isComplete = true;
38053 }
38054 }
38055 /**
38056 * Drives a `TypeCheckContext` to generate type-checking code efficiently for a single input file.
38057 */
38058 class SingleFileTypeCheckingHost {
38059 constructor(sfPath, fileData, strategy, impl) {
38060 this.sfPath = sfPath;
38061 this.fileData = fileData;
38062 this.strategy = strategy;
38063 this.impl = impl;
38064 this.seenInlines = false;
38065 }
38066 assertPath(sfPath) {
38067 if (this.sfPath !== sfPath) {
38068 throw new Error(`AssertionError: querying TypeCheckingHost outside of assigned file`);
38069 }
38070 }
38071 getSourceManager(sfPath) {
38072 this.assertPath(sfPath);
38073 return this.fileData.sourceManager;
38074 }
38075 shouldCheckComponent(node) {
38076 if (this.sfPath !== absoluteFromSourceFile(node.getSourceFile())) {
38077 return false;
38078 }
38079 const shimPath = this.strategy.shimPathForComponent(node);
38080 // Only need to generate a TCB for the class if no shim exists for it currently.
38081 return !this.fileData.shimData.has(shimPath);
38082 }
38083 recordShimData(sfPath, data) {
38084 this.assertPath(sfPath);
38085 // Previous type-checking state may have required the use of inlines (assuming they were
38086 // supported). If the current operation also requires inlines, this presents a problem:
38087 // generating new inlines may invalidate any old inlines that old state depends on.
38088 //
38089 // Rather than resolve this issue by tracking specific dependencies on inlines, if the new state
38090 // relies on inlines, any old state that relied on them is simply cleared. This happens when the
38091 // first new state that uses inlines is encountered.
38092 if (data.hasInlines && !this.seenInlines) {
38093 this.impl.clearAllShimDataUsingInlines();
38094 this.seenInlines = true;
38095 }
38096 this.fileData.shimData.set(data.path, data);
38097 if (data.hasInlines) {
38098 this.fileData.hasInlines = true;
38099 }
38100 }
38101 recordComplete(sfPath) {
38102 this.assertPath(sfPath);
38103 this.fileData.isComplete = true;
38104 }
38105 }
38106 /**
38107 * Drives a `TypeCheckContext` to generate type-checking code efficiently for only those components
38108 * which map to a single shim of a single input file.
38109 */
38110 class SingleShimTypeCheckingHost extends SingleFileTypeCheckingHost {
38111 constructor(sfPath, fileData, strategy, impl, shimPath) {
38112 super(sfPath, fileData, strategy, impl);
38113 this.shimPath = shimPath;
38114 }
38115 shouldCheckNode(node) {
38116 if (this.sfPath !== absoluteFromSourceFile(node.getSourceFile())) {
38117 return false;
38118 }
38119 // Only generate a TCB for the component if it maps to the requested shim file.
38120 const shimPath = this.strategy.shimPathForComponent(node);
38121 if (shimPath !== this.shimPath) {
38122 return false;
38123 }
38124 // Only need to generate a TCB for the class if no shim exists for it currently.
38125 return !this.fileData.shimData.has(shimPath);
38126 }
38127 }
38128
38129 /**
38130 * @license
38131 * Copyright Google LLC All Rights Reserved.
38132 *
38133 * Use of this source code is governed by an MIT-style license that can be
38134 * found in the LICENSE file at https://angular.io/license
38135 */
38136 /**
38137 * Discriminant type for a `CompilationTicket`.
38138 */
38139 var CompilationTicketKind;
38140 (function (CompilationTicketKind) {
38141 CompilationTicketKind[CompilationTicketKind["Fresh"] = 0] = "Fresh";
38142 CompilationTicketKind[CompilationTicketKind["IncrementalTypeScript"] = 1] = "IncrementalTypeScript";
38143 CompilationTicketKind[CompilationTicketKind["IncrementalResource"] = 2] = "IncrementalResource";
38144 })(CompilationTicketKind || (CompilationTicketKind = {}));
38145 /**
38146 * Create a `CompilationTicket` for a brand new compilation, using no prior state.
38147 */
38148 function freshCompilationTicket(tsProgram, options, incrementalBuildStrategy, typeCheckingProgramStrategy, enableTemplateTypeChecker, usePoisonedData) {
38149 return {
38150 kind: CompilationTicketKind.Fresh,
38151 tsProgram,
38152 options,
38153 incrementalBuildStrategy,
38154 typeCheckingProgramStrategy,
38155 enableTemplateTypeChecker,
38156 usePoisonedData,
38157 };
38158 }
38159 /**
38160 * Create a `CompilationTicket` as efficiently as possible, based on a previous `NgCompiler`
38161 * instance and a new `ts.Program`.
38162 */
38163 function incrementalFromCompilerTicket(oldCompiler, newProgram, incrementalBuildStrategy, typeCheckingProgramStrategy, modifiedResourceFiles) {
38164 const oldProgram = oldCompiler.getNextProgram();
38165 const oldDriver = oldCompiler.incrementalStrategy.getIncrementalDriver(oldProgram);
38166 if (oldDriver === null) {
38167 // No incremental step is possible here, since no IncrementalDriver was found for the old
38168 // program.
38169 return freshCompilationTicket(newProgram, oldCompiler.options, incrementalBuildStrategy, typeCheckingProgramStrategy, oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData);
38170 }
38171 const newDriver = IncrementalDriver.reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles);
38172 return {
38173 kind: CompilationTicketKind.IncrementalTypeScript,
38174 enableTemplateTypeChecker: oldCompiler.enableTemplateTypeChecker,
38175 usePoisonedData: oldCompiler.usePoisonedData,
38176 options: oldCompiler.options,
38177 incrementalBuildStrategy,
38178 typeCheckingProgramStrategy,
38179 newDriver,
38180 oldProgram,
38181 newProgram,
38182 };
38183 }
38184 function resourceChangeTicket(compiler, modifiedResourceFiles) {
38185 return {
38186 kind: CompilationTicketKind.IncrementalResource,
38187 compiler,
38188 modifiedResourceFiles,
38189 };
38190 }
38191 /**
38192 * The heart of the Angular Ivy compiler.
38193 *
38194 * The `NgCompiler` provides an API for performing Angular compilation within a custom TypeScript
38195 * compiler. Each instance of `NgCompiler` supports a single compilation, which might be
38196 * incremental.
38197 *
38198 * `NgCompiler` is lazy, and does not perform any of the work of the compilation until one of its
38199 * output methods (e.g. `getDiagnostics`) is called.
38200 *
38201 * See the README.md for more information.
38202 */
38203 class NgCompiler {
38204 constructor(adapter, options, tsProgram, typeCheckingProgramStrategy, incrementalStrategy, incrementalDriver, enableTemplateTypeChecker, usePoisonedData, perfRecorder = NOOP_PERF_RECORDER) {
38205 this.adapter = adapter;
38206 this.options = options;
38207 this.tsProgram = tsProgram;
38208 this.typeCheckingProgramStrategy = typeCheckingProgramStrategy;
38209 this.incrementalStrategy = incrementalStrategy;
38210 this.incrementalDriver = incrementalDriver;
38211 this.enableTemplateTypeChecker = enableTemplateTypeChecker;
38212 this.usePoisonedData = usePoisonedData;
38213 this.perfRecorder = perfRecorder;
38214 /**
38215 * Lazily evaluated state of the compilation.
38216 *
38217 * This is created on demand by calling `ensureAnalyzed`.
38218 */
38219 this.compilation = null;
38220 /**
38221 * Any diagnostics related to the construction of the compilation.
38222 *
38223 * These are diagnostics which arose during setup of the host and/or program.
38224 */
38225 this.constructionDiagnostics = [];
38226 /**
38227 * Non-template diagnostics related to the program itself. Does not include template
38228 * diagnostics because the template type checker memoizes them itself.
38229 *
38230 * This is set by (and memoizes) `getNonTemplateDiagnostics`.
38231 */
38232 this.nonTemplateDiagnostics = null;
38233 this.constructionDiagnostics.push(...this.adapter.constructionDiagnostics);
38234 const incompatibleTypeCheckOptionsDiagnostic = verifyCompatibleTypeCheckOptions(this.options);
38235 if (incompatibleTypeCheckOptionsDiagnostic !== null) {
38236 this.constructionDiagnostics.push(incompatibleTypeCheckOptionsDiagnostic);
38237 }
38238 this.nextProgram = tsProgram;
38239 this.closureCompilerEnabled = !!this.options.annotateForClosureCompiler;
38240 this.entryPoint =
38241 adapter.entryPoint !== null ? getSourceFileOrNull(tsProgram, adapter.entryPoint) : null;
38242 const moduleResolutionCache = ts$1.createModuleResolutionCache(this.adapter.getCurrentDirectory(),
38243 // Note: this used to be an arrow-function closure. However, JS engines like v8 have some
38244 // strange behaviors with retaining the lexical scope of the closure. Even if this function
38245 // doesn't retain a reference to `this`, if other closures in the constructor here reference
38246 // `this` internally then a closure created here would retain them. This can cause major
38247 // memory leak issues since the `moduleResolutionCache` is a long-lived object and finds its
38248 // way into all kinds of places inside TS internal objects.
38249 this.adapter.getCanonicalFileName.bind(this.adapter));
38250 this.moduleResolver =
38251 new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache);
38252 this.resourceManager = new AdapterResourceLoader(adapter, this.options);
38253 this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(this.moduleResolver));
38254 this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, tsProgram);
38255 this.ignoreForDiagnostics =
38256 new Set(tsProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf)));
38257 this.ignoreForEmit = this.adapter.ignoreForEmit;
38258 }
38259 /**
38260 * Convert a `CompilationTicket` into an `NgCompiler` instance for the requested compilation.
38261 *
38262 * Depending on the nature of the compilation request, the `NgCompiler` instance may be reused
38263 * from a previous compilation and updated with any changes, it may be a new instance which
38264 * incrementally reuses state from a previous compilation, or it may represent a fresh compilation
38265 * entirely.
38266 */
38267 static fromTicket(ticket, adapter, perfRecorder) {
38268 switch (ticket.kind) {
38269 case CompilationTicketKind.Fresh:
38270 return new NgCompiler(adapter, ticket.options, ticket.tsProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, IncrementalDriver.fresh(ticket.tsProgram), ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
38271 case CompilationTicketKind.IncrementalTypeScript:
38272 return new NgCompiler(adapter, ticket.options, ticket.newProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, ticket.newDriver, ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
38273 case CompilationTicketKind.IncrementalResource:
38274 const compiler = ticket.compiler;
38275 compiler.updateWithChangedResources(ticket.modifiedResourceFiles);
38276 return compiler;
38277 }
38278 }
38279 updateWithChangedResources(changedResources) {
38280 if (this.compilation === null) {
38281 // Analysis hasn't happened yet, so no update is necessary - any changes to resources will be
38282 // captured by the inital analysis pass itself.
38283 return;
38284 }
38285 this.resourceManager.invalidate();
38286 const classesToUpdate = new Set();
38287 for (const resourceFile of changedResources) {
38288 for (const templateClass of this.getComponentsWithTemplateFile(resourceFile)) {
38289 classesToUpdate.add(templateClass);
38290 }
38291 for (const styleClass of this.getComponentsWithStyleFile(resourceFile)) {
38292 classesToUpdate.add(styleClass);
38293 }
38294 }
38295 for (const clazz of classesToUpdate) {
38296 this.compilation.traitCompiler.updateResources(clazz);
38297 if (!ts$1.isClassDeclaration(clazz)) {
38298 continue;
38299 }
38300 this.compilation.templateTypeChecker.invalidateClass(clazz);
38301 }
38302 }
38303 /**
38304 * Get the resource dependencies of a file.
38305 *
38306 * If the file is not part of the compilation, an empty array will be returned.
38307 */
38308 getResourceDependencies(file) {
38309 this.ensureAnalyzed();
38310 return this.incrementalDriver.depGraph.getResourceDependencies(file);
38311 }
38312 /**
38313 * Get all Angular-related diagnostics for this compilation.
38314 */
38315 getDiagnostics() {
38316 return this.addMessageTextDetails([...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics()]);
38317 }
38318 /**
38319 * Get all Angular-related diagnostics for this compilation.
38320 *
38321 * If a `ts.SourceFile` is passed, only diagnostics related to that file are returned.
38322 */
38323 getDiagnosticsForFile(file, optimizeFor) {
38324 return this.addMessageTextDetails([
38325 ...this.getNonTemplateDiagnostics().filter(diag => diag.file === file),
38326 ...this.getTemplateDiagnosticsForFile(file, optimizeFor)
38327 ]);
38328 }
38329 /**
38330 * Add Angular.io error guide links to diagnostics for this compilation.
38331 */
38332 addMessageTextDetails(diagnostics) {
38333 return diagnostics.map(diag => {
38334 if (diag.code && COMPILER_ERRORS_WITH_GUIDES.has(ngErrorCode(diag.code))) {
38335 return Object.assign(Object.assign({}, diag), { messageText: diag.messageText +
38336 `. Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/NG${ngErrorCode(diag.code)}` });
38337 }
38338 return diag;
38339 });
38340 }
38341 /**
38342 * Get all setup-related diagnostics for this compilation.
38343 */
38344 getOptionDiagnostics() {
38345 return this.constructionDiagnostics;
38346 }
38347 /**
38348 * Get the `ts.Program` to use as a starting point when spawning a subsequent incremental
38349 * compilation.
38350 *
38351 * The `NgCompiler` spawns an internal incremental TypeScript compilation (inheriting the
38352 * consumer's `ts.Program` into a new one for the purposes of template type-checking). After this
38353 * operation, the consumer's `ts.Program` is no longer usable for starting a new incremental
38354 * compilation. `getNextProgram` retrieves the `ts.Program` which can be used instead.
38355 */
38356 getNextProgram() {
38357 return this.nextProgram;
38358 }
38359 getTemplateTypeChecker() {
38360 if (!this.enableTemplateTypeChecker) {
38361 throw new Error('The `TemplateTypeChecker` does not work without `enableTemplateTypeChecker`.');
38362 }
38363 return this.ensureAnalyzed().templateTypeChecker;
38364 }
38365 /**
38366 * Retrieves the `ts.Declaration`s for any component(s) which use the given template file.
38367 */
38368 getComponentsWithTemplateFile(templateFilePath) {
38369 const { resourceRegistry } = this.ensureAnalyzed();
38370 return resourceRegistry.getComponentsWithTemplate(resolve(templateFilePath));
38371 }
38372 /**
38373 * Retrieves the `ts.Declaration`s for any component(s) which use the given template file.
38374 */
38375 getComponentsWithStyleFile(styleFilePath) {
38376 const { resourceRegistry } = this.ensureAnalyzed();
38377 return resourceRegistry.getComponentsWithStyle(resolve(styleFilePath));
38378 }
38379 /**
38380 * Retrieves external resources for the given component.
38381 */
38382 getComponentResources(classDecl) {
38383 if (!isNamedClassDeclaration(classDecl)) {
38384 return null;
38385 }
38386 const { resourceRegistry } = this.ensureAnalyzed();
38387 const styles = resourceRegistry.getStyles(classDecl);
38388 const template = resourceRegistry.getTemplate(classDecl);
38389 if (template === null) {
38390 return null;
38391 }
38392 return { styles, template };
38393 }
38394 /**
38395 * Perform Angular's analysis step (as a precursor to `getDiagnostics` or `prepareEmit`)
38396 * asynchronously.
38397 *
38398 * Normally, this operation happens lazily whenever `getDiagnostics` or `prepareEmit` are called.
38399 * However, certain consumers may wish to allow for an asynchronous phase of analysis, where
38400 * resources such as `styleUrls` are resolved asynchonously. In these cases `analyzeAsync` must be
38401 * called first, and its `Promise` awaited prior to calling any other APIs of `NgCompiler`.
38402 */
38403 analyzeAsync() {
38404 return __awaiter(this, void 0, void 0, function* () {
38405 if (this.compilation !== null) {
38406 return;
38407 }
38408 this.compilation = this.makeCompilation();
38409 const analyzeSpan = this.perfRecorder.start('analyze');
38410 const promises = [];
38411 for (const sf of this.tsProgram.getSourceFiles()) {
38412 if (sf.isDeclarationFile) {
38413 continue;
38414 }
38415 const analyzeFileSpan = this.perfRecorder.start('analyzeFile', sf);
38416 let analysisPromise = this.compilation.traitCompiler.analyzeAsync(sf);
38417 this.scanForMwp(sf);
38418 if (analysisPromise === undefined) {
38419 this.perfRecorder.stop(analyzeFileSpan);
38420 }
38421 else if (this.perfRecorder.enabled) {
38422 analysisPromise = analysisPromise.then(() => this.perfRecorder.stop(analyzeFileSpan));
38423 }
38424 if (analysisPromise !== undefined) {
38425 promises.push(analysisPromise);
38426 }
38427 }
38428 yield Promise.all(promises);
38429 this.perfRecorder.stop(analyzeSpan);
38430 this.resolveCompilation(this.compilation.traitCompiler);
38431 });
38432 }
38433 /**
38434 * List lazy routes detected during analysis.
38435 *
38436 * This can be called for one specific route, or to retrieve all top-level routes.
38437 */
38438 listLazyRoutes(entryRoute) {
38439 if (entryRoute) {
38440 // Note:
38441 // This resolution step is here to match the implementation of the old `AotCompilerHost` (see
38442 // https://github.com/angular/angular/blob/50732e156/packages/compiler-cli/src/transformers/compiler_host.ts#L175-L188).
38443 //
38444 // `@angular/cli` will always call this API with an absolute path, so the resolution step is
38445 // not necessary, but keeping it backwards compatible in case someone else is using the API.
38446 // Relative entry paths are disallowed.
38447 if (entryRoute.startsWith('.')) {
38448 throw new Error(`Failed to list lazy routes: Resolution of relative paths (${entryRoute}) is not supported.`);
38449 }
38450 // Non-relative entry paths fall into one of the following categories:
38451 // - Absolute system paths (e.g. `/foo/bar/my-project/my-module`), which are unaffected by the
38452 // logic below.
38453 // - Paths to enternal modules (e.g. `some-lib`).
38454 // - Paths mapped to directories in `tsconfig.json` (e.g. `shared/my-module`).
38455 // (See https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping.)
38456 //
38457 // In all cases above, the `containingFile` argument is ignored, so we can just take the first
38458 // of the root files.
38459 const containingFile = this.tsProgram.getRootFileNames()[0];
38460 const [entryPath, moduleName] = entryRoute.split('#');
38461 const resolvedModule = resolveModuleName(entryPath, containingFile, this.options, this.adapter, null);
38462 if (resolvedModule) {
38463 entryRoute = entryPointKeyFor(resolvedModule.resolvedFileName, moduleName);
38464 }
38465 }
38466 const compilation = this.ensureAnalyzed();
38467 return compilation.routeAnalyzer.listLazyRoutes(entryRoute);
38468 }
38469 /**
38470 * Fetch transformers and other information which is necessary for a consumer to `emit` the
38471 * program with Angular-added definitions.
38472 */
38473 prepareEmit() {
38474 const compilation = this.ensureAnalyzed();
38475 const coreImportsFrom = compilation.isCore ? getR3SymbolsFile(this.tsProgram) : null;
38476 let importRewriter;
38477 if (coreImportsFrom !== null) {
38478 importRewriter = new R3SymbolsImportRewriter(coreImportsFrom.fileName);
38479 }
38480 else {
38481 importRewriter = new NoopImportRewriter();
38482 }
38483 const before = [
38484 ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, compilation.defaultImportTracker, compilation.isCore, this.closureCompilerEnabled),
38485 aliasTransformFactory(compilation.traitCompiler.exportStatements),
38486 compilation.defaultImportTracker.importPreservingTransformer(),
38487 ];
38488 const afterDeclarations = [];
38489 if (compilation.dtsTransforms !== null) {
38490 afterDeclarations.push(declarationTransformFactory(compilation.dtsTransforms, importRewriter));
38491 }
38492 // Only add aliasing re-exports to the .d.ts output if the `AliasingHost` requests it.
38493 if (compilation.aliasingHost !== null && compilation.aliasingHost.aliasExportsInDts) {
38494 afterDeclarations.push(aliasTransformFactory(compilation.traitCompiler.exportStatements));
38495 }
38496 if (this.adapter.factoryTracker !== null) {
38497 before.push(generatedFactoryTransform(this.adapter.factoryTracker.sourceInfo, importRewriter));
38498 }
38499 before.push(ivySwitchTransform);
38500 return { transformers: { before, afterDeclarations } };
38501 }
38502 /**
38503 * Run the indexing process and return a `Map` of all indexed components.
38504 *
38505 * See the `indexing` package for more details.
38506 */
38507 getIndexedComponents() {
38508 const compilation = this.ensureAnalyzed();
38509 const context = new IndexingContext();
38510 compilation.traitCompiler.index(context);
38511 return generateAnalysis(context);
38512 }
38513 ensureAnalyzed() {
38514 if (this.compilation === null) {
38515 this.analyzeSync();
38516 }
38517 return this.compilation;
38518 }
38519 analyzeSync() {
38520 const analyzeSpan = this.perfRecorder.start('analyze');
38521 this.compilation = this.makeCompilation();
38522 for (const sf of this.tsProgram.getSourceFiles()) {
38523 if (sf.isDeclarationFile) {
38524 continue;
38525 }
38526 const analyzeFileSpan = this.perfRecorder.start('analyzeFile', sf);
38527 this.compilation.traitCompiler.analyzeSync(sf);
38528 this.scanForMwp(sf);
38529 this.perfRecorder.stop(analyzeFileSpan);
38530 }
38531 this.perfRecorder.stop(analyzeSpan);
38532 this.resolveCompilation(this.compilation.traitCompiler);
38533 }
38534 resolveCompilation(traitCompiler) {
38535 traitCompiler.resolve();
38536 this.recordNgModuleScopeDependencies();
38537 // At this point, analysis is complete and the compiler can now calculate which files need to
38538 // be emitted, so do that.
38539 this.incrementalDriver.recordSuccessfulAnalysis(traitCompiler);
38540 }
38541 get fullTemplateTypeCheck() {
38542 // Determine the strictness level of type checking based on compiler options. As
38543 // `strictTemplates` is a superset of `fullTemplateTypeCheck`, the former implies the latter.
38544 // Also see `verifyCompatibleTypeCheckOptions` where it is verified that `fullTemplateTypeCheck`
38545 // is not disabled when `strictTemplates` is enabled.
38546 const strictTemplates = !!this.options.strictTemplates;
38547 return strictTemplates || !!this.options.fullTemplateTypeCheck;
38548 }
38549 getTypeCheckingConfig() {
38550 // Determine the strictness level of type checking based on compiler options. As
38551 // `strictTemplates` is a superset of `fullTemplateTypeCheck`, the former implies the latter.
38552 // Also see `verifyCompatibleTypeCheckOptions` where it is verified that `fullTemplateTypeCheck`
38553 // is not disabled when `strictTemplates` is enabled.
38554 const strictTemplates = !!this.options.strictTemplates;
38555 // First select a type-checking configuration, based on whether full template type-checking is
38556 // requested.
38557 let typeCheckingConfig;
38558 if (this.fullTemplateTypeCheck) {
38559 typeCheckingConfig = {
38560 applyTemplateContextGuards: strictTemplates,
38561 checkQueries: false,
38562 checkTemplateBodies: true,
38563 alwaysCheckSchemaInTemplateBodies: true,
38564 checkTypeOfInputBindings: strictTemplates,
38565 honorAccessModifiersForInputBindings: false,
38566 strictNullInputBindings: strictTemplates,
38567 checkTypeOfAttributes: strictTemplates,
38568 // Even in full template type-checking mode, DOM binding checks are not quite ready yet.
38569 checkTypeOfDomBindings: false,
38570 checkTypeOfOutputEvents: strictTemplates,
38571 checkTypeOfAnimationEvents: strictTemplates,
38572 // Checking of DOM events currently has an adverse effect on developer experience,
38573 // e.g. for `<input (blur)="update($event.target.value)">` enabling this check results in:
38574 // - error TS2531: Object is possibly 'null'.
38575 // - error TS2339: Property 'value' does not exist on type 'EventTarget'.
38576 checkTypeOfDomEvents: strictTemplates,
38577 checkTypeOfDomReferences: strictTemplates,
38578 // Non-DOM references have the correct type in View Engine so there is no strictness flag.
38579 checkTypeOfNonDomReferences: true,
38580 // Pipes are checked in View Engine so there is no strictness flag.
38581 checkTypeOfPipes: true,
38582 strictSafeNavigationTypes: strictTemplates,
38583 useContextGenericType: strictTemplates,
38584 strictLiteralTypes: true,
38585 enableTemplateTypeChecker: this.enableTemplateTypeChecker,
38586 };
38587 }
38588 else {
38589 typeCheckingConfig = {
38590 applyTemplateContextGuards: false,
38591 checkQueries: false,
38592 checkTemplateBodies: false,
38593 // Enable deep schema checking in "basic" template type-checking mode only if Closure
38594 // compilation is requested, which is a good proxy for "only in google3".
38595 alwaysCheckSchemaInTemplateBodies: this.closureCompilerEnabled,
38596 checkTypeOfInputBindings: false,
38597 strictNullInputBindings: false,
38598 honorAccessModifiersForInputBindings: false,
38599 checkTypeOfAttributes: false,
38600 checkTypeOfDomBindings: false,
38601 checkTypeOfOutputEvents: false,
38602 checkTypeOfAnimationEvents: false,
38603 checkTypeOfDomEvents: false,
38604 checkTypeOfDomReferences: false,
38605 checkTypeOfNonDomReferences: false,
38606 checkTypeOfPipes: false,
38607 strictSafeNavigationTypes: false,
38608 useContextGenericType: false,
38609 strictLiteralTypes: false,
38610 enableTemplateTypeChecker: this.enableTemplateTypeChecker,
38611 };
38612 }
38613 // Apply explicitly configured strictness flags on top of the default configuration
38614 // based on "fullTemplateTypeCheck".
38615 if (this.options.strictInputTypes !== undefined) {
38616 typeCheckingConfig.checkTypeOfInputBindings = this.options.strictInputTypes;
38617 typeCheckingConfig.applyTemplateContextGuards = this.options.strictInputTypes;
38618 }
38619 if (this.options.strictInputAccessModifiers !== undefined) {
38620 typeCheckingConfig.honorAccessModifiersForInputBindings =
38621 this.options.strictInputAccessModifiers;
38622 }
38623 if (this.options.strictNullInputTypes !== undefined) {
38624 typeCheckingConfig.strictNullInputBindings = this.options.strictNullInputTypes;
38625 }
38626 if (this.options.strictOutputEventTypes !== undefined) {
38627 typeCheckingConfig.checkTypeOfOutputEvents = this.options.strictOutputEventTypes;
38628 typeCheckingConfig.checkTypeOfAnimationEvents = this.options.strictOutputEventTypes;
38629 }
38630 if (this.options.strictDomEventTypes !== undefined) {
38631 typeCheckingConfig.checkTypeOfDomEvents = this.options.strictDomEventTypes;
38632 }
38633 if (this.options.strictSafeNavigationTypes !== undefined) {
38634 typeCheckingConfig.strictSafeNavigationTypes = this.options.strictSafeNavigationTypes;
38635 }
38636 if (this.options.strictDomLocalRefTypes !== undefined) {
38637 typeCheckingConfig.checkTypeOfDomReferences = this.options.strictDomLocalRefTypes;
38638 }
38639 if (this.options.strictAttributeTypes !== undefined) {
38640 typeCheckingConfig.checkTypeOfAttributes = this.options.strictAttributeTypes;
38641 }
38642 if (this.options.strictContextGenerics !== undefined) {
38643 typeCheckingConfig.useContextGenericType = this.options.strictContextGenerics;
38644 }
38645 if (this.options.strictLiteralTypes !== undefined) {
38646 typeCheckingConfig.strictLiteralTypes = this.options.strictLiteralTypes;
38647 }
38648 return typeCheckingConfig;
38649 }
38650 getTemplateDiagnostics() {
38651 const compilation = this.ensureAnalyzed();
38652 // Get the diagnostics.
38653 const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics');
38654 const diagnostics = [];
38655 for (const sf of this.tsProgram.getSourceFiles()) {
38656 if (sf.isDeclarationFile || this.adapter.isShim(sf)) {
38657 continue;
38658 }
38659 diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
38660 }
38661 const program = this.typeCheckingProgramStrategy.getProgram();
38662 this.perfRecorder.stop(typeCheckSpan);
38663 this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, program);
38664 this.nextProgram = program;
38665 return diagnostics;
38666 }
38667 getTemplateDiagnosticsForFile(sf, optimizeFor) {
38668 const compilation = this.ensureAnalyzed();
38669 // Get the diagnostics.
38670 const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics');
38671 const diagnostics = [];
38672 if (!sf.isDeclarationFile && !this.adapter.isShim(sf)) {
38673 diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, optimizeFor));
38674 }
38675 const program = this.typeCheckingProgramStrategy.getProgram();
38676 this.perfRecorder.stop(typeCheckSpan);
38677 this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, program);
38678 this.nextProgram = program;
38679 return diagnostics;
38680 }
38681 getNonTemplateDiagnostics() {
38682 if (this.nonTemplateDiagnostics === null) {
38683 const compilation = this.ensureAnalyzed();
38684 this.nonTemplateDiagnostics = [...compilation.traitCompiler.diagnostics];
38685 if (this.entryPoint !== null && compilation.exportReferenceGraph !== null) {
38686 this.nonTemplateDiagnostics.push(...checkForPrivateExports(this.entryPoint, this.tsProgram.getTypeChecker(), compilation.exportReferenceGraph));
38687 }
38688 }
38689 return this.nonTemplateDiagnostics;
38690 }
38691 /**
38692 * Reifies the inter-dependencies of NgModules and the components within their compilation scopes
38693 * into the `IncrementalDriver`'s dependency graph.
38694 */
38695 recordNgModuleScopeDependencies() {
38696 const recordSpan = this.perfRecorder.start('recordDependencies');
38697 const depGraph = this.incrementalDriver.depGraph;
38698 for (const scope of this.compilation.scopeRegistry.getCompilationScopes()) {
38699 const file = scope.declaration.getSourceFile();
38700 const ngModuleFile = scope.ngModule.getSourceFile();
38701 // A change to any dependency of the declaration causes the declaration to be invalidated,
38702 // which requires the NgModule to be invalidated as well.
38703 depGraph.addTransitiveDependency(ngModuleFile, file);
38704 // A change to the NgModule file should cause the declaration itself to be invalidated.
38705 depGraph.addDependency(file, ngModuleFile);
38706 const meta = this.compilation.metaReader.getDirectiveMetadata(new Reference$1(scope.declaration));
38707 if (meta !== null && meta.isComponent) {
38708 // If a component's template changes, it might have affected the import graph, and thus the
38709 // remote scoping feature which is activated in the event of potential import cycles. Thus,
38710 // the module depends not only on the transitive dependencies of the component, but on its
38711 // resources as well.
38712 depGraph.addTransitiveResources(ngModuleFile, file);
38713 // A change to any directive/pipe in the compilation scope should cause the component to be
38714 // invalidated.
38715 for (const directive of scope.directives) {
38716 // When a directive in scope is updated, the component needs to be recompiled as e.g. a
38717 // selector may have changed.
38718 depGraph.addTransitiveDependency(file, directive.ref.node.getSourceFile());
38719 }
38720 for (const pipe of scope.pipes) {
38721 // When a pipe in scope is updated, the component needs to be recompiled as e.g. the
38722 // pipe's name may have changed.
38723 depGraph.addTransitiveDependency(file, pipe.ref.node.getSourceFile());
38724 }
38725 // Components depend on the entire export scope. In addition to transitive dependencies on
38726 // all directives/pipes in the export scope, they also depend on every NgModule in the
38727 // scope, as changes to a module may add new directives/pipes to the scope.
38728 for (const depModule of scope.ngModules) {
38729 // There is a correctness issue here. To be correct, this should be a transitive
38730 // dependency on the depModule file, since the depModule's exports might change via one of
38731 // its dependencies, even if depModule's file itself doesn't change. However, doing this
38732 // would also trigger recompilation if a non-exported component or directive changed,
38733 // which causes performance issues for rebuilds.
38734 //
38735 // Given the rebuild issue is an edge case, currently we err on the side of performance
38736 // instead of correctness. A correct and performant design would distinguish between
38737 // changes to the depModule which affect its export scope and changes which do not, and
38738 // only add a dependency for the former. This concept is currently in development.
38739 //
38740 // TODO(alxhub): fix correctness issue by understanding the semantics of the dependency.
38741 depGraph.addDependency(file, depModule.getSourceFile());
38742 }
38743 }
38744 else {
38745 // Directives (not components) and pipes only depend on the NgModule which directly declares
38746 // them.
38747 depGraph.addDependency(file, ngModuleFile);
38748 }
38749 }
38750 this.perfRecorder.stop(recordSpan);
38751 }
38752 scanForMwp(sf) {
38753 this.compilation.mwpScanner.scan(sf, {
38754 addTypeReplacement: (node, type) => {
38755 // Only obtain the return type transform for the source file once there's a type to replace,
38756 // so that no transform is allocated when there's nothing to do.
38757 this.compilation.dtsTransforms.getReturnTypeTransform(sf).addTypeReplacement(node, type);
38758 }
38759 });
38760 }
38761 makeCompilation() {
38762 const checker = this.tsProgram.getTypeChecker();
38763 const reflector = new TypeScriptReflectionHost(checker);
38764 // Construct the ReferenceEmitter.
38765 let refEmitter;
38766 let aliasingHost = null;
38767 if (this.adapter.unifiedModulesHost === null || !this.options._useHostForImportGeneration) {
38768 let localImportStrategy;
38769 // The strategy used for local, in-project imports depends on whether TS has been configured
38770 // with rootDirs. If so, then multiple directories may be mapped in the same "module
38771 // namespace" and the logic of `LogicalProjectStrategy` is required to generate correct
38772 // imports which may cross these multiple directories. Otherwise, plain relative imports are
38773 // sufficient.
38774 if (this.options.rootDir !== undefined ||
38775 (this.options.rootDirs !== undefined && this.options.rootDirs.length > 0)) {
38776 // rootDirs logic is in effect - use the `LogicalProjectStrategy` for in-project relative
38777 // imports.
38778 localImportStrategy = new LogicalProjectStrategy(reflector, new LogicalFileSystem([...this.adapter.rootDirs], this.adapter));
38779 }
38780 else {
38781 // Plain relative imports are all that's needed.
38782 localImportStrategy = new RelativePathStrategy(reflector);
38783 }
38784 // The CompilerHost doesn't have fileNameToModuleName, so build an NPM-centric reference
38785 // resolution strategy.
38786 refEmitter = new ReferenceEmitter([
38787 // First, try to use local identifiers if available.
38788 new LocalIdentifierStrategy(),
38789 // Next, attempt to use an absolute import.
38790 new AbsoluteModuleStrategy(this.tsProgram, checker, this.moduleResolver, reflector),
38791 // Finally, check if the reference is being written into a file within the project's .ts
38792 // sources, and use a relative import if so. If this fails, ReferenceEmitter will throw
38793 // an error.
38794 localImportStrategy,
38795 ]);
38796 // If an entrypoint is present, then all user imports should be directed through the
38797 // entrypoint and private exports are not needed. The compiler will validate that all publicly
38798 // visible directives/pipes are importable via this entrypoint.
38799 if (this.entryPoint === null && this.options.generateDeepReexports === true) {
38800 // No entrypoint is present and deep re-exports were requested, so configure the aliasing
38801 // system to generate them.
38802 aliasingHost = new PrivateExportAliasingHost(reflector);
38803 }
38804 }
38805 else {
38806 // The CompilerHost supports fileNameToModuleName, so use that to emit imports.
38807 refEmitter = new ReferenceEmitter([
38808 // First, try to use local identifiers if available.
38809 new LocalIdentifierStrategy(),
38810 // Then use aliased references (this is a workaround to StrictDeps checks).
38811 new AliasStrategy(),
38812 // Then use fileNameToModuleName to emit imports.
38813 new UnifiedModulesStrategy(reflector, this.adapter.unifiedModulesHost),
38814 ]);
38815 aliasingHost = new UnifiedModulesAliasingHost(this.adapter.unifiedModulesHost);
38816 }
38817 const evaluator = new PartialEvaluator(reflector, checker, this.incrementalDriver.depGraph);
38818 const dtsReader = new DtsMetadataReader(checker, reflector);
38819 const localMetaRegistry = new LocalMetadataRegistry();
38820 const localMetaReader = localMetaRegistry;
38821 const depScopeReader = new MetadataDtsModuleScopeResolver(dtsReader, aliasingHost);
38822 const scopeRegistry = new LocalModuleScopeRegistry(localMetaReader, depScopeReader, refEmitter, aliasingHost);
38823 const scopeReader = scopeRegistry;
38824 const metaRegistry = new CompoundMetadataRegistry([localMetaRegistry, scopeRegistry]);
38825 const injectableRegistry = new InjectableClassRegistry(reflector);
38826 const metaReader = new CompoundMetadataReader([localMetaReader, dtsReader]);
38827 const typeCheckScopeRegistry = new TypeCheckScopeRegistry(scopeReader, metaReader);
38828 // If a flat module entrypoint was specified, then track references via a `ReferenceGraph` in
38829 // order to produce proper diagnostics for incorrectly exported directives/pipes/etc. If there
38830 // is no flat module entrypoint then don't pay the cost of tracking references.
38831 let referencesRegistry;
38832 let exportReferenceGraph = null;
38833 if (this.entryPoint !== null) {
38834 exportReferenceGraph = new ReferenceGraph();
38835 referencesRegistry = new ReferenceGraphAdapter(exportReferenceGraph);
38836 }
38837 else {
38838 referencesRegistry = new NoopReferencesRegistry();
38839 }
38840 const routeAnalyzer = new NgModuleRouteAnalyzer(this.moduleResolver, evaluator);
38841 const dtsTransforms = new DtsTransformRegistry();
38842 const mwpScanner = new ModuleWithProvidersScanner(reflector, evaluator, refEmitter);
38843 const isCore = isAngularCorePackage(this.tsProgram);
38844 const defaultImportTracker = new DefaultImportTracker();
38845 const resourceRegistry = new ResourceRegistry();
38846 // Set up the IvyCompilation, which manages state for the Ivy transformer.
38847 const handlers = [
38848 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, refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, injectableRegistry, this.closureCompilerEnabled),
38849 // TODO(alxhub): understand why the cast here is necessary (something to do with `null`
38850 // not being assignable to `unknown` when wrapped in `Readonly`).
38851 // clang-format off
38852 new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportTracker, injectableRegistry, isCore, this.closureCompilerEnabled,
38853 // In ngtsc we no longer want to compile undecorated classes with Angular features.
38854 // Migrations for these patterns ran as part of `ng update` and we want to ensure
38855 // that projects do not regress. See https://hackmd.io/@alx/ryfYYuvzH for more details.
38856 /* compileUndecoratedClassesWithAngularFeatures */ false),
38857 // clang-format on
38858 // Pipe handler must be before injectable handler in list so pipe factories are printed
38859 // before injectable factories (so injectable factories can delegate to them)
38860 new PipeDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportTracker, injectableRegistry, isCore),
38861 new InjectableDecoratorHandler(reflector, defaultImportTracker, isCore, this.options.strictInjectionParameters || false, injectableRegistry),
38862 new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, this.adapter.factoryTracker, defaultImportTracker, this.closureCompilerEnabled, injectableRegistry, this.options.i18nInLocale),
38863 ];
38864 const compilationMode = this.options.compilationMode === 'partial' ? CompilationMode.PARTIAL : CompilationMode.FULL;
38865 const traitCompiler = new TraitCompiler(handlers, reflector, this.perfRecorder, this.incrementalDriver, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms);
38866 const templateTypeChecker = new TemplateTypeCheckerImpl(this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver, scopeRegistry, typeCheckScopeRegistry);
38867 return {
38868 isCore,
38869 traitCompiler,
38870 reflector,
38871 scopeRegistry,
38872 dtsTransforms,
38873 exportReferenceGraph,
38874 routeAnalyzer,
38875 mwpScanner,
38876 metaReader,
38877 typeCheckScopeRegistry,
38878 defaultImportTracker,
38879 aliasingHost,
38880 refEmitter,
38881 templateTypeChecker,
38882 resourceRegistry,
38883 };
38884 }
38885 }
38886 /**
38887 * Determine if the given `Program` is @angular/core.
38888 */
38889 function isAngularCorePackage(program) {
38890 // Look for its_just_angular.ts somewhere in the program.
38891 const r3Symbols = getR3SymbolsFile(program);
38892 if (r3Symbols === null) {
38893 return false;
38894 }
38895 // Look for the constant ITS_JUST_ANGULAR in that file.
38896 return r3Symbols.statements.some(stmt => {
38897 // The statement must be a variable declaration statement.
38898 if (!ts$1.isVariableStatement(stmt)) {
38899 return false;
38900 }
38901 // It must be exported.
38902 if (stmt.modifiers === undefined ||
38903 !stmt.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.ExportKeyword)) {
38904 return false;
38905 }
38906 // It must declare ITS_JUST_ANGULAR.
38907 return stmt.declarationList.declarations.some(decl => {
38908 // The declaration must match the name.
38909 if (!ts$1.isIdentifier(decl.name) || decl.name.text !== 'ITS_JUST_ANGULAR') {
38910 return false;
38911 }
38912 // It must initialize the variable to true.
38913 if (decl.initializer === undefined || decl.initializer.kind !== ts$1.SyntaxKind.TrueKeyword) {
38914 return false;
38915 }
38916 // This definition matches.
38917 return true;
38918 });
38919 });
38920 }
38921 /**
38922 * Find the 'r3_symbols.ts' file in the given `Program`, or return `null` if it wasn't there.
38923 */
38924 function getR3SymbolsFile(program) {
38925 return program.getSourceFiles().find(file => file.fileName.indexOf('r3_symbols.ts') >= 0) || null;
38926 }
38927 /**
38928 * Since "strictTemplates" is a true superset of type checking capabilities compared to
38929 * "fullTemplateTypeCheck", it is required that the latter is not explicitly disabled if the
38930 * former is enabled.
38931 */
38932 function verifyCompatibleTypeCheckOptions(options) {
38933 if (options.fullTemplateTypeCheck === false && options.strictTemplates === true) {
38934 return {
38935 category: ts$1.DiagnosticCategory.Error,
38936 code: ngErrorCode(ErrorCode.CONFIG_STRICT_TEMPLATES_IMPLIES_FULL_TEMPLATE_TYPECHECK),
38937 file: undefined,
38938 start: undefined,
38939 length: undefined,
38940 messageText: `Angular compiler option "strictTemplates" is enabled, however "fullTemplateTypeCheck" is disabled.
38941
38942Having the "strictTemplates" flag enabled implies that "fullTemplateTypeCheck" is also enabled, so
38943the latter can not be explicitly disabled.
38944
38945One of the following actions is required:
389461. Remove the "fullTemplateTypeCheck" option.
389472. Remove "strictTemplates" or set it to 'false'.
38948
38949More information about the template type checking compiler options can be found in the documentation:
38950https://v9.angular.io/guide/template-typecheck#template-type-checking`,
38951 };
38952 }
38953 return null;
38954 }
38955 class ReferenceGraphAdapter {
38956 constructor(graph) {
38957 this.graph = graph;
38958 }
38959 add(source, ...references) {
38960 for (const { node } of references) {
38961 let sourceFile = node.getSourceFile();
38962 if (sourceFile === undefined) {
38963 sourceFile = ts$1.getOriginalNode(node).getSourceFile();
38964 }
38965 // Only record local references (not references into .d.ts files).
38966 if (sourceFile === undefined || !isDtsPath(sourceFile.fileName)) {
38967 this.graph.add(source, node);
38968 }
38969 }
38970 }
38971 }
38972
38973 /**
38974 * @license
38975 * Copyright Google LLC All Rights Reserved.
38976 *
38977 * Use of this source code is governed by an MIT-style license that can be
38978 * found in the LICENSE file at https://angular.io/license
38979 */
38980 function calcProjectFileAndBasePath(project, host = getFileSystem()) {
38981 const absProject = host.resolve(project);
38982 const projectIsDir = host.lstat(absProject).isDirectory();
38983 const projectFile = projectIsDir ? host.join(absProject, 'tsconfig.json') : absProject;
38984 const projectDir = projectIsDir ? absProject : host.dirname(absProject);
38985 const basePath = host.resolve(projectDir);
38986 return { projectFile, basePath };
38987 }
38988 function createNgCompilerOptions(basePath, config, tsOptions) {
38989 // enableIvy `ngtsc` is an alias for `true`.
38990 const { angularCompilerOptions = {} } = config;
38991 const { enableIvy } = angularCompilerOptions;
38992 angularCompilerOptions.enableIvy = enableIvy !== false && enableIvy !== 'tsc';
38993 return Object.assign(Object.assign(Object.assign({}, tsOptions), angularCompilerOptions), { genDir: basePath, basePath });
38994 }
38995 function readConfiguration(project, existingOptions, host = getFileSystem()) {
38996 try {
38997 const { projectFile, basePath } = calcProjectFileAndBasePath(project, host);
38998 const readExtendedConfigFile = (configFile, existingConfig) => {
38999 const { config, error } = ts$1.readConfigFile(configFile, file => host.readFile(host.resolve(file)));
39000 if (error) {
39001 return { error };
39002 }
39003 // we are only interested into merging 'angularCompilerOptions' as
39004 // other options like 'compilerOptions' are merged by TS
39005 const baseConfig = existingConfig || config;
39006 if (existingConfig) {
39007 baseConfig.angularCompilerOptions = Object.assign(Object.assign({}, config.angularCompilerOptions), baseConfig.angularCompilerOptions);
39008 }
39009 if (config.extends) {
39010 let extendedConfigPath = host.resolve(host.dirname(configFile), config.extends);
39011 extendedConfigPath = host.extname(extendedConfigPath) ?
39012 extendedConfigPath :
39013 absoluteFrom(`${extendedConfigPath}.json`);
39014 if (host.exists(extendedConfigPath)) {
39015 // Call read config recursively as TypeScript only merges CompilerOptions
39016 return readExtendedConfigFile(extendedConfigPath, baseConfig);
39017 }
39018 }
39019 return { config: baseConfig };
39020 };
39021 const { config, error } = readExtendedConfigFile(projectFile);
39022 if (error) {
39023 return {
39024 project,
39025 errors: [error],
39026 rootNames: [],
39027 options: {},
39028 emitFlags: EmitFlags.Default
39029 };
39030 }
39031 const parseConfigHost = {
39032 useCaseSensitiveFileNames: true,
39033 fileExists: host.exists.bind(host),
39034 readDirectory: ts$1.sys.readDirectory,
39035 readFile: ts$1.sys.readFile
39036 };
39037 const configFileName = host.resolve(host.pwd(), projectFile);
39038 const parsed = ts$1.parseJsonConfigFileContent(config, parseConfigHost, basePath, existingOptions, configFileName);
39039 const rootNames = parsed.fileNames;
39040 const projectReferences = parsed.projectReferences;
39041 const options = createNgCompilerOptions(basePath, config, parsed.options);
39042 let emitFlags = EmitFlags.Default;
39043 if (!(options.skipMetadataEmit || options.flatModuleOutFile)) {
39044 emitFlags |= EmitFlags.Metadata;
39045 }
39046 if (options.skipTemplateCodegen) {
39047 emitFlags = emitFlags & ~EmitFlags.Codegen;
39048 }
39049 return {
39050 project: projectFile,
39051 rootNames,
39052 projectReferences,
39053 options,
39054 errors: parsed.errors,
39055 emitFlags
39056 };
39057 }
39058 catch (e) {
39059 const errors = [{
39060 category: ts$1.DiagnosticCategory.Error,
39061 messageText: e.stack,
39062 file: undefined,
39063 start: undefined,
39064 length: undefined,
39065 source: 'angular',
39066 code: UNKNOWN_ERROR_CODE,
39067 }];
39068 return { project: '', errors, rootNames: [], options: {}, emitFlags: EmitFlags.Default };
39069 }
39070 }
39071
39072 /**
39073 * @license
39074 * Copyright Google LLC All Rights Reserved.
39075 *
39076 * Use of this source code is governed by an MIT-style license that can be
39077 * found in the LICENSE file at https://angular.io/license
39078 */
39079 /**
39080 * Known values for global variables in `@angular/core` that Terser should set using
39081 * https://github.com/terser-js/terser#conditional-compilation
39082 */
39083 const GLOBAL_DEFS_FOR_TERSER = {
39084 ngDevMode: false,
39085 ngI18nClosureMode: false,
39086 };
39087 const GLOBAL_DEFS_FOR_TERSER_WITH_AOT = Object.assign(Object.assign({}, GLOBAL_DEFS_FOR_TERSER), { ngJitMode: false });
39088
39089 /**
39090 * @license
39091 * Copyright Google LLC All Rights Reserved.
39092 *
39093 * Use of this source code is governed by an MIT-style license that can be
39094 * found in the LICENSE file at https://angular.io/license
39095 */
39096 setFileSystem(new NodeJSFileSystem());
39097
39098 /**
39099 * @license
39100 * Copyright Google LLC All Rights Reserved.
39101 *
39102 * Use of this source code is governed by an MIT-style license that can be
39103 * found in the LICENSE file at https://angular.io/license
39104 */
39105 // Reverse mappings of enum would generate strings
39106 const ALIAS_NAME = ts$1.SymbolDisplayPartKind[ts$1.SymbolDisplayPartKind.aliasName];
39107 const SYMBOL_INTERFACE = ts$1.SymbolDisplayPartKind[ts$1.SymbolDisplayPartKind.interfaceName];
39108 const SYMBOL_PUNC = ts$1.SymbolDisplayPartKind[ts$1.SymbolDisplayPartKind.punctuation];
39109 const SYMBOL_SPACE = ts$1.SymbolDisplayPartKind[ts$1.SymbolDisplayPartKind.space];
39110 const SYMBOL_TEXT = ts$1.SymbolDisplayPartKind[ts$1.SymbolDisplayPartKind.text];
39111 /**
39112 * Label for various kinds of Angular entities for TS display info.
39113 */
39114 var DisplayInfoKind;
39115 (function (DisplayInfoKind) {
39116 DisplayInfoKind["ATTRIBUTE"] = "attribute";
39117 DisplayInfoKind["COMPONENT"] = "component";
39118 DisplayInfoKind["DIRECTIVE"] = "directive";
39119 DisplayInfoKind["EVENT"] = "event";
39120 DisplayInfoKind["REFERENCE"] = "reference";
39121 DisplayInfoKind["ELEMENT"] = "element";
39122 DisplayInfoKind["VARIABLE"] = "variable";
39123 DisplayInfoKind["PIPE"] = "pipe";
39124 DisplayInfoKind["PROPERTY"] = "property";
39125 DisplayInfoKind["METHOD"] = "method";
39126 DisplayInfoKind["TEMPLATE"] = "template";
39127 })(DisplayInfoKind || (DisplayInfoKind = {}));
39128 function getSymbolDisplayInfo(tsLS, typeChecker, symbol) {
39129 let kind;
39130 if (symbol.kind === SymbolKind.Reference) {
39131 kind = DisplayInfoKind.REFERENCE;
39132 }
39133 else if (symbol.kind === SymbolKind.Variable) {
39134 kind = DisplayInfoKind.VARIABLE;
39135 }
39136 else {
39137 throw new Error(`AssertionError: unexpected symbol kind ${SymbolKind[symbol.kind]}`);
39138 }
39139 const displayParts = createDisplayParts(symbol.declaration.name, kind, /* containerName */ undefined, typeChecker.typeToString(symbol.tsType));
39140 const documentation = symbol.kind === SymbolKind.Reference ?
39141 getDocumentationFromTypeDefAtLocation(tsLS, symbol.targetLocation) :
39142 getDocumentationFromTypeDefAtLocation(tsLS, symbol.initializerLocation);
39143 return {
39144 kind,
39145 displayParts,
39146 documentation,
39147 };
39148 }
39149 /**
39150 * Construct a compound `ts.SymbolDisplayPart[]` which incorporates the container and type of a
39151 * target declaration.
39152 * @param name Name of the target
39153 * @param kind component, directive, pipe, etc.
39154 * @param containerName either the Symbol's container or the NgModule that contains the directive
39155 * @param type user-friendly name of the type
39156 * @param documentation docstring or comment
39157 */
39158 function createDisplayParts(name, kind, containerName, type) {
39159 const containerDisplayParts = containerName !== undefined ?
39160 [
39161 { text: containerName, kind: SYMBOL_INTERFACE },
39162 { text: '.', kind: SYMBOL_PUNC },
39163 ] :
39164 [];
39165 const typeDisplayParts = type !== undefined ?
39166 [
39167 { text: ':', kind: SYMBOL_PUNC },
39168 { text: ' ', kind: SYMBOL_SPACE },
39169 { text: type, kind: SYMBOL_INTERFACE },
39170 ] :
39171 [];
39172 return [
39173 { text: '(', kind: SYMBOL_PUNC },
39174 { text: kind, kind: SYMBOL_TEXT },
39175 { text: ')', kind: SYMBOL_PUNC },
39176 { text: ' ', kind: SYMBOL_SPACE },
39177 ...containerDisplayParts,
39178 { text: name, kind: SYMBOL_INTERFACE },
39179 ...typeDisplayParts,
39180 ];
39181 }
39182 /**
39183 * Convert a `SymbolDisplayInfoKind` to a `ts.ScriptElementKind` type, allowing it to pass through
39184 * TypeScript APIs.
39185 *
39186 * In practice, this is an "illegal" type cast. Since `ts.ScriptElementKind` is a string, this is
39187 * safe to do if TypeScript only uses the value in a string context. Consumers of this conversion
39188 * function are responsible for ensuring this is the case.
39189 */
39190 function unsafeCastDisplayInfoKindToScriptElementKind(kind) {
39191 return kind;
39192 }
39193 function getDocumentationFromTypeDefAtLocation(tsLS, shimLocation) {
39194 var _a;
39195 const typeDefs = tsLS.getTypeDefinitionAtPosition(shimLocation.shimPath, shimLocation.positionInShimFile);
39196 if (typeDefs === undefined || typeDefs.length === 0) {
39197 return undefined;
39198 }
39199 return (_a = tsLS.getQuickInfoAtPosition(typeDefs[0].fileName, typeDefs[0].textSpan.start)) === null || _a === void 0 ? void 0 : _a.documentation;
39200 }
39201 function getDirectiveDisplayInfo(tsLS, dir) {
39202 var _a, _b;
39203 const kind = dir.isComponent ? DisplayInfoKind.COMPONENT : DisplayInfoKind.DIRECTIVE;
39204 const decl = dir.tsSymbol.declarations.find(ts$1.isClassDeclaration);
39205 if (decl === undefined || decl.name === undefined) {
39206 return { kind, displayParts: [], documentation: [] };
39207 }
39208 const res = tsLS.getQuickInfoAtPosition(decl.getSourceFile().fileName, decl.name.getStart());
39209 if (res === undefined) {
39210 return { kind, displayParts: [], documentation: [] };
39211 }
39212 const displayParts = createDisplayParts(dir.tsSymbol.name, kind, (_b = (_a = dir.ngModule) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.text, undefined);
39213 return {
39214 kind,
39215 displayParts,
39216 documentation: res.documentation,
39217 };
39218 }
39219 function getTsSymbolDisplayInfo(tsLS, checker, symbol, kind, ownerName) {
39220 const decl = symbol.valueDeclaration;
39221 if (decl === undefined || (!ts$1.isPropertyDeclaration(decl) && !ts$1.isMethodDeclaration(decl)) ||
39222 !ts$1.isIdentifier(decl.name)) {
39223 return null;
39224 }
39225 const res = tsLS.getQuickInfoAtPosition(decl.getSourceFile().fileName, decl.name.getStart());
39226 if (res === undefined) {
39227 return { kind, displayParts: [], documentation: [] };
39228 }
39229 const type = checker.getDeclaredTypeOfSymbol(symbol);
39230 const typeString = checker.typeToString(type);
39231 const displayParts = createDisplayParts(symbol.name, kind, ownerName !== null && ownerName !== void 0 ? ownerName : undefined, typeString);
39232 return {
39233 kind,
39234 displayParts,
39235 documentation: res.documentation,
39236 };
39237 }
39238
39239 /**
39240 * @license
39241 * Copyright Google LLC All Rights Reserved.
39242 *
39243 * Use of this source code is governed by an MIT-style license that can be
39244 * found in the LICENSE file at https://angular.io/license
39245 */
39246 /**
39247 * Return the node that most tightly encompasses the specified `position`.
39248 * @param node The starting node to start the top-down search.
39249 * @param position The target position within the `node`.
39250 */
39251 function findTightestNode(node, position) {
39252 var _a;
39253 if (node.getStart() <= position && position < node.getEnd()) {
39254 return (_a = node.forEachChild(c => findTightestNode(c, position))) !== null && _a !== void 0 ? _a : node;
39255 }
39256 return undefined;
39257 }
39258 function getParentClassDeclaration(startNode) {
39259 while (startNode) {
39260 if (ts$1.isClassDeclaration(startNode)) {
39261 return startNode;
39262 }
39263 startNode = startNode.parent;
39264 }
39265 return undefined;
39266 }
39267
39268 /**
39269 * @license
39270 * Copyright Google LLC All Rights Reserved.
39271 *
39272 * Use of this source code is governed by an MIT-style license that can be
39273 * found in the LICENSE file at https://angular.io/license
39274 */
39275 function getTextSpanOfNode(node) {
39276 if (isTemplateNodeWithKeyAndValue(node)) {
39277 return toTextSpan(node.keySpan);
39278 }
39279 else if (node instanceof PropertyWrite || node instanceof MethodCall ||
39280 node instanceof BindingPipe || node instanceof PropertyRead) {
39281 // The `name` part of a `PropertyWrite`, `MethodCall`, and `BindingPipe` does not
39282 // have its own AST so there is no way to retrieve a `Symbol` for just the `name` via a specific
39283 // node.
39284 return toTextSpan(node.nameSpan);
39285 }
39286 else {
39287 return toTextSpan(node.sourceSpan);
39288 }
39289 }
39290 function toTextSpan(span) {
39291 let start, end;
39292 if (span instanceof AbsoluteSourceSpan || span instanceof ParseSpan) {
39293 start = span.start;
39294 end = span.end;
39295 }
39296 else {
39297 start = span.start.offset;
39298 end = span.end.offset;
39299 }
39300 return { start, length: end - start };
39301 }
39302 function isTemplateNodeWithKeyAndValue(node) {
39303 return isTemplateNode(node) && node.hasOwnProperty('keySpan');
39304 }
39305 function isWithinKeyValue(position, node) {
39306 let { keySpan, valueSpan } = node;
39307 if (valueSpan === undefined && node instanceof BoundEvent) {
39308 valueSpan = node.handlerSpan;
39309 }
39310 const isWithinKeyValue = isWithin(position, keySpan) || !!(valueSpan && isWithin(position, valueSpan));
39311 return isWithinKeyValue;
39312 }
39313 function isTemplateNode(node) {
39314 // Template node implements the Node interface so we cannot use instanceof.
39315 return node.sourceSpan instanceof ParseSourceSpan;
39316 }
39317 function getInlineTemplateInfoAtPosition(sf, position, compiler) {
39318 const expression = findTightestNode(sf, position);
39319 if (expression === undefined) {
39320 return undefined;
39321 }
39322 const classDecl = getParentClassDeclaration(expression);
39323 if (classDecl === undefined) {
39324 return undefined;
39325 }
39326 // Return `undefined` if the position is not on the template expression or the template resource
39327 // is not inline.
39328 const resources = compiler.getComponentResources(classDecl);
39329 if (resources === null || isExternalResource(resources.template) ||
39330 expression !== resources.template.expression) {
39331 return undefined;
39332 }
39333 const template = compiler.getTemplateTypeChecker().getTemplate(classDecl);
39334 if (template === null) {
39335 return undefined;
39336 }
39337 return { template, component: classDecl };
39338 }
39339 /**
39340 * Retrieves the `ts.ClassDeclaration` at a location along with its template nodes.
39341 */
39342 function getTemplateInfoAtPosition(fileName, position, compiler) {
39343 if (isTypeScriptFile(fileName)) {
39344 const sf = compiler.getNextProgram().getSourceFile(fileName);
39345 if (sf === undefined) {
39346 return undefined;
39347 }
39348 return getInlineTemplateInfoAtPosition(sf, position, compiler);
39349 }
39350 else {
39351 return getFirstComponentForTemplateFile(fileName, compiler);
39352 }
39353 }
39354 /**
39355 * First, attempt to sort component declarations by file name.
39356 * If the files are the same, sort by start location of the declaration.
39357 */
39358 function tsDeclarationSortComparator(a, b) {
39359 const aFile = a.getSourceFile().fileName;
39360 const bFile = b.getSourceFile().fileName;
39361 if (aFile < bFile) {
39362 return -1;
39363 }
39364 else if (aFile > bFile) {
39365 return 1;
39366 }
39367 else {
39368 return b.getFullStart() - a.getFullStart();
39369 }
39370 }
39371 function getFirstComponentForTemplateFile(fileName, compiler) {
39372 const templateTypeChecker = compiler.getTemplateTypeChecker();
39373 const components = compiler.getComponentsWithTemplateFile(fileName);
39374 const sortedComponents = Array.from(components).sort(tsDeclarationSortComparator);
39375 for (const component of sortedComponents) {
39376 if (!ts$1.isClassDeclaration(component)) {
39377 continue;
39378 }
39379 const template = templateTypeChecker.getTemplate(component);
39380 if (template === null) {
39381 continue;
39382 }
39383 return { template, component };
39384 }
39385 return undefined;
39386 }
39387 /**
39388 * Given an attribute node, converts it to string form.
39389 */
39390 function toAttributeString(attribute) {
39391 var _a, _b;
39392 if (attribute instanceof BoundEvent) {
39393 return `[${attribute.name}]`;
39394 }
39395 else {
39396 return `[${attribute.name}=${(_b = (_a = attribute.valueSpan) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : ''}]`;
39397 }
39398 }
39399 function getNodeName(node) {
39400 return node instanceof Template ? node.tagName : node.name;
39401 }
39402 /**
39403 * Given a template or element node, returns all attributes on the node.
39404 */
39405 function getAttributes(node) {
39406 const attributes = [...node.attributes, ...node.inputs, ...node.outputs];
39407 if (node instanceof Template) {
39408 attributes.push(...node.templateAttrs);
39409 }
39410 return attributes;
39411 }
39412 /**
39413 * Given two `Set`s, returns all items in the `left` which do not appear in the `right`.
39414 */
39415 function difference(left, right) {
39416 const result = new Set();
39417 for (const dir of left) {
39418 if (!right.has(dir)) {
39419 result.add(dir);
39420 }
39421 }
39422 return result;
39423 }
39424 /**
39425 * Given an element or template, determines which directives match because the tag is present. For
39426 * example, if a directive selector is `div[myAttr]`, this would match div elements but would not if
39427 * the selector were just `[myAttr]`. We find which directives are applied because of this tag by
39428 * elimination: compare the directive matches with the tag present against the directive matches
39429 * without it. The difference would be the directives which match because the tag is present.
39430 *
39431 * @param element The element or template node that the attribute/tag is part of.
39432 * @param directives The list of directives to match against.
39433 * @returns The list of directives matching the tag name via the strategy described above.
39434 */
39435 // TODO(atscott): Add unit tests for this and the one for attributes
39436 function getDirectiveMatchesForElementTag(element, directives) {
39437 const attributes = getAttributes(element);
39438 const allAttrs = attributes.map(toAttributeString);
39439 const allDirectiveMatches = getDirectiveMatchesForSelector(directives, getNodeName(element) + allAttrs.join(''));
39440 const matchesWithoutElement = getDirectiveMatchesForSelector(directives, allAttrs.join(''));
39441 return difference(allDirectiveMatches, matchesWithoutElement);
39442 }
39443 function makeElementSelector(element) {
39444 const attributes = getAttributes(element);
39445 const allAttrs = attributes.map(toAttributeString);
39446 return getNodeName(element) + allAttrs.join('');
39447 }
39448 /**
39449 * Given an attribute name, determines which directives match because the attribute is present. We
39450 * find which directives are applied because of this attribute by elimination: compare the directive
39451 * matches with the attribute present against the directive matches without it. The difference would
39452 * be the directives which match because the attribute is present.
39453 *
39454 * @param name The name of the attribute
39455 * @param hostNode The node which the attribute appears on
39456 * @param directives The list of directives to match against.
39457 * @returns The list of directives matching the tag name via the strategy described above.
39458 */
39459 function getDirectiveMatchesForAttribute(name, hostNode, directives) {
39460 const attributes = getAttributes(hostNode);
39461 const allAttrs = attributes.map(toAttributeString);
39462 const allDirectiveMatches = getDirectiveMatchesForSelector(directives, getNodeName(hostNode) + allAttrs.join(''));
39463 const attrsExcludingName = attributes.filter(a => a.name !== name).map(toAttributeString);
39464 const matchesWithoutAttr = getDirectiveMatchesForSelector(directives, getNodeName(hostNode) + attrsExcludingName.join(''));
39465 return difference(allDirectiveMatches, matchesWithoutAttr);
39466 }
39467 /**
39468 * Given a list of directives and a text to use as a selector, returns the directives which match
39469 * for the selector.
39470 */
39471 function getDirectiveMatchesForSelector(directives, selector) {
39472 const selectors = CssSelector.parse(selector);
39473 if (selectors.length === 0) {
39474 return new Set();
39475 }
39476 return new Set(directives.filter((dir) => {
39477 if (dir.selector === null) {
39478 return false;
39479 }
39480 const matcher = new SelectorMatcher();
39481 matcher.addSelectables(CssSelector.parse(dir.selector));
39482 return selectors.some(selector => matcher.match(selector, null));
39483 }));
39484 }
39485 /**
39486 * Returns a new `ts.SymbolDisplayPart` array which has the alias imports from the tcb filtered
39487 * out, i.e. `i0.NgForOf`.
39488 */
39489 function filterAliasImports(displayParts) {
39490 const tcbAliasImportRegex = /i\d+/;
39491 function isImportAlias(part) {
39492 return part.kind === ALIAS_NAME && tcbAliasImportRegex.test(part.text);
39493 }
39494 function isDotPunctuation(part) {
39495 return part.kind === SYMBOL_PUNC && part.text === '.';
39496 }
39497 return displayParts.filter((part, i) => {
39498 const previousPart = displayParts[i - 1];
39499 const nextPart = displayParts[i + 1];
39500 const aliasNameFollowedByDot = isImportAlias(part) && nextPart !== undefined && isDotPunctuation(nextPart);
39501 const dotPrecededByAlias = isDotPunctuation(part) && previousPart !== undefined && isImportAlias(previousPart);
39502 return !aliasNameFollowedByDot && !dotPrecededByAlias;
39503 });
39504 }
39505 function isDollarEvent(n) {
39506 return n instanceof PropertyRead && n.name === '$event' &&
39507 n.receiver instanceof ImplicitReceiver && !(n.receiver instanceof ThisReceiver);
39508 }
39509 /**
39510 * Returns a new array formed by applying a given callback function to each element of the array,
39511 * and then flattening the result by one level.
39512 */
39513 function flatMap(items, f) {
39514 const results = [];
39515 for (const x of items) {
39516 results.push(...f(x));
39517 }
39518 return results;
39519 }
39520 function isTypeScriptFile(fileName) {
39521 return fileName.endsWith('.ts');
39522 }
39523 function isWithin(position, span) {
39524 let start, end;
39525 if (span instanceof ParseSourceSpan) {
39526 start = span.start.offset;
39527 end = span.end.offset;
39528 }
39529 else {
39530 start = span.start;
39531 end = span.end;
39532 }
39533 // Note both start and end are inclusive because we want to match conditions
39534 // like ¦start and end¦ where ¦ is the cursor.
39535 return start <= position && position <= end;
39536 }
39537 /**
39538 * For a given location in a shim file, retrieves the corresponding file url for the template and
39539 * the span in the template.
39540 */
39541 function getTemplateLocationFromShimLocation(templateTypeChecker, shimPath, positionInShimFile) {
39542 const mapping = templateTypeChecker.getTemplateMappingAtShimLocation({ shimPath, positionInShimFile });
39543 if (mapping === null) {
39544 return null;
39545 }
39546 const { templateSourceMapping, span } = mapping;
39547 let templateUrl;
39548 if (templateSourceMapping.type === 'direct') {
39549 templateUrl = absoluteFromSourceFile(templateSourceMapping.node.getSourceFile());
39550 }
39551 else if (templateSourceMapping.type === 'external') {
39552 templateUrl = absoluteFrom(templateSourceMapping.templateUrl);
39553 }
39554 else {
39555 // This includes indirect mappings, which are difficult to map directly to the code
39556 // location. Diagnostics similarly return a synthetic template string for this case rather
39557 // than a real location.
39558 return null;
39559 }
39560 return { templateUrl, span };
39561 }
39562
39563 /**
39564 * @license
39565 * Copyright Google LLC All Rights Reserved.
39566 *
39567 * Use of this source code is governed by an MIT-style license that can be
39568 * found in the LICENSE file at https://angular.io/license
39569 */
39570 class LanguageServiceAdapter {
39571 constructor(project) {
39572 this.project = project;
39573 this.entryPoint = null;
39574 this.constructionDiagnostics = [];
39575 this.ignoreForEmit = new Set();
39576 this.factoryTracker = null; // no .ngfactory shims
39577 this.unifiedModulesHost = null; // only used in Bazel
39578 /**
39579 * Map of resource filenames to the version of the file last read via `readResource`.
39580 *
39581 * Used to implement `getModifiedResourceFiles`.
39582 */
39583 this.lastReadResourceVersion = new Map();
39584 this.rootDirs = getRootDirs(this, project.getCompilationSettings());
39585 }
39586 isShim(sf) {
39587 return isShim(sf);
39588 }
39589 fileExists(fileName) {
39590 return this.project.fileExists(fileName);
39591 }
39592 readFile(fileName) {
39593 return this.project.readFile(fileName);
39594 }
39595 getCurrentDirectory() {
39596 return this.project.getCurrentDirectory();
39597 }
39598 getCanonicalFileName(fileName) {
39599 return this.project.projectService.toCanonicalFileName(fileName);
39600 }
39601 /**
39602 * Return the real path of a symlink. This method is required in order to
39603 * resolve symlinks in node_modules.
39604 */
39605 realpath(path) {
39606 var _a, _b, _c;
39607 return (_c = (_b = (_a = this.project).realpath) === null || _b === void 0 ? void 0 : _b.call(_a, path)) !== null && _c !== void 0 ? _c : path;
39608 }
39609 /**
39610 * readResource() is an Angular-specific method for reading files that are not
39611 * managed by the TS compiler host, namely templates and stylesheets.
39612 * It is a method on ExtendedTsCompilerHost, see
39613 * packages/compiler-cli/src/ngtsc/core/api/src/interfaces.ts
39614 */
39615 readResource(fileName) {
39616 if (isTypeScriptFile(fileName)) {
39617 throw new Error(`readResource() should not be called on TS file: ${fileName}`);
39618 }
39619 // Calling getScriptSnapshot() will actually create a ScriptInfo if it does
39620 // not exist! The same applies for getScriptVersion().
39621 // getScriptInfo() will not create one if it does not exist.
39622 // In this case, we *want* a script info to be created so that we could
39623 // keep track of its version.
39624 const snapshot = this.project.getScriptSnapshot(fileName);
39625 if (!snapshot) {
39626 // This would fail if the file does not exist, or readFile() fails for
39627 // whatever reasons.
39628 throw new Error(`Failed to get script snapshot while trying to read ${fileName}`);
39629 }
39630 const version = this.project.getScriptVersion(fileName);
39631 this.lastReadResourceVersion.set(fileName, version);
39632 return snapshot.getText(0, snapshot.getLength());
39633 }
39634 getModifiedResourceFiles() {
39635 const modifiedFiles = new Set();
39636 for (const [fileName, oldVersion] of this.lastReadResourceVersion) {
39637 if (this.project.getScriptVersion(fileName) !== oldVersion) {
39638 modifiedFiles.add(fileName);
39639 }
39640 }
39641 return modifiedFiles.size > 0 ? modifiedFiles : undefined;
39642 }
39643 }
39644 /**
39645 * Used to read configuration files.
39646 *
39647 * A language service parse configuration host is independent of the adapter
39648 * because signatures of calls like `FileSystem#readFile` are a bit stricter
39649 * than those on the adapter.
39650 */
39651 class LSParseConfigHost {
39652 constructor(serverHost) {
39653 this.serverHost = serverHost;
39654 }
39655 exists(path) {
39656 return this.serverHost.fileExists(path) || this.serverHost.directoryExists(path);
39657 }
39658 readFile(path) {
39659 const content = this.serverHost.readFile(path);
39660 if (content === undefined) {
39661 throw new Error(`LanguageServiceFS#readFile called on unavailable file ${path}`);
39662 }
39663 return content;
39664 }
39665 lstat(path) {
39666 return {
39667 isFile: () => {
39668 return this.serverHost.fileExists(path);
39669 },
39670 isDirectory: () => {
39671 return this.serverHost.directoryExists(path);
39672 },
39673 isSymbolicLink: () => {
39674 throw new Error(`LanguageServiceFS#lstat#isSymbolicLink not implemented`);
39675 },
39676 };
39677 }
39678 pwd() {
39679 return this.serverHost.getCurrentDirectory();
39680 }
39681 extname(path$1) {
39682 return path.extname(path$1);
39683 }
39684 resolve(...paths) {
39685 return path.resolve(...paths);
39686 }
39687 dirname(file) {
39688 return path.dirname(file);
39689 }
39690 join(basePath, ...paths) {
39691 return path.join(basePath, ...paths);
39692 }
39693 }
39694
39695 /**
39696 * @license
39697 * Copyright Google LLC All Rights Reserved.
39698 *
39699 * Use of this source code is governed by an MIT-style license that can be
39700 * found in the LICENSE file at https://angular.io/license
39701 */
39702 /**
39703 * Manages the `NgCompiler` instance which backs the language service, updating or replacing it as
39704 * needed to produce an up-to-date understanding of the current program.
39705 *
39706 * TODO(alxhub): currently the options used for the compiler are specified at `CompilerFactory`
39707 * construction, and are not changable. In a real project, users can update `tsconfig.json`. We need
39708 * to properly handle a change in the compiler options, either by having an API to update the
39709 * `CompilerFactory` to use new options, or by replacing it entirely.
39710 */
39711 class CompilerFactory {
39712 constructor(adapter, programStrategy, options) {
39713 this.adapter = adapter;
39714 this.programStrategy = programStrategy;
39715 this.options = options;
39716 this.incrementalStrategy = new TrackedIncrementalBuildStrategy();
39717 this.compiler = null;
39718 this.lastKnownProgram = null;
39719 }
39720 getOrCreate() {
39721 var _a;
39722 const program = this.programStrategy.getProgram();
39723 const modifiedResourceFiles = (_a = this.adapter.getModifiedResourceFiles()) !== null && _a !== void 0 ? _a : new Set();
39724 if (this.compiler !== null && program === this.lastKnownProgram) {
39725 if (modifiedResourceFiles.size > 0) {
39726 // Only resource files have changed since the last NgCompiler was created.
39727 const ticket = resourceChangeTicket(this.compiler, modifiedResourceFiles);
39728 this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
39729 }
39730 return this.compiler;
39731 }
39732 let ticket;
39733 if (this.compiler === null || this.lastKnownProgram === null) {
39734 ticket = freshCompilationTicket(program, this.options, this.incrementalStrategy, this.programStrategy, true, true);
39735 }
39736 else {
39737 ticket = incrementalFromCompilerTicket(this.compiler, program, this.incrementalStrategy, this.programStrategy, modifiedResourceFiles);
39738 }
39739 this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
39740 this.lastKnownProgram = program;
39741 return this.compiler;
39742 }
39743 registerLastKnownProgram() {
39744 this.lastKnownProgram = this.programStrategy.getProgram();
39745 }
39746 }
39747
39748 /**
39749 * @license
39750 * Copyright Google LLC All Rights Reserved.
39751 *
39752 * Use of this source code is governed by an MIT-style license that can be
39753 * found in the LICENSE file at https://angular.io/license
39754 */
39755 /**
39756 * Differentiates different kinds of `AttributeCompletion`s.
39757 */
39758 var AttributeCompletionKind;
39759 (function (AttributeCompletionKind) {
39760 /**
39761 * Completion of an attribute from the HTML schema.
39762 *
39763 * Attributes often have a corresponding DOM property of the same name.
39764 */
39765 AttributeCompletionKind[AttributeCompletionKind["DomAttribute"] = 0] = "DomAttribute";
39766 /**
39767 * Completion of a property from the DOM schema.
39768 *
39769 * `DomProperty` completions are generated only for properties which don't share their name with
39770 * an HTML attribute.
39771 */
39772 AttributeCompletionKind[AttributeCompletionKind["DomProperty"] = 1] = "DomProperty";
39773 /**
39774 * Completion of an attribute that results in a new directive being matched on an element.
39775 */
39776 AttributeCompletionKind[AttributeCompletionKind["DirectiveAttribute"] = 2] = "DirectiveAttribute";
39777 /**
39778 * Completion of an attribute that results in a new structural directive being matched on an
39779 * element.
39780 */
39781 AttributeCompletionKind[AttributeCompletionKind["StructuralDirectiveAttribute"] = 3] = "StructuralDirectiveAttribute";
39782 /**
39783 * Completion of an input from a directive which is either present on the element, or becomes
39784 * present after the addition of this attribute.
39785 */
39786 AttributeCompletionKind[AttributeCompletionKind["DirectiveInput"] = 4] = "DirectiveInput";
39787 /**
39788 * Completion of an output from a directive which is either present on the element, or becomes
39789 * present after the addition of this attribute.
39790 */
39791 AttributeCompletionKind[AttributeCompletionKind["DirectiveOutput"] = 5] = "DirectiveOutput";
39792 })(AttributeCompletionKind || (AttributeCompletionKind = {}));
39793 /**
39794 * Given an element and its context, produce a `Map` of all possible attribute completions.
39795 *
39796 * 3 kinds of attributes are considered for completion, from highest to lowest priority:
39797 *
39798 * 1. Inputs/outputs of directives present on the element already.
39799 * 2. Inputs/outputs of directives that are not present on the element, but which would become
39800 * present if such a binding is added.
39801 * 3. Attributes from the DOM schema for the element.
39802 *
39803 * The priority of these options determines which completions are added to the `Map`. If a directive
39804 * input shares the same name as a DOM attribute, the `Map` will reflect the directive input
39805 * completion, not the DOM completion for that name.
39806 */
39807 function buildAttributeCompletionTable(component, element, checker) {
39808 const table = new Map();
39809 // Use the `ElementSymbol` or `TemplateSymbol` to iterate over directives present on the node, and
39810 // their inputs/outputs. These have the highest priority of completion results.
39811 const symbol = checker.getSymbolOfNode(element, component);
39812 const presentDirectives = new Set();
39813 if (symbol !== null) {
39814 // An `ElementSymbol` was available. This means inputs and outputs for directives on the
39815 // element can be added to the completion table.
39816 for (const dirSymbol of symbol.directives) {
39817 const directive = dirSymbol.tsSymbol.valueDeclaration;
39818 if (!ts$1.isClassDeclaration(directive)) {
39819 continue;
39820 }
39821 presentDirectives.add(directive);
39822 const meta = checker.getDirectiveMetadata(directive);
39823 if (meta === null) {
39824 continue;
39825 }
39826 for (const [propertyName, classPropertyName] of meta.inputs) {
39827 if (table.has(propertyName)) {
39828 continue;
39829 }
39830 table.set(propertyName, {
39831 kind: AttributeCompletionKind.DirectiveInput,
39832 propertyName,
39833 directive: dirSymbol,
39834 classPropertyName,
39835 twoWayBindingSupported: meta.outputs.hasBindingPropertyName(propertyName + 'Change'),
39836 });
39837 }
39838 for (const [propertyName, classPropertyName] of meta.outputs) {
39839 if (table.has(propertyName)) {
39840 continue;
39841 }
39842 table.set(propertyName, {
39843 kind: AttributeCompletionKind.DirectiveOutput,
39844 eventName: propertyName,
39845 directive: dirSymbol,
39846 classPropertyName,
39847 });
39848 }
39849 }
39850 }
39851 // Next, explore hypothetical directives and determine if the addition of any single attributes
39852 // can cause the directive to match the element.
39853 const directivesInScope = checker.getDirectivesInScope(component);
39854 if (directivesInScope !== null) {
39855 const elementSelector = makeElementSelector(element);
39856 for (const dirInScope of directivesInScope) {
39857 const directive = dirInScope.tsSymbol.valueDeclaration;
39858 // Skip directives that are present on the element.
39859 if (!ts$1.isClassDeclaration(directive) || presentDirectives.has(directive)) {
39860 continue;
39861 }
39862 const meta = checker.getDirectiveMetadata(directive);
39863 if (meta === null || meta.selector === null) {
39864 continue;
39865 }
39866 if (!meta.isStructural) {
39867 // For non-structural directives, the directive's attribute selector(s) are matched against
39868 // a hypothetical version of the element with those attributes. A match indicates that
39869 // adding that attribute/input/output binding would cause the directive to become present,
39870 // meaning that such a binding is a valid completion.
39871 const selectors = CssSelector.parse(meta.selector);
39872 const matcher = new SelectorMatcher();
39873 matcher.addSelectables(selectors);
39874 for (const selector of selectors) {
39875 for (const [attrName, attrValue] of selectorAttributes(selector)) {
39876 if (attrValue !== '') {
39877 // This attribute selector requires a value, which is not supported in completion.
39878 continue;
39879 }
39880 if (table.has(attrName)) {
39881 // Skip this attribute as there's already a binding for it.
39882 continue;
39883 }
39884 // Check whether adding this attribute would cause the directive to start matching.
39885 const newElementSelector = elementSelector + `[${attrName}]`;
39886 if (!matcher.match(CssSelector.parse(newElementSelector)[0], null)) {
39887 // Nope, move on with our lives.
39888 continue;
39889 }
39890 // Adding this attribute causes a new directive to be matched. Decide how to categorize
39891 // it based on the directive's inputs and outputs.
39892 if (meta.inputs.hasBindingPropertyName(attrName)) {
39893 // This attribute corresponds to an input binding.
39894 table.set(attrName, {
39895 kind: AttributeCompletionKind.DirectiveInput,
39896 directive: dirInScope,
39897 propertyName: attrName,
39898 classPropertyName: meta.inputs.getByBindingPropertyName(attrName)[0].classPropertyName,
39899 twoWayBindingSupported: meta.outputs.hasBindingPropertyName(attrName + 'Change'),
39900 });
39901 }
39902 else if (meta.outputs.hasBindingPropertyName(attrName)) {
39903 // This attribute corresponds to an output binding.
39904 table.set(attrName, {
39905 kind: AttributeCompletionKind.DirectiveOutput,
39906 directive: dirInScope,
39907 eventName: attrName,
39908 classPropertyName: meta.outputs.getByBindingPropertyName(attrName)[0].classPropertyName,
39909 });
39910 }
39911 else {
39912 // This attribute causes a new directive to be matched, but does not also correspond
39913 // to an input or output binding.
39914 table.set(attrName, {
39915 kind: AttributeCompletionKind.DirectiveAttribute,
39916 attribute: attrName,
39917 directive: dirInScope,
39918 });
39919 }
39920 }
39921 }
39922 }
39923 else {
39924 // Hypothetically matching a structural directive is a litle different than a plain
39925 // directive. Use of the '*' structural directive syntactic sugar means that the actual
39926 // directive is applied to a plain <ng-template> node, not the existing element with any
39927 // other attributes it might already have.
39928 // Additionally, more than one attribute/input might need to be present in order for the
39929 // directive to match (e.g. `ngFor` has a selector of `[ngFor][ngForOf]`). This gets a
39930 // little tricky.
39931 const structuralAttributes = getStructuralAttributes(meta);
39932 for (const attrName of structuralAttributes) {
39933 table.set(attrName, {
39934 kind: AttributeCompletionKind.StructuralDirectiveAttribute,
39935 attribute: attrName,
39936 directive: dirInScope,
39937 });
39938 }
39939 }
39940 }
39941 }
39942 // Finally, add any DOM attributes not already covered by inputs.
39943 if (element instanceof Element) {
39944 for (const { attribute, property } of checker.getPotentialDomBindings(element.name)) {
39945 const isAlsoProperty = attribute === property;
39946 if (!table.has(attribute)) {
39947 table.set(attribute, {
39948 kind: AttributeCompletionKind.DomAttribute,
39949 attribute,
39950 isAlsoProperty,
39951 });
39952 }
39953 if (!isAlsoProperty && !table.has(property)) {
39954 table.set(property, {
39955 kind: AttributeCompletionKind.DomProperty,
39956 property,
39957 });
39958 }
39959 }
39960 }
39961 return table;
39962 }
39963 /**
39964 * Given an `AttributeCompletion`, add any available completions to a `ts.CompletionEntry` array of
39965 * results.
39966 *
39967 * The kind of completions generated depends on whether the current context is an attribute context
39968 * or not. For example, completing on `<element attr|>` will generate two results: `attribute` and
39969 * `[attribute]` - either a static attribute can be generated, or a property binding. However,
39970 * `<element [attr|]>` is not an attribute context, and so only the property completion `attribute`
39971 * is generated. Note that this completion does not have the `[]` property binding sugar as its
39972 * implicitly present in a property binding context (we're already completing within an `[attr|]`
39973 * expression).
39974 */
39975 function addAttributeCompletionEntries(entries, completion, isAttributeContext, isElementContext, replacementSpan) {
39976 switch (completion.kind) {
39977 case AttributeCompletionKind.DirectiveAttribute: {
39978 entries.push({
39979 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.DIRECTIVE),
39980 name: completion.attribute,
39981 sortText: completion.attribute,
39982 replacementSpan,
39983 });
39984 break;
39985 }
39986 case AttributeCompletionKind.StructuralDirectiveAttribute: {
39987 // In an element, the completion is offered with a leading '*' to activate the structural
39988 // directive. Once present, the structural attribute will be parsed as a template and not an
39989 // element, and the prefix is no longer necessary.
39990 const prefix = isElementContext ? '*' : '';
39991 entries.push({
39992 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.DIRECTIVE),
39993 name: prefix + completion.attribute,
39994 sortText: prefix + completion.attribute,
39995 replacementSpan,
39996 });
39997 break;
39998 }
39999 case AttributeCompletionKind.DirectiveInput: {
40000 if (isAttributeContext) {
40001 // Offer a completion of a property binding.
40002 entries.push({
40003 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40004 name: `[${completion.propertyName}]`,
40005 sortText: completion.propertyName,
40006 replacementSpan,
40007 });
40008 // If the directive supports banana-in-a-box for this input, offer that as well.
40009 if (completion.twoWayBindingSupported) {
40010 entries.push({
40011 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40012 name: `[(${completion.propertyName})]`,
40013 // This completion should sort after the property binding.
40014 sortText: completion.propertyName + '_1',
40015 replacementSpan,
40016 });
40017 }
40018 // Offer a completion of the input binding as an attribute.
40019 entries.push({
40020 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.ATTRIBUTE),
40021 name: completion.propertyName,
40022 // This completion should sort after both property binding options (one-way and two-way).
40023 sortText: completion.propertyName + '_2',
40024 replacementSpan,
40025 });
40026 }
40027 else {
40028 entries.push({
40029 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40030 name: completion.propertyName,
40031 sortText: completion.propertyName,
40032 replacementSpan,
40033 });
40034 }
40035 break;
40036 }
40037 case AttributeCompletionKind.DirectiveOutput: {
40038 if (isAttributeContext) {
40039 entries.push({
40040 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.EVENT),
40041 name: `(${completion.eventName})`,
40042 sortText: completion.eventName,
40043 replacementSpan,
40044 });
40045 }
40046 else {
40047 entries.push({
40048 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.EVENT),
40049 name: completion.eventName,
40050 sortText: completion.eventName,
40051 replacementSpan,
40052 });
40053 }
40054 break;
40055 }
40056 case AttributeCompletionKind.DomAttribute: {
40057 if (isAttributeContext) {
40058 // Offer a completion of an attribute binding.
40059 entries.push({
40060 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.ATTRIBUTE),
40061 name: completion.attribute,
40062 sortText: completion.attribute,
40063 replacementSpan,
40064 });
40065 if (completion.isAlsoProperty) {
40066 // Offer a completion of a property binding to the DOM property.
40067 entries.push({
40068 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40069 name: `[${completion.attribute}]`,
40070 // In the case of DOM attributes, the property binding should sort after the attribute
40071 // binding.
40072 sortText: completion.attribute + '_1',
40073 replacementSpan,
40074 });
40075 }
40076 }
40077 else if (completion.isAlsoProperty) {
40078 entries.push({
40079 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40080 name: completion.attribute,
40081 sortText: completion.attribute,
40082 replacementSpan,
40083 });
40084 }
40085 break;
40086 }
40087 case AttributeCompletionKind.DomProperty: {
40088 if (!isAttributeContext) {
40089 entries.push({
40090 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PROPERTY),
40091 name: completion.property,
40092 sortText: completion.property,
40093 replacementSpan,
40094 });
40095 }
40096 }
40097 }
40098 }
40099 function getAttributeCompletionSymbol(completion, checker) {
40100 var _a;
40101 switch (completion.kind) {
40102 case AttributeCompletionKind.DomAttribute:
40103 case AttributeCompletionKind.DomProperty:
40104 return null;
40105 case AttributeCompletionKind.DirectiveAttribute:
40106 case AttributeCompletionKind.StructuralDirectiveAttribute:
40107 return completion.directive.tsSymbol;
40108 case AttributeCompletionKind.DirectiveInput:
40109 case AttributeCompletionKind.DirectiveOutput:
40110 return (_a = checker.getDeclaredTypeOfSymbol(completion.directive.tsSymbol)
40111 .getProperty(completion.classPropertyName)) !== null && _a !== void 0 ? _a : null;
40112 }
40113 }
40114 /**
40115 * Iterates over `CssSelector` attributes, which are internally represented in a zipped array style
40116 * which is not conducive to straightforward iteration.
40117 */
40118 function* selectorAttributes(selector) {
40119 for (let i = 0; i < selector.attrs.length; i += 2) {
40120 yield [selector.attrs[0], selector.attrs[1]];
40121 }
40122 }
40123 function getStructuralAttributes(meta) {
40124 if (meta.selector === null) {
40125 return [];
40126 }
40127 const structuralAttributes = [];
40128 const selectors = CssSelector.parse(meta.selector);
40129 for (const selector of selectors) {
40130 if (selector.element !== null && selector.element !== 'ng-template') {
40131 // This particular selector does not apply under structural directive syntax.
40132 continue;
40133 }
40134 // Every attribute of this selector must be name-only - no required values.
40135 const attributeSelectors = Array.from(selectorAttributes(selector));
40136 if (!attributeSelectors.every(([_, attrValue]) => attrValue === '')) {
40137 continue;
40138 }
40139 // Get every named selector.
40140 const attributes = attributeSelectors.map(([attrName, _]) => attrName);
40141 // Find the shortest attribute. This is the structural directive "base", and all potential
40142 // input bindings must begin with the base. E.g. in `*ngFor="let a of b"`, `ngFor` is the
40143 // base attribute, and the `of` binding key corresponds to an input of `ngForOf`.
40144 const baseAttr = attributes.reduce((prev, curr) => prev === null || curr.length < prev.length ? curr : prev, null);
40145 if (baseAttr === null) {
40146 // No attributes in this selector?
40147 continue;
40148 }
40149 // Validate that the attributes are compatible with use as a structural directive.
40150 const isValid = (attr) => {
40151 // The base attribute is valid by default.
40152 if (attr === baseAttr) {
40153 return true;
40154 }
40155 // Non-base attributes must all be prefixed with the base attribute.
40156 if (!attr.startsWith(baseAttr)) {
40157 return false;
40158 }
40159 // Non-base attributes must also correspond to directive inputs.
40160 if (!meta.inputs.hasBindingPropertyName(attr)) {
40161 return false;
40162 }
40163 // This attribute is compatible.
40164 return true;
40165 };
40166 if (!attributes.every(isValid)) {
40167 continue;
40168 }
40169 // This attribute is valid as a structural attribute for this directive.
40170 structuralAttributes.push(baseAttr);
40171 }
40172 return structuralAttributes;
40173 }
40174
40175 /**
40176 * @license
40177 * Copyright Google LLC All Rights Reserved.
40178 *
40179 * Use of this source code is governed by an MIT-style license that can be
40180 * found in the LICENSE file at https://angular.io/license
40181 */
40182 var CompletionNodeContext;
40183 (function (CompletionNodeContext) {
40184 CompletionNodeContext[CompletionNodeContext["None"] = 0] = "None";
40185 CompletionNodeContext[CompletionNodeContext["ElementTag"] = 1] = "ElementTag";
40186 CompletionNodeContext[CompletionNodeContext["ElementAttributeKey"] = 2] = "ElementAttributeKey";
40187 CompletionNodeContext[CompletionNodeContext["ElementAttributeValue"] = 3] = "ElementAttributeValue";
40188 CompletionNodeContext[CompletionNodeContext["EventValue"] = 4] = "EventValue";
40189 CompletionNodeContext[CompletionNodeContext["TwoWayBinding"] = 5] = "TwoWayBinding";
40190 })(CompletionNodeContext || (CompletionNodeContext = {}));
40191 /**
40192 * Performs autocompletion operations on a given node in the template.
40193 *
40194 * This class acts as a closure around all of the context required to perform the 3 autocompletion
40195 * operations (completions, get details, and get symbol) at a specific node.
40196 *
40197 * The generic `N` type for the template node is narrowed internally for certain operations, as the
40198 * compiler operations required to implement completion may be different for different node types.
40199 *
40200 * @param N type of the template node in question, narrowed accordingly.
40201 */
40202 class CompletionBuilder {
40203 constructor(tsLS, compiler, component, node, nodeContext, nodeParent, template) {
40204 this.tsLS = tsLS;
40205 this.compiler = compiler;
40206 this.component = component;
40207 this.node = node;
40208 this.nodeContext = nodeContext;
40209 this.nodeParent = nodeParent;
40210 this.template = template;
40211 this.typeChecker = this.compiler.getNextProgram().getTypeChecker();
40212 this.templateTypeChecker = this.compiler.getTemplateTypeChecker();
40213 }
40214 /**
40215 * Analogue for `ts.LanguageService.getCompletionsAtPosition`.
40216 */
40217 getCompletionsAtPosition(options) {
40218 if (this.isPropertyExpressionCompletion()) {
40219 return this.getPropertyExpressionCompletion(options);
40220 }
40221 else if (this.isElementTagCompletion()) {
40222 return this.getElementTagCompletion();
40223 }
40224 else if (this.isElementAttributeCompletion()) {
40225 return this.getElementAttributeCompletions();
40226 }
40227 else if (this.isPipeCompletion()) {
40228 return this.getPipeCompletions();
40229 }
40230 else {
40231 return undefined;
40232 }
40233 }
40234 /**
40235 * Analogue for `ts.LanguageService.getCompletionEntryDetails`.
40236 */
40237 getCompletionEntryDetails(entryName, formatOptions, preferences) {
40238 if (this.isPropertyExpressionCompletion()) {
40239 return this.getPropertyExpressionCompletionDetails(entryName, formatOptions, preferences);
40240 }
40241 else if (this.isElementTagCompletion()) {
40242 return this.getElementTagCompletionDetails(entryName);
40243 }
40244 else if (this.isElementAttributeCompletion()) {
40245 return this.getElementAttributeCompletionDetails(entryName);
40246 }
40247 }
40248 /**
40249 * Analogue for `ts.LanguageService.getCompletionEntrySymbol`.
40250 */
40251 getCompletionEntrySymbol(name) {
40252 if (this.isPropertyExpressionCompletion()) {
40253 return this.getPropertyExpressionCompletionSymbol(name);
40254 }
40255 else if (this.isElementTagCompletion()) {
40256 return this.getElementTagCompletionSymbol(name);
40257 }
40258 else if (this.isElementAttributeCompletion()) {
40259 return this.getElementAttributeCompletionSymbol(name);
40260 }
40261 else {
40262 return undefined;
40263 }
40264 }
40265 /**
40266 * Determine if the current node is the completion of a property expression, and narrow the type
40267 * of `this.node` if so.
40268 *
40269 * This narrowing gives access to additional methods related to completion of property
40270 * expressions.
40271 */
40272 isPropertyExpressionCompletion() {
40273 return this.node instanceof PropertyRead || this.node instanceof MethodCall ||
40274 this.node instanceof SafePropertyRead || this.node instanceof SafeMethodCall ||
40275 this.node instanceof PropertyWrite || this.node instanceof EmptyExpr ||
40276 // BoundEvent nodes only count as property completions if in an EventValue context.
40277 (this.node instanceof BoundEvent && this.nodeContext === CompletionNodeContext.EventValue);
40278 }
40279 /**
40280 * Get completions for property expressions.
40281 */
40282 getPropertyExpressionCompletion(options) {
40283 if (this.node instanceof EmptyExpr || this.node instanceof BoundEvent ||
40284 this.node.receiver instanceof ImplicitReceiver) {
40285 return this.getGlobalPropertyExpressionCompletion(options);
40286 }
40287 else {
40288 const location = this.compiler.getTemplateTypeChecker().getExpressionCompletionLocation(this.node, this.component);
40289 if (location === null) {
40290 return undefined;
40291 }
40292 const tsResults = this.tsLS.getCompletionsAtPosition(location.shimPath, location.positionInShimFile, options);
40293 if (tsResults === undefined) {
40294 return undefined;
40295 }
40296 const replacementSpan = makeReplacementSpanFromAst(this.node);
40297 let ngResults = [];
40298 for (const result of tsResults.entries) {
40299 ngResults.push(Object.assign(Object.assign({}, result), { replacementSpan }));
40300 }
40301 return Object.assign(Object.assign({}, tsResults), { entries: ngResults });
40302 }
40303 }
40304 /**
40305 * Get the details of a specific completion for a property expression.
40306 */
40307 getPropertyExpressionCompletionDetails(entryName, formatOptions, preferences) {
40308 let details = undefined;
40309 if (this.node instanceof EmptyExpr || this.node instanceof BoundEvent ||
40310 this.node.receiver instanceof ImplicitReceiver) {
40311 details =
40312 this.getGlobalPropertyExpressionCompletionDetails(entryName, formatOptions, preferences);
40313 }
40314 else {
40315 const location = this.compiler.getTemplateTypeChecker().getExpressionCompletionLocation(this.node, this.component);
40316 if (location === null) {
40317 return undefined;
40318 }
40319 details = this.tsLS.getCompletionEntryDetails(location.shimPath, location.positionInShimFile, entryName, formatOptions,
40320 /* source */ undefined, preferences);
40321 }
40322 if (details !== undefined) {
40323 details.displayParts = filterAliasImports(details.displayParts);
40324 }
40325 return details;
40326 }
40327 /**
40328 * Get the `ts.Symbol` for a specific completion for a property expression.
40329 */
40330 getPropertyExpressionCompletionSymbol(name) {
40331 if (this.node instanceof EmptyExpr || this.node instanceof LiteralPrimitive ||
40332 this.node instanceof BoundEvent || this.node.receiver instanceof ImplicitReceiver) {
40333 return this.getGlobalPropertyExpressionCompletionSymbol(name);
40334 }
40335 else {
40336 const location = this.compiler.getTemplateTypeChecker().getExpressionCompletionLocation(this.node, this.component);
40337 if (location === null) {
40338 return undefined;
40339 }
40340 return this.tsLS.getCompletionEntrySymbol(location.shimPath, location.positionInShimFile, name, /* source */ undefined);
40341 }
40342 }
40343 /**
40344 * Get completions for a property expression in a global context (e.g. `{{y|}}`).
40345 */
40346 getGlobalPropertyExpressionCompletion(options) {
40347 const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component);
40348 if (completions === null) {
40349 return undefined;
40350 }
40351 const { componentContext, templateContext } = completions;
40352 const replacementSpan = makeReplacementSpanFromAst(this.node);
40353 // Merge TS completion results with results from the template scope.
40354 let entries = [];
40355 const tsLsCompletions = this.tsLS.getCompletionsAtPosition(componentContext.shimPath, componentContext.positionInShimFile, options);
40356 if (tsLsCompletions !== undefined) {
40357 for (const tsCompletion of tsLsCompletions.entries) {
40358 // Skip completions that are shadowed by a template entity definition.
40359 if (templateContext.has(tsCompletion.name)) {
40360 continue;
40361 }
40362 entries.push(Object.assign(Object.assign({}, tsCompletion), {
40363 // Substitute the TS completion's `replacementSpan` (which uses offsets within the TCB)
40364 // with the `replacementSpan` within the template source.
40365 replacementSpan }));
40366 }
40367 }
40368 for (const [name, entity] of templateContext) {
40369 entries.push({
40370 name,
40371 sortText: name,
40372 replacementSpan,
40373 kindModifiers: ts$1.ScriptElementKindModifier.none,
40374 kind: unsafeCastDisplayInfoKindToScriptElementKind(entity.kind === CompletionKind.Reference ? DisplayInfoKind.REFERENCE :
40375 DisplayInfoKind.VARIABLE),
40376 });
40377 }
40378 return {
40379 entries,
40380 // Although this completion is "global" in the sense of an Angular expression (there is no
40381 // explicit receiver), it is not "global" in a TypeScript sense since Angular expressions have
40382 // the component as an implicit receiver.
40383 isGlobalCompletion: false,
40384 isMemberCompletion: true,
40385 isNewIdentifierLocation: false,
40386 };
40387 }
40388 /**
40389 * Get the details of a specific completion for a property expression in a global context (e.g.
40390 * `{{y|}}`).
40391 */
40392 getGlobalPropertyExpressionCompletionDetails(entryName, formatOptions, preferences) {
40393 const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component);
40394 if (completions === null) {
40395 return undefined;
40396 }
40397 const { componentContext, templateContext } = completions;
40398 if (templateContext.has(entryName)) {
40399 const entry = templateContext.get(entryName);
40400 // Entries that reference a symbol in the template context refer either to local references or
40401 // variables.
40402 const symbol = this.templateTypeChecker.getSymbolOfNode(entry.node, this.component);
40403 if (symbol === null) {
40404 return undefined;
40405 }
40406 const { kind, displayParts, documentation } = getSymbolDisplayInfo(this.tsLS, this.typeChecker, symbol);
40407 return {
40408 kind: unsafeCastDisplayInfoKindToScriptElementKind(kind),
40409 name: entryName,
40410 kindModifiers: ts$1.ScriptElementKindModifier.none,
40411 displayParts,
40412 documentation,
40413 };
40414 }
40415 else {
40416 return this.tsLS.getCompletionEntryDetails(componentContext.shimPath, componentContext.positionInShimFile, entryName, formatOptions,
40417 /* source */ undefined, preferences);
40418 }
40419 }
40420 /**
40421 * Get the `ts.Symbol` of a specific completion for a property expression in a global context
40422 * (e.g.
40423 * `{{y|}}`).
40424 */
40425 getGlobalPropertyExpressionCompletionSymbol(entryName) {
40426 const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component);
40427 if (completions === null) {
40428 return undefined;
40429 }
40430 const { componentContext, templateContext } = completions;
40431 if (templateContext.has(entryName)) {
40432 const node = templateContext.get(entryName).node;
40433 const symbol = this.templateTypeChecker.getSymbolOfNode(node, this.component);
40434 if (symbol === null || symbol.tsSymbol === null) {
40435 return undefined;
40436 }
40437 return symbol.tsSymbol;
40438 }
40439 else {
40440 return this.tsLS.getCompletionEntrySymbol(componentContext.shimPath, componentContext.positionInShimFile, entryName,
40441 /* source */ undefined);
40442 }
40443 }
40444 isElementTagCompletion() {
40445 return this.node instanceof Element &&
40446 this.nodeContext === CompletionNodeContext.ElementTag;
40447 }
40448 getElementTagCompletion() {
40449 const templateTypeChecker = this.compiler.getTemplateTypeChecker();
40450 // The replacementSpan is the tag name.
40451 const replacementSpan = {
40452 start: this.node.sourceSpan.start.offset + 1,
40453 length: this.node.name.length,
40454 };
40455 const entries = Array.from(templateTypeChecker.getPotentialElementTags(this.component))
40456 .map(([tag, directive]) => ({
40457 kind: tagCompletionKind(directive),
40458 name: tag,
40459 sortText: tag,
40460 replacementSpan,
40461 }));
40462 return {
40463 entries,
40464 isGlobalCompletion: false,
40465 isMemberCompletion: false,
40466 isNewIdentifierLocation: false,
40467 };
40468 }
40469 getElementTagCompletionDetails(entryName) {
40470 const templateTypeChecker = this.compiler.getTemplateTypeChecker();
40471 const tagMap = templateTypeChecker.getPotentialElementTags(this.component);
40472 if (!tagMap.has(entryName)) {
40473 return undefined;
40474 }
40475 const directive = tagMap.get(entryName);
40476 let displayParts;
40477 let documentation = undefined;
40478 if (directive === null) {
40479 displayParts = [];
40480 }
40481 else {
40482 const displayInfo = getDirectiveDisplayInfo(this.tsLS, directive);
40483 displayParts = displayInfo.displayParts;
40484 documentation = displayInfo.documentation;
40485 }
40486 return {
40487 kind: tagCompletionKind(directive),
40488 name: entryName,
40489 kindModifiers: ts$1.ScriptElementKindModifier.none,
40490 displayParts,
40491 documentation,
40492 };
40493 }
40494 getElementTagCompletionSymbol(entryName) {
40495 const templateTypeChecker = this.compiler.getTemplateTypeChecker();
40496 const tagMap = templateTypeChecker.getPotentialElementTags(this.component);
40497 if (!tagMap.has(entryName)) {
40498 return undefined;
40499 }
40500 const directive = tagMap.get(entryName);
40501 return directive === null || directive === void 0 ? void 0 : directive.tsSymbol;
40502 }
40503 isElementAttributeCompletion() {
40504 return (this.nodeContext === CompletionNodeContext.ElementAttributeKey ||
40505 this.nodeContext === CompletionNodeContext.TwoWayBinding) &&
40506 (this.node instanceof Element || this.node instanceof BoundAttribute ||
40507 this.node instanceof TextAttribute || this.node instanceof BoundEvent);
40508 }
40509 getElementAttributeCompletions() {
40510 let element;
40511 if (this.node instanceof Element) {
40512 element = this.node;
40513 }
40514 else if (this.nodeParent instanceof Element || this.nodeParent instanceof Template) {
40515 element = this.nodeParent;
40516 }
40517 else {
40518 // Nothing to do without an element to process.
40519 return undefined;
40520 }
40521 let replacementSpan = undefined;
40522 if ((this.node instanceof BoundAttribute || this.node instanceof BoundEvent ||
40523 this.node instanceof TextAttribute) &&
40524 this.node.keySpan !== undefined) {
40525 replacementSpan = makeReplacementSpanFromParseSourceSpan(this.node.keySpan);
40526 }
40527 const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
40528 let entries = [];
40529 for (const completion of attrTable.values()) {
40530 // First, filter out completions that don't make sense for the current node. For example, if
40531 // the user is completing on a property binding `[foo|]`, don't offer output event
40532 // completions.
40533 switch (completion.kind) {
40534 case AttributeCompletionKind.DomAttribute:
40535 case AttributeCompletionKind.DomProperty:
40536 if (this.node instanceof BoundEvent) {
40537 continue;
40538 }
40539 break;
40540 case AttributeCompletionKind.DirectiveInput:
40541 if (this.node instanceof BoundEvent) {
40542 continue;
40543 }
40544 if (!completion.twoWayBindingSupported &&
40545 this.nodeContext === CompletionNodeContext.TwoWayBinding) {
40546 continue;
40547 }
40548 break;
40549 case AttributeCompletionKind.DirectiveOutput:
40550 if (this.node instanceof BoundAttribute) {
40551 continue;
40552 }
40553 break;
40554 case AttributeCompletionKind.DirectiveAttribute:
40555 if (this.node instanceof BoundAttribute ||
40556 this.node instanceof BoundEvent) {
40557 continue;
40558 }
40559 break;
40560 }
40561 // Is the completion in an attribute context (instead of a property context)?
40562 const isAttributeContext = (this.node instanceof Element || this.node instanceof TextAttribute);
40563 // Is the completion for an element (not an <ng-template>)?
40564 const isElementContext = this.node instanceof Element || this.nodeParent instanceof Element;
40565 addAttributeCompletionEntries(entries, completion, isAttributeContext, isElementContext, replacementSpan);
40566 }
40567 return {
40568 entries,
40569 isGlobalCompletion: false,
40570 isMemberCompletion: false,
40571 isNewIdentifierLocation: true,
40572 };
40573 }
40574 getElementAttributeCompletionDetails(entryName) {
40575 // `entryName` here may be `foo` or `[foo]`, depending on which suggested completion the user
40576 // chose. Strip off any binding syntax to get the real attribute name.
40577 const { name, kind } = stripBindingSugar(entryName);
40578 let element;
40579 if (this.node instanceof Element || this.node instanceof Template) {
40580 element = this.node;
40581 }
40582 else if (this.nodeParent instanceof Element || this.nodeParent instanceof Template) {
40583 element = this.nodeParent;
40584 }
40585 else {
40586 // Nothing to do without an element to process.
40587 return undefined;
40588 }
40589 const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
40590 if (!attrTable.has(name)) {
40591 return undefined;
40592 }
40593 const completion = attrTable.get(name);
40594 let displayParts;
40595 let documentation = undefined;
40596 let info;
40597 switch (completion.kind) {
40598 case AttributeCompletionKind.DomAttribute:
40599 case AttributeCompletionKind.DomProperty:
40600 // TODO(alxhub): ideally we would show the same documentation as quick info here. However,
40601 // since these bindings don't exist in the TCB, there is no straightforward way to retrieve
40602 // a `ts.Symbol` for the field in the TS DOM definition.
40603 displayParts = [];
40604 break;
40605 case AttributeCompletionKind.DirectiveAttribute:
40606 info = getDirectiveDisplayInfo(this.tsLS, completion.directive);
40607 displayParts = info.displayParts;
40608 documentation = info.documentation;
40609 break;
40610 case AttributeCompletionKind.DirectiveInput:
40611 case AttributeCompletionKind.DirectiveOutput:
40612 const propertySymbol = getAttributeCompletionSymbol(completion, this.typeChecker);
40613 if (propertySymbol === null) {
40614 return undefined;
40615 }
40616 info = getTsSymbolDisplayInfo(this.tsLS, this.typeChecker, propertySymbol, completion.kind === AttributeCompletionKind.DirectiveInput ? DisplayInfoKind.PROPERTY :
40617 DisplayInfoKind.EVENT, completion.directive.tsSymbol.name);
40618 if (info === null) {
40619 return undefined;
40620 }
40621 displayParts = info.displayParts;
40622 documentation = info.documentation;
40623 }
40624 return {
40625 name: entryName,
40626 kind: unsafeCastDisplayInfoKindToScriptElementKind(kind),
40627 kindModifiers: ts$1.ScriptElementKindModifier.none,
40628 displayParts: [],
40629 documentation,
40630 };
40631 }
40632 getElementAttributeCompletionSymbol(attribute) {
40633 var _a;
40634 const { name } = stripBindingSugar(attribute);
40635 let element;
40636 if (this.node instanceof Element || this.node instanceof Template) {
40637 element = this.node;
40638 }
40639 else if (this.nodeParent instanceof Element || this.nodeParent instanceof Template) {
40640 element = this.nodeParent;
40641 }
40642 else {
40643 // Nothing to do without an element to process.
40644 return undefined;
40645 }
40646 const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
40647 if (!attrTable.has(name)) {
40648 return undefined;
40649 }
40650 const completion = attrTable.get(name);
40651 return (_a = getAttributeCompletionSymbol(completion, this.typeChecker)) !== null && _a !== void 0 ? _a : undefined;
40652 }
40653 isPipeCompletion() {
40654 return this.node instanceof BindingPipe;
40655 }
40656 getPipeCompletions() {
40657 const pipes = this.templateTypeChecker.getPipesInScope(this.component);
40658 if (pipes === null) {
40659 return undefined;
40660 }
40661 const replacementSpan = makeReplacementSpanFromAst(this.node);
40662 const entries = pipes.map(pipe => ({
40663 name: pipe.name,
40664 sortText: pipe.name,
40665 kind: unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.PIPE),
40666 replacementSpan,
40667 }));
40668 return {
40669 entries,
40670 isGlobalCompletion: false,
40671 isMemberCompletion: false,
40672 isNewIdentifierLocation: false,
40673 };
40674 }
40675 }
40676 function makeReplacementSpanFromParseSourceSpan(span) {
40677 return {
40678 start: span.start.offset,
40679 length: span.end.offset - span.start.offset,
40680 };
40681 }
40682 function makeReplacementSpanFromAst(node) {
40683 if ((node instanceof EmptyExpr || node instanceof LiteralPrimitive ||
40684 node instanceof BoundEvent)) {
40685 // empty nodes do not replace any existing text
40686 return undefined;
40687 }
40688 return {
40689 start: node.nameSpan.start,
40690 length: node.nameSpan.end - node.nameSpan.start,
40691 };
40692 }
40693 function tagCompletionKind(directive) {
40694 let kind;
40695 if (directive === null) {
40696 kind = DisplayInfoKind.ELEMENT;
40697 }
40698 else if (directive.isComponent) {
40699 kind = DisplayInfoKind.COMPONENT;
40700 }
40701 else {
40702 kind = DisplayInfoKind.DIRECTIVE;
40703 }
40704 return unsafeCastDisplayInfoKindToScriptElementKind(kind);
40705 }
40706 const BINDING_SUGAR = /[\[\(\)\]]/g;
40707 function stripBindingSugar(binding) {
40708 const name = binding.replace(BINDING_SUGAR, '');
40709 if (binding.startsWith('[')) {
40710 return { name, kind: DisplayInfoKind.PROPERTY };
40711 }
40712 else if (binding.startsWith('(')) {
40713 return { name, kind: DisplayInfoKind.EVENT };
40714 }
40715 else {
40716 return { name, kind: DisplayInfoKind.ATTRIBUTE };
40717 }
40718 }
40719
40720 /**
40721 * @license
40722 * Copyright Google LLC All Rights Reserved.
40723 *
40724 * Use of this source code is governed by an MIT-style license that can be
40725 * found in the LICENSE file at https://angular.io/license
40726 */
40727 /**
40728 * Differentiates the various kinds of `TargetNode`s.
40729 */
40730 var TargetNodeKind;
40731 (function (TargetNodeKind) {
40732 TargetNodeKind[TargetNodeKind["RawExpression"] = 0] = "RawExpression";
40733 TargetNodeKind[TargetNodeKind["RawTemplateNode"] = 1] = "RawTemplateNode";
40734 TargetNodeKind[TargetNodeKind["ElementInTagContext"] = 2] = "ElementInTagContext";
40735 TargetNodeKind[TargetNodeKind["ElementInBodyContext"] = 3] = "ElementInBodyContext";
40736 TargetNodeKind[TargetNodeKind["AttributeInKeyContext"] = 4] = "AttributeInKeyContext";
40737 TargetNodeKind[TargetNodeKind["AttributeInValueContext"] = 5] = "AttributeInValueContext";
40738 TargetNodeKind[TargetNodeKind["TwoWayBindingContext"] = 6] = "TwoWayBindingContext";
40739 })(TargetNodeKind || (TargetNodeKind = {}));
40740 /**
40741 * This special marker is added to the path when the cursor is within the sourceSpan but not the key
40742 * or value span of a node with key/value spans.
40743 */
40744 const OUTSIDE_K_V_MARKER = new AST(new ParseSpan(-1, -1), new AbsoluteSourceSpan(-1, -1));
40745 /**
40746 * Return the template AST node or expression AST node that most accurately
40747 * represents the node at the specified cursor `position`.
40748 *
40749 * @param template AST tree of the template
40750 * @param position target cursor position
40751 */
40752 function getTargetAtPosition(template, position) {
40753 const path = TemplateTargetVisitor.visitTemplate(template, position);
40754 if (path.length === 0) {
40755 return null;
40756 }
40757 const candidate = path[path.length - 1];
40758 // Walk up the result nodes to find the nearest `t.Template` which contains the targeted node.
40759 let context = null;
40760 for (let i = path.length - 2; i >= 0; i--) {
40761 const node = path[i];
40762 if (node instanceof Template) {
40763 context = node;
40764 break;
40765 }
40766 }
40767 // Given the candidate node, determine the full targeted context.
40768 let nodeInContext;
40769 if (candidate instanceof AST) {
40770 nodeInContext = {
40771 kind: TargetNodeKind.RawExpression,
40772 node: candidate,
40773 };
40774 }
40775 else if (candidate instanceof Element) {
40776 // Elements have two contexts: the tag context (position is within the element tag) or the
40777 // element body context (position is outside of the tag name, but still in the element).
40778 // Calculate the end of the element tag name. Any position beyond this is in the element body.
40779 const tagEndPos = candidate.sourceSpan.start.offset + 1 /* '<' element open */ + candidate.name.length;
40780 if (position > tagEndPos) {
40781 // Position is within the element body
40782 nodeInContext = {
40783 kind: TargetNodeKind.ElementInBodyContext,
40784 node: candidate,
40785 };
40786 }
40787 else {
40788 nodeInContext = {
40789 kind: TargetNodeKind.ElementInTagContext,
40790 node: candidate,
40791 };
40792 }
40793 }
40794 else if ((candidate instanceof BoundAttribute || candidate instanceof BoundEvent ||
40795 candidate instanceof TextAttribute) &&
40796 candidate.keySpan !== undefined) {
40797 const previousCandidate = path[path.length - 2];
40798 if (candidate instanceof BoundEvent && previousCandidate instanceof BoundAttribute &&
40799 candidate.name === previousCandidate.name + 'Change') {
40800 const boundAttribute = previousCandidate;
40801 const boundEvent = candidate;
40802 nodeInContext = {
40803 kind: TargetNodeKind.TwoWayBindingContext,
40804 nodes: [boundAttribute, boundEvent],
40805 };
40806 }
40807 else if (isWithin(position, candidate.keySpan)) {
40808 nodeInContext = {
40809 kind: TargetNodeKind.AttributeInKeyContext,
40810 node: candidate,
40811 };
40812 }
40813 else {
40814 nodeInContext = {
40815 kind: TargetNodeKind.AttributeInValueContext,
40816 node: candidate,
40817 };
40818 }
40819 }
40820 else {
40821 nodeInContext = {
40822 kind: TargetNodeKind.RawTemplateNode,
40823 node: candidate,
40824 };
40825 }
40826 let parent = null;
40827 if (nodeInContext.kind === TargetNodeKind.TwoWayBindingContext && path.length >= 3) {
40828 parent = path[path.length - 3];
40829 }
40830 else if (path.length >= 2) {
40831 parent = path[path.length - 2];
40832 }
40833 return { position, context: nodeInContext, template: context, parent };
40834 }
40835 /**
40836 * Visitor which, given a position and a template, identifies the node within the template at that
40837 * position, as well as records the path of increasingly nested nodes that were traversed to reach
40838 * that position.
40839 */
40840 class TemplateTargetVisitor {
40841 // Position must be absolute in the source file.
40842 constructor(position) {
40843 this.position = position;
40844 // We need to keep a path instead of the last node because we might need more
40845 // context for the last node, for example what is the parent node?
40846 this.path = [];
40847 }
40848 static visitTemplate(template, position) {
40849 const visitor = new TemplateTargetVisitor(position);
40850 visitor.visitAll(template);
40851 const { path } = visitor;
40852 const strictPath = path.filter(v => v !== OUTSIDE_K_V_MARKER);
40853 const candidate = strictPath[strictPath.length - 1];
40854 const matchedASourceSpanButNotAKvSpan = path.some(v => v === OUTSIDE_K_V_MARKER);
40855 if (matchedASourceSpanButNotAKvSpan &&
40856 (candidate instanceof Template || candidate instanceof Element)) {
40857 // Template nodes with key and value spans are always defined on a `t.Template` or
40858 // `t.Element`. If we found a node on a template with a `sourceSpan` that includes the cursor,
40859 // it is possible that we are outside the k/v spans (i.e. in-between them). If this is the
40860 // case and we do not have any other candidate matches on the `t.Element` or `t.Template`, we
40861 // want to return no results. Otherwise, the `t.Element`/`t.Template` result is incorrect for
40862 // that cursor position.
40863 return [];
40864 }
40865 return strictPath;
40866 }
40867 visit(node) {
40868 const { start, end } = getSpanIncludingEndTag(node);
40869 if (!isWithin(this.position, { start, end })) {
40870 return;
40871 }
40872 const last = this.path[this.path.length - 1];
40873 const withinKeySpanOfLastNode = last && isTemplateNodeWithKeyAndValue(last) && isWithin(this.position, last.keySpan);
40874 const withinKeySpanOfCurrentNode = isTemplateNodeWithKeyAndValue(node) && isWithin(this.position, node.keySpan);
40875 if (withinKeySpanOfLastNode && !withinKeySpanOfCurrentNode) {
40876 // We've already identified that we are within a `keySpan` of a node.
40877 // Unless we are _also_ in the `keySpan` of the current node (happens with two way bindings),
40878 // we should stop processing nodes at this point to prevent matching any other nodes. This can
40879 // happen when the end span of a different node touches the start of the keySpan for the
40880 // candidate node. Because our `isWithin` logic is inclusive on both ends, we can match both
40881 // nodes.
40882 return;
40883 }
40884 if (isTemplateNodeWithKeyAndValue(node) && !isWithinKeyValue(this.position, node)) {
40885 // If cursor is within source span but not within key span or value span,
40886 // do not return the node.
40887 this.path.push(OUTSIDE_K_V_MARKER);
40888 }
40889 else {
40890 this.path.push(node);
40891 node.visit(this);
40892 }
40893 }
40894 visitElement(element) {
40895 this.visitElementOrTemplate(element);
40896 }
40897 visitTemplate(template) {
40898 this.visitElementOrTemplate(template);
40899 }
40900 visitElementOrTemplate(element) {
40901 this.visitAll(element.attributes);
40902 this.visitAll(element.inputs);
40903 this.visitAll(element.outputs);
40904 if (element instanceof Template) {
40905 this.visitAll(element.templateAttrs);
40906 }
40907 this.visitAll(element.references);
40908 if (element instanceof Template) {
40909 this.visitAll(element.variables);
40910 }
40911 // If we get here and have not found a candidate node on the element itself, proceed with
40912 // looking for a more specific node on the element children.
40913 if (this.path[this.path.length - 1] !== element) {
40914 return;
40915 }
40916 this.visitAll(element.children);
40917 }
40918 visitContent(content) {
40919 visitAll(this, content.attributes);
40920 }
40921 visitVariable(variable) {
40922 // Variable has no template nodes or expression nodes.
40923 }
40924 visitReference(reference) {
40925 // Reference has no template nodes or expression nodes.
40926 }
40927 visitTextAttribute(attribute) {
40928 // Text attribute has no template nodes or expression nodes.
40929 }
40930 visitBoundAttribute(attribute) {
40931 const visitor = new ExpressionVisitor$1(this.position);
40932 visitor.visit(attribute.value, this.path);
40933 }
40934 visitBoundEvent(event) {
40935 // An event binding with no value (e.g. `(event|)`) parses to a `BoundEvent` with a
40936 // `LiteralPrimitive` handler with value `'ERROR'`, as opposed to a property binding with no
40937 // value which has an `EmptyExpr` as its value. This is a synthetic node created by the binding
40938 // parser, and is not suitable to use for Language Service analysis. Skip it.
40939 //
40940 // TODO(alxhub): modify the parser to generate an `EmptyExpr` instead.
40941 let handler = event.handler;
40942 if (handler instanceof ASTWithSource) {
40943 handler = handler.ast;
40944 }
40945 if (handler instanceof LiteralPrimitive && handler.value === 'ERROR') {
40946 return;
40947 }
40948 const visitor = new ExpressionVisitor$1(this.position);
40949 visitor.visit(event.handler, this.path);
40950 }
40951 visitText(text) {
40952 // Text has no template nodes or expression nodes.
40953 }
40954 visitBoundText(text) {
40955 const visitor = new ExpressionVisitor$1(this.position);
40956 visitor.visit(text.value, this.path);
40957 }
40958 visitIcu(icu) {
40959 for (const boundText of Object.values(icu.vars)) {
40960 this.visit(boundText);
40961 }
40962 for (const boundTextOrText of Object.values(icu.placeholders)) {
40963 this.visit(boundTextOrText);
40964 }
40965 }
40966 visitAll(nodes) {
40967 for (const node of nodes) {
40968 this.visit(node);
40969 }
40970 }
40971 }
40972 class ExpressionVisitor$1 extends RecursiveAstVisitor {
40973 // Position must be absolute in the source file.
40974 constructor(position) {
40975 super();
40976 this.position = position;
40977 }
40978 visit(node, path) {
40979 if (node instanceof ASTWithSource) {
40980 // In order to reduce noise, do not include `ASTWithSource` in the path.
40981 // For the purpose of source spans, there is no difference between
40982 // `ASTWithSource` and and underlying node that it wraps.
40983 node = node.ast;
40984 }
40985 // The third condition is to account for the implicit receiver, which should
40986 // not be visited.
40987 if (isWithin(this.position, node.sourceSpan) && !(node instanceof ImplicitReceiver)) {
40988 path.push(node);
40989 node.visit(this, path);
40990 }
40991 }
40992 }
40993 function getSpanIncludingEndTag(ast) {
40994 const result = {
40995 start: ast.sourceSpan.start.offset,
40996 end: ast.sourceSpan.end.offset,
40997 };
40998 // For Element and Template node, sourceSpan.end is the end of the opening
40999 // tag. For the purpose of language service, we need to actually recognize
41000 // the end of the closing tag. Otherwise, for situation like
41001 // <my-component></my-comp¦onent> where the cursor is in the closing tag
41002 // we will not be able to return any information.
41003 if ((ast instanceof Element || ast instanceof Template) && ast.endSourceSpan) {
41004 result.end = ast.endSourceSpan.end.offset;
41005 }
41006 return result;
41007 }
41008
41009 /**
41010 * @license
41011 * Copyright Google LLC All Rights Reserved.
41012 *
41013 * Use of this source code is governed by an MIT-style license that can be
41014 * found in the LICENSE file at https://angular.io/license
41015 */
41016 class DefinitionBuilder {
41017 constructor(tsLS, compiler) {
41018 this.tsLS = tsLS;
41019 this.compiler = compiler;
41020 }
41021 getDefinitionAndBoundSpan(fileName, position) {
41022 var _a;
41023 const templateInfo = getTemplateInfoAtPosition(fileName, position, this.compiler);
41024 if (templateInfo === undefined) {
41025 // We were unable to get a template at the given position. If we are in a TS file, instead
41026 // attempt to get an Angular definition at the location inside a TS file (examples of this
41027 // would be templateUrl or a url in styleUrls).
41028 if (!isTypeScriptFile(fileName)) {
41029 return;
41030 }
41031 return getDefinitionForExpressionAtPosition(fileName, position, this.compiler);
41032 }
41033 const definitionMetas = this.getDefinitionMetaAtPosition(templateInfo, position);
41034 if (definitionMetas === undefined) {
41035 return undefined;
41036 }
41037 const definitions = [];
41038 for (const definitionMeta of definitionMetas) {
41039 // The `$event` of event handlers would point to the $event parameter in the shim file, as in
41040 // `_t3["x"].subscribe(function ($event): any { $event }) ;`
41041 // If we wanted to return something for this, it would be more appropriate for something like
41042 // `getTypeDefinition`.
41043 if (isDollarEvent(definitionMeta.node)) {
41044 continue;
41045 }
41046 definitions.push(...((_a = this.getDefinitionsForSymbol(Object.assign(Object.assign({}, definitionMeta), templateInfo))) !== null && _a !== void 0 ? _a : []));
41047 }
41048 if (definitions.length === 0) {
41049 return undefined;
41050 }
41051 return { definitions, textSpan: getTextSpanOfNode(definitionMetas[0].node) };
41052 }
41053 getDefinitionsForSymbol({ symbol, node, parent, component }) {
41054 switch (symbol.kind) {
41055 case SymbolKind.Directive:
41056 case SymbolKind.Element:
41057 case SymbolKind.Template:
41058 case SymbolKind.DomBinding:
41059 // Though it is generally more appropriate for the above symbol definitions to be
41060 // associated with "type definitions" since the location in the template is the
41061 // actual definition location, the better user experience would be to allow
41062 // LS users to "go to definition" on an item in the template that maps to a class and be
41063 // taken to the directive or HTML class.
41064 return this.getTypeDefinitionsForTemplateInstance(symbol, node);
41065 case SymbolKind.Pipe: {
41066 if (symbol.tsSymbol !== null) {
41067 return this.getDefinitionsForSymbols(symbol);
41068 }
41069 else {
41070 // If there is no `ts.Symbol` for the pipe transform, we want to return the
41071 // type definition (the pipe class).
41072 return this.getTypeDefinitionsForSymbols(symbol.classSymbol);
41073 }
41074 }
41075 case SymbolKind.Output:
41076 case SymbolKind.Input: {
41077 const bindingDefs = this.getDefinitionsForSymbols(...symbol.bindings);
41078 // Also attempt to get directive matches for the input name. If there is a directive that
41079 // has the input name as part of the selector, we want to return that as well.
41080 const directiveDefs = this.getDirectiveTypeDefsForBindingNode(node, parent, component);
41081 return [...bindingDefs, ...directiveDefs];
41082 }
41083 case SymbolKind.Variable:
41084 case SymbolKind.Reference: {
41085 const definitions = [];
41086 if (symbol.declaration !== node) {
41087 const shimLocation = symbol.kind === SymbolKind.Variable ? symbol.localVarLocation :
41088 symbol.referenceVarLocation;
41089 const mapping = getTemplateLocationFromShimLocation(this.compiler.getTemplateTypeChecker(), shimLocation.shimPath, shimLocation.positionInShimFile);
41090 if (mapping !== null) {
41091 definitions.push({
41092 name: symbol.declaration.name,
41093 containerName: '',
41094 containerKind: ts$1.ScriptElementKind.unknown,
41095 kind: ts$1.ScriptElementKind.variableElement,
41096 textSpan: getTextSpanOfNode(symbol.declaration),
41097 contextSpan: toTextSpan(symbol.declaration.sourceSpan),
41098 fileName: mapping.templateUrl,
41099 });
41100 }
41101 }
41102 if (symbol.kind === SymbolKind.Variable) {
41103 definitions.push(...this.getDefinitionsForSymbols({ shimLocation: symbol.initializerLocation }));
41104 }
41105 return definitions;
41106 }
41107 case SymbolKind.Expression: {
41108 return this.getDefinitionsForSymbols(symbol);
41109 }
41110 }
41111 }
41112 getDefinitionsForSymbols(...symbols) {
41113 return flatMap(symbols, ({ shimLocation }) => {
41114 var _a;
41115 const { shimPath, positionInShimFile } = shimLocation;
41116 return (_a = this.tsLS.getDefinitionAtPosition(shimPath, positionInShimFile)) !== null && _a !== void 0 ? _a : [];
41117 });
41118 }
41119 getTypeDefinitionsAtPosition(fileName, position) {
41120 const templateInfo = getTemplateInfoAtPosition(fileName, position, this.compiler);
41121 if (templateInfo === undefined) {
41122 return;
41123 }
41124 const definitionMetas = this.getDefinitionMetaAtPosition(templateInfo, position);
41125 if (definitionMetas === undefined) {
41126 return undefined;
41127 }
41128 const definitions = [];
41129 for (const { symbol, node, parent } of definitionMetas) {
41130 switch (symbol.kind) {
41131 case SymbolKind.Directive:
41132 case SymbolKind.DomBinding:
41133 case SymbolKind.Element:
41134 case SymbolKind.Template:
41135 definitions.push(...this.getTypeDefinitionsForTemplateInstance(symbol, node));
41136 break;
41137 case SymbolKind.Output:
41138 case SymbolKind.Input: {
41139 const bindingDefs = this.getTypeDefinitionsForSymbols(...symbol.bindings);
41140 definitions.push(...bindingDefs);
41141 // Also attempt to get directive matches for the input name. If there is a directive that
41142 // has the input name as part of the selector, we want to return that as well.
41143 const directiveDefs = this.getDirectiveTypeDefsForBindingNode(node, parent, templateInfo.component);
41144 definitions.push(...directiveDefs);
41145 break;
41146 }
41147 case SymbolKind.Pipe: {
41148 if (symbol.tsSymbol !== null) {
41149 definitions.push(...this.getTypeDefinitionsForSymbols(symbol));
41150 }
41151 else {
41152 // If there is no `ts.Symbol` for the pipe transform, we want to return the
41153 // type definition (the pipe class).
41154 definitions.push(...this.getTypeDefinitionsForSymbols(symbol.classSymbol));
41155 }
41156 break;
41157 }
41158 case SymbolKind.Reference:
41159 definitions.push(...this.getTypeDefinitionsForSymbols({ shimLocation: symbol.targetLocation }));
41160 break;
41161 case SymbolKind.Expression:
41162 definitions.push(...this.getTypeDefinitionsForSymbols(symbol));
41163 break;
41164 case SymbolKind.Variable: {
41165 definitions.push(...this.getTypeDefinitionsForSymbols({ shimLocation: symbol.initializerLocation }));
41166 break;
41167 }
41168 }
41169 return definitions;
41170 }
41171 }
41172 getTypeDefinitionsForTemplateInstance(symbol, node) {
41173 switch (symbol.kind) {
41174 case SymbolKind.Template: {
41175 const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives);
41176 return this.getTypeDefinitionsForSymbols(...matches);
41177 }
41178 case SymbolKind.Element: {
41179 const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives);
41180 // If one of the directive matches is a component, we should not include the native element
41181 // in the results because it is replaced by the component.
41182 return Array.from(matches).some(dir => dir.isComponent) ?
41183 this.getTypeDefinitionsForSymbols(...matches) :
41184 this.getTypeDefinitionsForSymbols(...matches, symbol);
41185 }
41186 case SymbolKind.DomBinding: {
41187 if (!(node instanceof TextAttribute)) {
41188 return [];
41189 }
41190 const dirs = getDirectiveMatchesForAttribute(node.name, symbol.host.templateNode, symbol.host.directives);
41191 return this.getTypeDefinitionsForSymbols(...dirs);
41192 }
41193 case SymbolKind.Directive:
41194 return this.getTypeDefinitionsForSymbols(symbol);
41195 }
41196 }
41197 getDirectiveTypeDefsForBindingNode(node, parent, component) {
41198 if (!(node instanceof BoundAttribute) && !(node instanceof TextAttribute) &&
41199 !(node instanceof BoundEvent)) {
41200 return [];
41201 }
41202 if (parent === null ||
41203 !(parent instanceof Template || parent instanceof Element)) {
41204 return [];
41205 }
41206 const templateOrElementSymbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(parent, component);
41207 if (templateOrElementSymbol === null ||
41208 (templateOrElementSymbol.kind !== SymbolKind.Template &&
41209 templateOrElementSymbol.kind !== SymbolKind.Element)) {
41210 return [];
41211 }
41212 const dirs = getDirectiveMatchesForAttribute(node.name, parent, templateOrElementSymbol.directives);
41213 return this.getTypeDefinitionsForSymbols(...dirs);
41214 }
41215 getTypeDefinitionsForSymbols(...symbols) {
41216 return flatMap(symbols, ({ shimLocation }) => {
41217 var _a;
41218 const { shimPath, positionInShimFile } = shimLocation;
41219 return (_a = this.tsLS.getTypeDefinitionAtPosition(shimPath, positionInShimFile)) !== null && _a !== void 0 ? _a : [];
41220 });
41221 }
41222 getDefinitionMetaAtPosition({ template, component }, position) {
41223 const target = getTargetAtPosition(template, position);
41224 if (target === null) {
41225 return undefined;
41226 }
41227 const { context, parent } = target;
41228 const nodes = context.kind === TargetNodeKind.TwoWayBindingContext ? context.nodes : [context.node];
41229 const definitionMetas = [];
41230 for (const node of nodes) {
41231 const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(node, component);
41232 if (symbol === null) {
41233 continue;
41234 }
41235 definitionMetas.push({ node, parent, symbol });
41236 }
41237 return definitionMetas.length > 0 ? definitionMetas : undefined;
41238 }
41239 }
41240 /**
41241 * Gets an Angular-specific definition in a TypeScript source file.
41242 */
41243 function getDefinitionForExpressionAtPosition(fileName, position, compiler) {
41244 const sf = compiler.getNextProgram().getSourceFile(fileName);
41245 if (sf === undefined) {
41246 return;
41247 }
41248 const expression = findTightestNode(sf, position);
41249 if (expression === undefined) {
41250 return;
41251 }
41252 const classDeclaration = getParentClassDeclaration(expression);
41253 if (classDeclaration === undefined) {
41254 return;
41255 }
41256 const componentResources = compiler.getComponentResources(classDeclaration);
41257 if (componentResources === null) {
41258 return;
41259 }
41260 const allResources = [...componentResources.styles, componentResources.template];
41261 const resourceForExpression = allResources.find(resource => resource.expression === expression);
41262 if (resourceForExpression === undefined || !isExternalResource(resourceForExpression)) {
41263 return;
41264 }
41265 const templateDefinitions = [{
41266 kind: ts$1.ScriptElementKind.externalModuleName,
41267 name: resourceForExpression.path,
41268 containerKind: ts$1.ScriptElementKind.unknown,
41269 containerName: '',
41270 // Reading the template is expensive, so don't provide a preview.
41271 // TODO(ayazhafiz): Consider providing an actual span:
41272 // 1. We're likely to read the template anyway
41273 // 2. We could show just the first 100 chars or so
41274 textSpan: { start: 0, length: 0 },
41275 fileName: resourceForExpression.path,
41276 }];
41277 return {
41278 definitions: templateDefinitions,
41279 textSpan: {
41280 // Exclude opening and closing quotes in the url span.
41281 start: expression.getStart() + 1,
41282 length: expression.getWidth() - 2,
41283 },
41284 };
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 class QuickInfoBuilder {
41295 constructor(tsLS, compiler, component, node) {
41296 this.tsLS = tsLS;
41297 this.compiler = compiler;
41298 this.component = component;
41299 this.node = node;
41300 this.typeChecker = this.compiler.getNextProgram().getTypeChecker();
41301 }
41302 get() {
41303 const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(this.node, this.component);
41304 if (symbol === null) {
41305 return isDollarAny(this.node) ? createDollarAnyQuickInfo(this.node) : undefined;
41306 }
41307 return this.getQuickInfoForSymbol(symbol);
41308 }
41309 getQuickInfoForSymbol(symbol) {
41310 switch (symbol.kind) {
41311 case SymbolKind.Input:
41312 case SymbolKind.Output:
41313 return this.getQuickInfoForBindingSymbol(symbol);
41314 case SymbolKind.Template:
41315 return createNgTemplateQuickInfo(this.node);
41316 case SymbolKind.Element:
41317 return this.getQuickInfoForElementSymbol(symbol);
41318 case SymbolKind.Variable:
41319 return this.getQuickInfoForVariableSymbol(symbol);
41320 case SymbolKind.Reference:
41321 return this.getQuickInfoForReferenceSymbol(symbol);
41322 case SymbolKind.DomBinding:
41323 return this.getQuickInfoForDomBinding(symbol);
41324 case SymbolKind.Directive:
41325 return this.getQuickInfoAtShimLocation(symbol.shimLocation);
41326 case SymbolKind.Pipe:
41327 return this.getQuickInfoForPipeSymbol(symbol);
41328 case SymbolKind.Expression:
41329 return this.getQuickInfoAtShimLocation(symbol.shimLocation);
41330 }
41331 }
41332 getQuickInfoForBindingSymbol(symbol) {
41333 if (symbol.bindings.length === 0) {
41334 return undefined;
41335 }
41336 const kind = symbol.kind === SymbolKind.Input ? DisplayInfoKind.PROPERTY : DisplayInfoKind.EVENT;
41337 const quickInfo = this.getQuickInfoAtShimLocation(symbol.bindings[0].shimLocation);
41338 return quickInfo === undefined ? undefined : updateQuickInfoKind(quickInfo, kind);
41339 }
41340 getQuickInfoForElementSymbol(symbol) {
41341 const { templateNode } = symbol;
41342 const matches = getDirectiveMatchesForElementTag(templateNode, symbol.directives);
41343 if (matches.size > 0) {
41344 return this.getQuickInfoForDirectiveSymbol(matches.values().next().value, templateNode);
41345 }
41346 return createQuickInfo(templateNode.name, DisplayInfoKind.ELEMENT, getTextSpanOfNode(templateNode), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType));
41347 }
41348 getQuickInfoForVariableSymbol(symbol) {
41349 const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.initializerLocation);
41350 return createQuickInfo(symbol.declaration.name, DisplayInfoKind.VARIABLE, getTextSpanOfNode(this.node), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType), documentation);
41351 }
41352 getQuickInfoForReferenceSymbol(symbol) {
41353 const documentation = this.getDocumentationFromTypeDefAtLocation(symbol.targetLocation);
41354 return createQuickInfo(symbol.declaration.name, DisplayInfoKind.REFERENCE, getTextSpanOfNode(this.node), undefined /* containerName */, this.typeChecker.typeToString(symbol.tsType), documentation);
41355 }
41356 getQuickInfoForPipeSymbol(symbol) {
41357 if (symbol.tsSymbol !== null) {
41358 const quickInfo = this.getQuickInfoAtShimLocation(symbol.shimLocation);
41359 return quickInfo === undefined ? undefined :
41360 updateQuickInfoKind(quickInfo, DisplayInfoKind.PIPE);
41361 }
41362 else {
41363 return createQuickInfo(this.typeChecker.typeToString(symbol.classSymbol.tsType), DisplayInfoKind.PIPE, getTextSpanOfNode(this.node));
41364 }
41365 }
41366 getQuickInfoForDomBinding(symbol) {
41367 if (!(this.node instanceof TextAttribute) &&
41368 !(this.node instanceof BoundAttribute)) {
41369 return undefined;
41370 }
41371 const directives = getDirectiveMatchesForAttribute(this.node.name, symbol.host.templateNode, symbol.host.directives);
41372 if (directives.size === 0) {
41373 return undefined;
41374 }
41375 return this.getQuickInfoForDirectiveSymbol(directives.values().next().value);
41376 }
41377 getQuickInfoForDirectiveSymbol(dir, node = this.node) {
41378 const kind = dir.isComponent ? DisplayInfoKind.COMPONENT : DisplayInfoKind.DIRECTIVE;
41379 const documentation = this.getDocumentationFromTypeDefAtLocation(dir.shimLocation);
41380 let containerName;
41381 if (ts$1.isClassDeclaration(dir.tsSymbol.valueDeclaration) && dir.ngModule !== null) {
41382 containerName = dir.ngModule.name.getText();
41383 }
41384 return createQuickInfo(this.typeChecker.typeToString(dir.tsType), kind, getTextSpanOfNode(this.node), containerName, undefined, documentation);
41385 }
41386 getDocumentationFromTypeDefAtLocation(shimLocation) {
41387 var _a;
41388 const typeDefs = this.tsLS.getTypeDefinitionAtPosition(shimLocation.shimPath, shimLocation.positionInShimFile);
41389 if (typeDefs === undefined || typeDefs.length === 0) {
41390 return undefined;
41391 }
41392 return (_a = this.tsLS.getQuickInfoAtPosition(typeDefs[0].fileName, typeDefs[0].textSpan.start)) === null || _a === void 0 ? void 0 : _a.documentation;
41393 }
41394 getQuickInfoAtShimLocation(location) {
41395 const quickInfo = this.tsLS.getQuickInfoAtPosition(location.shimPath, location.positionInShimFile);
41396 if (quickInfo === undefined || quickInfo.displayParts === undefined) {
41397 return quickInfo;
41398 }
41399 quickInfo.displayParts = filterAliasImports(quickInfo.displayParts);
41400 const textSpan = getTextSpanOfNode(this.node);
41401 return Object.assign(Object.assign({}, quickInfo), { textSpan });
41402 }
41403 }
41404 function updateQuickInfoKind(quickInfo, kind) {
41405 if (quickInfo.displayParts === undefined) {
41406 return quickInfo;
41407 }
41408 const startsWithKind = quickInfo.displayParts.length >= 3 &&
41409 displayPartsEqual(quickInfo.displayParts[0], { text: '(', kind: SYMBOL_PUNC }) &&
41410 quickInfo.displayParts[1].kind === SYMBOL_TEXT &&
41411 displayPartsEqual(quickInfo.displayParts[2], { text: ')', kind: SYMBOL_PUNC });
41412 if (startsWithKind) {
41413 quickInfo.displayParts[1].text = kind;
41414 }
41415 else {
41416 quickInfo.displayParts = [
41417 { text: '(', kind: SYMBOL_PUNC },
41418 { text: kind, kind: SYMBOL_TEXT },
41419 { text: ')', kind: SYMBOL_PUNC },
41420 { text: ' ', kind: SYMBOL_SPACE },
41421 ...quickInfo.displayParts,
41422 ];
41423 }
41424 return quickInfo;
41425 }
41426 function displayPartsEqual(a, b) {
41427 return a.text === b.text && a.kind === b.kind;
41428 }
41429 function isDollarAny(node) {
41430 return node instanceof MethodCall && node.receiver instanceof ImplicitReceiver &&
41431 !(node.receiver instanceof ThisReceiver) && node.name === '$any' && node.args.length === 1;
41432 }
41433 function createDollarAnyQuickInfo(node) {
41434 return createQuickInfo('$any', DisplayInfoKind.METHOD, getTextSpanOfNode(node),
41435 /** containerName */ undefined, 'any', [{
41436 kind: SYMBOL_TEXT,
41437 text: 'function to cast an expression to the `any` type',
41438 }]);
41439 }
41440 // TODO(atscott): Create special `ts.QuickInfo` for `ng-template` and `ng-container` as well.
41441 function createNgTemplateQuickInfo(node) {
41442 return createQuickInfo('ng-template', DisplayInfoKind.TEMPLATE, getTextSpanOfNode(node),
41443 /** containerName */ undefined,
41444 /** type */ undefined, [{
41445 kind: SYMBOL_TEXT,
41446 text: 'The `<ng-template>` is an Angular element for rendering HTML. It is never displayed directly.',
41447 }]);
41448 }
41449 /**
41450 * Construct a QuickInfo object taking into account its container and type.
41451 * @param name Name of the QuickInfo target
41452 * @param kind component, directive, pipe, etc.
41453 * @param textSpan span of the target
41454 * @param containerName either the Symbol's container or the NgModule that contains the directive
41455 * @param type user-friendly name of the type
41456 * @param documentation docstring or comment
41457 */
41458 function createQuickInfo(name, kind, textSpan, containerName, type, documentation) {
41459 const displayParts = createDisplayParts(name, kind, containerName, type);
41460 return {
41461 kind: unsafeCastDisplayInfoKindToScriptElementKind(kind),
41462 kindModifiers: ts$1.ScriptElementKindModifier.none,
41463 textSpan: textSpan,
41464 displayParts,
41465 documentation,
41466 };
41467 }
41468
41469 /**
41470 * @license
41471 * Copyright Google LLC All Rights Reserved.
41472 *
41473 * Use of this source code is governed by an MIT-style license that can be
41474 * found in the LICENSE file at https://angular.io/license
41475 */
41476 function toFilePosition(shimLocation) {
41477 return { fileName: shimLocation.shimPath, position: shimLocation.positionInShimFile };
41478 }
41479 var RequestKind;
41480 (function (RequestKind) {
41481 RequestKind[RequestKind["Template"] = 0] = "Template";
41482 RequestKind[RequestKind["TypeScript"] = 1] = "TypeScript";
41483 })(RequestKind || (RequestKind = {}));
41484 class ReferencesAndRenameBuilder {
41485 constructor(strategy, tsLS, compiler) {
41486 this.strategy = strategy;
41487 this.tsLS = tsLS;
41488 this.compiler = compiler;
41489 this.ttc = this.compiler.getTemplateTypeChecker();
41490 }
41491 getRenameInfo(filePath, position) {
41492 const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
41493 // We could not get a template at position so we assume the request came from outside the
41494 // template.
41495 if (templateInfo === undefined) {
41496 return this.tsLS.getRenameInfo(filePath, position);
41497 }
41498 const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
41499 if (allTargetDetails === null) {
41500 return { canRename: false, localizedErrorMessage: 'Could not find template node at position.' };
41501 }
41502 const { templateTarget } = allTargetDetails[0];
41503 const templateTextAndSpan = getRenameTextAndSpanAtPosition(templateTarget, position);
41504 if (templateTextAndSpan === null) {
41505 return { canRename: false, localizedErrorMessage: 'Could not determine template node text.' };
41506 }
41507 const { text, span } = templateTextAndSpan;
41508 return {
41509 canRename: true,
41510 displayName: text,
41511 fullDisplayName: text,
41512 triggerSpan: span,
41513 };
41514 }
41515 findRenameLocations(filePath, position) {
41516 this.ttc.generateAllTypeCheckBlocks();
41517 const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
41518 // We could not get a template at position so we assume the request came from outside the
41519 // template.
41520 if (templateInfo === undefined) {
41521 const requestNode = this.getTsNodeAtPosition(filePath, position);
41522 if (requestNode === null) {
41523 return undefined;
41524 }
41525 const requestOrigin = { kind: RequestKind.TypeScript, requestNode };
41526 return this.findRenameLocationsAtTypescriptPosition(filePath, position, requestOrigin);
41527 }
41528 return this.findRenameLocationsAtTemplatePosition(templateInfo, position);
41529 }
41530 findRenameLocationsAtTemplatePosition(templateInfo, position) {
41531 const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
41532 if (allTargetDetails === null) {
41533 return undefined;
41534 }
41535 const allRenameLocations = [];
41536 for (const targetDetails of allTargetDetails) {
41537 const requestOrigin = {
41538 kind: RequestKind.Template,
41539 requestNode: targetDetails.templateTarget,
41540 position,
41541 };
41542 for (const location of targetDetails.typescriptLocations) {
41543 const locations = this.findRenameLocationsAtTypescriptPosition(location.fileName, location.position, requestOrigin);
41544 // If we couldn't find rename locations for _any_ result, we should not allow renaming to
41545 // proceed instead of having a partially complete rename.
41546 if (locations === undefined) {
41547 return undefined;
41548 }
41549 allRenameLocations.push(...locations);
41550 }
41551 }
41552 return allRenameLocations.length > 0 ? allRenameLocations : undefined;
41553 }
41554 getTsNodeAtPosition(filePath, position) {
41555 var _a;
41556 const sf = this.strategy.getProgram().getSourceFile(filePath);
41557 if (!sf) {
41558 return null;
41559 }
41560 return (_a = findTightestNode(sf, position)) !== null && _a !== void 0 ? _a : null;
41561 }
41562 findRenameLocationsAtTypescriptPosition(filePath, position, requestOrigin) {
41563 let originalNodeText;
41564 if (requestOrigin.kind === RequestKind.TypeScript) {
41565 originalNodeText = requestOrigin.requestNode.getText();
41566 }
41567 else {
41568 const templateNodeText = getRenameTextAndSpanAtPosition(requestOrigin.requestNode, requestOrigin.position);
41569 if (templateNodeText === null) {
41570 return undefined;
41571 }
41572 originalNodeText = templateNodeText.text;
41573 }
41574 const locations = this.tsLS.findRenameLocations(filePath, position, /*findInStrings*/ false, /*findInComments*/ false);
41575 if (locations === undefined) {
41576 return undefined;
41577 }
41578 const entries = new Map();
41579 for (const location of locations) {
41580 // TODO(atscott): Determine if a file is a shim file in a more robust way and make the API
41581 // available in an appropriate location.
41582 if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(location.fileName))) {
41583 const entry = this.convertToTemplateDocumentSpan(location, this.ttc, originalNodeText);
41584 // There is no template node whose text matches the original rename request. Bail on
41585 // renaming completely rather than providing incomplete results.
41586 if (entry === null) {
41587 return undefined;
41588 }
41589 entries.set(createLocationKey(entry), entry);
41590 }
41591 else {
41592 // Ensure we only allow renaming a TS result with matching text
41593 const refNode = this.getTsNodeAtPosition(location.fileName, location.textSpan.start);
41594 if (refNode === null || refNode.getText() !== originalNodeText) {
41595 return undefined;
41596 }
41597 entries.set(createLocationKey(location), location);
41598 }
41599 }
41600 return Array.from(entries.values());
41601 }
41602 getReferencesAtPosition(filePath, position) {
41603 this.ttc.generateAllTypeCheckBlocks();
41604 const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
41605 if (templateInfo === undefined) {
41606 return this.getReferencesAtTypescriptPosition(filePath, position);
41607 }
41608 return this.getReferencesAtTemplatePosition(templateInfo, position);
41609 }
41610 getReferencesAtTemplatePosition(templateInfo, position) {
41611 const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
41612 if (allTargetDetails === null) {
41613 return undefined;
41614 }
41615 const allReferences = [];
41616 for (const targetDetails of allTargetDetails) {
41617 for (const location of targetDetails.typescriptLocations) {
41618 const refs = this.getReferencesAtTypescriptPosition(location.fileName, location.position);
41619 if (refs !== undefined) {
41620 allReferences.push(...refs);
41621 }
41622 }
41623 }
41624 return allReferences.length > 0 ? allReferences : undefined;
41625 }
41626 getTargetDetailsAtTemplatePosition({ template, component }, position) {
41627 // Find the AST node in the template at the position.
41628 const positionDetails = getTargetAtPosition(template, position);
41629 if (positionDetails === null) {
41630 return null;
41631 }
41632 const nodes = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
41633 positionDetails.context.nodes :
41634 [positionDetails.context.node];
41635 const details = [];
41636 for (const node of nodes) {
41637 // Get the information about the TCB at the template position.
41638 const symbol = this.ttc.getSymbolOfNode(node, component);
41639 if (symbol === null) {
41640 continue;
41641 }
41642 const templateTarget = node;
41643 switch (symbol.kind) {
41644 case SymbolKind.Directive:
41645 case SymbolKind.Template:
41646 // References to elements, templates, and directives will be through template references
41647 // (#ref). They shouldn't be used directly for a Language Service reference request.
41648 break;
41649 case SymbolKind.Element: {
41650 const matches = getDirectiveMatchesForElementTag(symbol.templateNode, symbol.directives);
41651 details.push({ typescriptLocations: this.getPositionsForDirectives(matches), templateTarget });
41652 break;
41653 }
41654 case SymbolKind.DomBinding: {
41655 // Dom bindings aren't currently type-checked (see `checkTypeOfDomBindings`) so they don't
41656 // have a shim location. This means we can't match dom bindings to their lib.dom
41657 // reference, but we can still see if they match to a directive.
41658 if (!(node instanceof TextAttribute) && !(node instanceof BoundAttribute)) {
41659 return null;
41660 }
41661 const directives = getDirectiveMatchesForAttribute(node.name, symbol.host.templateNode, symbol.host.directives);
41662 details.push({
41663 typescriptLocations: this.getPositionsForDirectives(directives),
41664 templateTarget,
41665 });
41666 break;
41667 }
41668 case SymbolKind.Reference: {
41669 details.push({
41670 typescriptLocations: [toFilePosition(symbol.referenceVarLocation)],
41671 templateTarget,
41672 });
41673 break;
41674 }
41675 case SymbolKind.Variable: {
41676 if ((templateTarget instanceof Variable)) {
41677 if (templateTarget.valueSpan !== undefined &&
41678 isWithin(position, templateTarget.valueSpan)) {
41679 // In the valueSpan of the variable, we want to get the reference of the initializer.
41680 details.push({
41681 typescriptLocations: [toFilePosition(symbol.initializerLocation)],
41682 templateTarget,
41683 });
41684 }
41685 else if (isWithin(position, templateTarget.keySpan)) {
41686 // In the keySpan of the variable, we want to get the reference of the local variable.
41687 details.push({
41688 typescriptLocations: [toFilePosition(symbol.localVarLocation)],
41689 templateTarget,
41690 });
41691 }
41692 }
41693 else {
41694 // If the templateNode is not the `TmplAstVariable`, it must be a usage of the
41695 // variable somewhere in the template.
41696 details.push({
41697 typescriptLocations: [toFilePosition(symbol.localVarLocation)],
41698 templateTarget,
41699 });
41700 }
41701 break;
41702 }
41703 case SymbolKind.Input:
41704 case SymbolKind.Output: {
41705 details.push({
41706 typescriptLocations: symbol.bindings.map(binding => toFilePosition(binding.shimLocation)),
41707 templateTarget,
41708 });
41709 break;
41710 }
41711 case SymbolKind.Pipe:
41712 case SymbolKind.Expression: {
41713 details.push({ typescriptLocations: [toFilePosition(symbol.shimLocation)], templateTarget });
41714 break;
41715 }
41716 }
41717 }
41718 return details.length > 0 ? details : null;
41719 }
41720 getPositionsForDirectives(directives) {
41721 const allDirectives = [];
41722 for (const dir of directives.values()) {
41723 const dirClass = dir.tsSymbol.valueDeclaration;
41724 if (dirClass === undefined || !ts$1.isClassDeclaration(dirClass) ||
41725 dirClass.name === undefined) {
41726 continue;
41727 }
41728 const { fileName } = dirClass.getSourceFile();
41729 const position = dirClass.name.getStart();
41730 allDirectives.push({ fileName, position });
41731 }
41732 return allDirectives;
41733 }
41734 getReferencesAtTypescriptPosition(fileName, position) {
41735 const refs = this.tsLS.getReferencesAtPosition(fileName, position);
41736 if (refs === undefined) {
41737 return undefined;
41738 }
41739 const entries = new Map();
41740 for (const ref of refs) {
41741 if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(ref.fileName))) {
41742 const entry = this.convertToTemplateDocumentSpan(ref, this.ttc);
41743 if (entry !== null) {
41744 entries.set(createLocationKey(entry), entry);
41745 }
41746 }
41747 else {
41748 entries.set(createLocationKey(ref), ref);
41749 }
41750 }
41751 return Array.from(entries.values());
41752 }
41753 convertToTemplateDocumentSpan(shimDocumentSpan, templateTypeChecker, requiredNodeText) {
41754 const sf = this.strategy.getProgram().getSourceFile(shimDocumentSpan.fileName);
41755 if (sf === undefined) {
41756 return null;
41757 }
41758 const tcbNode = findTightestNode(sf, shimDocumentSpan.textSpan.start);
41759 if (tcbNode === undefined ||
41760 hasExpressionIdentifier(sf, tcbNode, ExpressionIdentifier.EVENT_PARAMETER)) {
41761 // If the reference result is the $event parameter in the subscribe/addEventListener
41762 // function in the TCB, we want to filter this result out of the references. We really only
41763 // want to return references to the parameter in the template itself.
41764 return null;
41765 }
41766 // TODO(atscott): Determine how to consistently resolve paths. i.e. with the project
41767 // serverHost or LSParseConfigHost in the adapter. We should have a better defined way to
41768 // normalize paths.
41769 const mapping = getTemplateLocationFromShimLocation(templateTypeChecker, absoluteFrom(shimDocumentSpan.fileName), shimDocumentSpan.textSpan.start);
41770 if (mapping === null) {
41771 return null;
41772 }
41773 const { span, templateUrl } = mapping;
41774 if (requiredNodeText !== undefined && span.toString() !== requiredNodeText) {
41775 return null;
41776 }
41777 return Object.assign(Object.assign({}, shimDocumentSpan), { fileName: templateUrl, textSpan: toTextSpan(span) });
41778 }
41779 }
41780 function getRenameTextAndSpanAtPosition(node, position) {
41781 if (node instanceof BoundAttribute || node instanceof TextAttribute ||
41782 node instanceof BoundEvent) {
41783 if (node.keySpan === undefined) {
41784 return null;
41785 }
41786 return { text: node.name, span: toTextSpan(node.keySpan) };
41787 }
41788 else if (node instanceof Variable || node instanceof Reference) {
41789 if (isWithin(position, node.keySpan)) {
41790 return { text: node.keySpan.toString(), span: toTextSpan(node.keySpan) };
41791 }
41792 else if (node.valueSpan && isWithin(position, node.valueSpan)) {
41793 return { text: node.valueSpan.toString(), span: toTextSpan(node.valueSpan) };
41794 }
41795 }
41796 if (node instanceof BindingPipe) {
41797 // TODO(atscott): Add support for renaming pipes
41798 return null;
41799 }
41800 if (node instanceof PropertyRead || node instanceof MethodCall || node instanceof PropertyWrite ||
41801 node instanceof SafePropertyRead || node instanceof SafeMethodCall) {
41802 return { text: node.name, span: toTextSpan(node.nameSpan) };
41803 }
41804 else if (node instanceof LiteralPrimitive) {
41805 const span = toTextSpan(node.sourceSpan);
41806 const text = node.value;
41807 if (typeof text === 'string') {
41808 // The span of a string literal includes the quotes but they should be removed for renaming.
41809 span.start += 1;
41810 span.length -= 2;
41811 }
41812 return { text, span };
41813 }
41814 return null;
41815 }
41816 /**
41817 * Creates a "key" for a rename/reference location by concatenating file name, span start, and span
41818 * length. This allows us to de-duplicate template results when an item may appear several times
41819 * in the TCB but map back to the same template location.
41820 */
41821 function createLocationKey(ds) {
41822 return ds.fileName + ds.textSpan.start + ds.textSpan.length;
41823 }
41824
41825 /**
41826 * @license
41827 * Copyright Google LLC All Rights Reserved.
41828 *
41829 * Use of this source code is governed by an MIT-style license that can be
41830 * found in the LICENSE file at https://angular.io/license
41831 */
41832 class LanguageService {
41833 constructor(project, tsLS) {
41834 this.project = project;
41835 this.tsLS = tsLS;
41836 this.parseConfigHost = new LSParseConfigHost(project.projectService.host);
41837 this.options = parseNgCompilerOptions(project, this.parseConfigHost);
41838 logCompilerOptions(project, this.options);
41839 this.strategy = createTypeCheckingProgramStrategy(project);
41840 this.adapter = new LanguageServiceAdapter(project);
41841 this.compilerFactory = new CompilerFactory(this.adapter, this.strategy, this.options);
41842 this.watchConfigFile(project);
41843 }
41844 getCompilerOptions() {
41845 return this.options;
41846 }
41847 getSemanticDiagnostics(fileName) {
41848 const compiler = this.compilerFactory.getOrCreate();
41849 const ttc = compiler.getTemplateTypeChecker();
41850 const diagnostics = [];
41851 if (isTypeScriptFile(fileName)) {
41852 const program = compiler.getNextProgram();
41853 const sourceFile = program.getSourceFile(fileName);
41854 if (sourceFile) {
41855 diagnostics.push(...compiler.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile));
41856 }
41857 }
41858 else {
41859 const components = compiler.getComponentsWithTemplateFile(fileName);
41860 for (const component of components) {
41861 if (ts.isClassDeclaration(component)) {
41862 diagnostics.push(...ttc.getDiagnosticsForComponent(component));
41863 }
41864 }
41865 }
41866 this.compilerFactory.registerLastKnownProgram();
41867 return diagnostics;
41868 }
41869 getDefinitionAndBoundSpan(fileName, position) {
41870 const compiler = this.compilerFactory.getOrCreate();
41871 const results = new DefinitionBuilder(this.tsLS, compiler).getDefinitionAndBoundSpan(fileName, position);
41872 this.compilerFactory.registerLastKnownProgram();
41873 return results;
41874 }
41875 getTypeDefinitionAtPosition(fileName, position) {
41876 const compiler = this.compilerFactory.getOrCreate();
41877 const results = new DefinitionBuilder(this.tsLS, compiler).getTypeDefinitionsAtPosition(fileName, position);
41878 this.compilerFactory.registerLastKnownProgram();
41879 return results;
41880 }
41881 getQuickInfoAtPosition(fileName, position) {
41882 const compiler = this.compilerFactory.getOrCreate();
41883 const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
41884 if (templateInfo === undefined) {
41885 return undefined;
41886 }
41887 const positionDetails = getTargetAtPosition(templateInfo.template, position);
41888 if (positionDetails === null) {
41889 return undefined;
41890 }
41891 // Because we can only show 1 quick info, just use the bound attribute if the target is a two
41892 // way binding. We may consider concatenating additional display parts from the other target
41893 // nodes or representing the two way binding in some other manner in the future.
41894 const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
41895 positionDetails.context.nodes[0] :
41896 positionDetails.context.node;
41897 const results = new QuickInfoBuilder(this.tsLS, compiler, templateInfo.component, node).get();
41898 this.compilerFactory.registerLastKnownProgram();
41899 return results;
41900 }
41901 getReferencesAtPosition(fileName, position) {
41902 const compiler = this.compilerFactory.getOrCreate();
41903 const results = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
41904 .getReferencesAtPosition(fileName, position);
41905 this.compilerFactory.registerLastKnownProgram();
41906 return results;
41907 }
41908 getRenameInfo(fileName, position) {
41909 var _a, _b, _c;
41910 const compiler = this.compilerFactory.getOrCreate();
41911 const renameInfo = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
41912 .getRenameInfo(absoluteFrom(fileName), position);
41913 if (!renameInfo.canRename) {
41914 return renameInfo;
41915 }
41916 const quickInfo = (_a = this.getQuickInfoAtPosition(fileName, position)) !== null && _a !== void 0 ? _a : this.tsLS.getQuickInfoAtPosition(fileName, position);
41917 const kind = (_b = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kind) !== null && _b !== void 0 ? _b : ts.ScriptElementKind.unknown;
41918 const kindModifiers = (_c = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kindModifiers) !== null && _c !== void 0 ? _c : ts.ScriptElementKind.unknown;
41919 return Object.assign(Object.assign({}, renameInfo), { kind, kindModifiers });
41920 }
41921 findRenameLocations(fileName, position) {
41922 const compiler = this.compilerFactory.getOrCreate();
41923 const results = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
41924 .findRenameLocations(fileName, position);
41925 this.compilerFactory.registerLastKnownProgram();
41926 return results;
41927 }
41928 getCompletionBuilder(fileName, position) {
41929 const compiler = this.compilerFactory.getOrCreate();
41930 const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
41931 if (templateInfo === undefined) {
41932 return null;
41933 }
41934 const positionDetails = getTargetAtPosition(templateInfo.template, position);
41935 if (positionDetails === null) {
41936 return null;
41937 }
41938 // For two-way bindings, we actually only need to be concerned with the bound attribute because
41939 // the bindings in the template are written with the attribute name, not the event name.
41940 const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
41941 positionDetails.context.nodes[0] :
41942 positionDetails.context.node;
41943 return new CompletionBuilder(this.tsLS, compiler, templateInfo.component, node, nodeContextFromTarget(positionDetails.context), positionDetails.parent, positionDetails.template);
41944 }
41945 getCompletionsAtPosition(fileName, position, options) {
41946 const builder = this.getCompletionBuilder(fileName, position);
41947 if (builder === null) {
41948 return undefined;
41949 }
41950 const result = builder.getCompletionsAtPosition(options);
41951 this.compilerFactory.registerLastKnownProgram();
41952 return result;
41953 }
41954 getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences) {
41955 const builder = this.getCompletionBuilder(fileName, position);
41956 if (builder === null) {
41957 return undefined;
41958 }
41959 const result = builder.getCompletionEntryDetails(entryName, formatOptions, preferences);
41960 this.compilerFactory.registerLastKnownProgram();
41961 return result;
41962 }
41963 getCompletionEntrySymbol(fileName, position, entryName) {
41964 const builder = this.getCompletionBuilder(fileName, position);
41965 if (builder === null) {
41966 return undefined;
41967 }
41968 const result = builder.getCompletionEntrySymbol(entryName);
41969 this.compilerFactory.registerLastKnownProgram();
41970 return result;
41971 }
41972 getTcb(fileName, position) {
41973 return this.withCompiler(compiler => {
41974 const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
41975 if (templateInfo === undefined) {
41976 return undefined;
41977 }
41978 const tcb = compiler.getTemplateTypeChecker().getTypeCheckBlock(templateInfo.component);
41979 if (tcb === null) {
41980 return undefined;
41981 }
41982 const sf = tcb.getSourceFile();
41983 let selections = [];
41984 const target = getTargetAtPosition(templateInfo.template, position);
41985 if (target !== null) {
41986 let selectionSpans;
41987 if ('nodes' in target.context) {
41988 selectionSpans = target.context.nodes.map(n => n.sourceSpan);
41989 }
41990 else {
41991 selectionSpans = [target.context.node.sourceSpan];
41992 }
41993 const selectionNodes = selectionSpans
41994 .map(s => findFirstMatchingNode(tcb, {
41995 withSpan: s,
41996 filter: (node) => true,
41997 }))
41998 .filter((n) => n !== null);
41999 selections = selectionNodes.map(n => {
42000 return {
42001 start: n.getStart(sf),
42002 length: n.getEnd() - n.getStart(sf),
42003 };
42004 });
42005 }
42006 return {
42007 fileName: sf.fileName,
42008 content: sf.getFullText(),
42009 selections,
42010 };
42011 });
42012 }
42013 withCompiler(p) {
42014 const compiler = this.compilerFactory.getOrCreate();
42015 const result = p(compiler);
42016 this.compilerFactory.registerLastKnownProgram();
42017 return result;
42018 }
42019 getCompilerOptionsDiagnostics() {
42020 const project = this.project;
42021 if (!(project instanceof ts.server.ConfiguredProject)) {
42022 return [];
42023 }
42024 const diagnostics = [];
42025 const configSourceFile = ts.readJsonConfigFile(project.getConfigFilePath(), (path) => project.readFile(path));
42026 if (!this.options.strictTemplates && !this.options.fullTemplateTypeCheck) {
42027 diagnostics.push({
42028 messageText: 'Some language features are not available. ' +
42029 'To access all features, enable `strictTemplates` in `angularCompilerOptions`.',
42030 category: ts.DiagnosticCategory.Suggestion,
42031 code: ngErrorCode(ErrorCode.SUGGEST_STRICT_TEMPLATES),
42032 file: configSourceFile,
42033 start: undefined,
42034 length: undefined,
42035 });
42036 }
42037 const compiler = this.compilerFactory.getOrCreate();
42038 diagnostics.push(...compiler.getOptionDiagnostics());
42039 return diagnostics;
42040 }
42041 watchConfigFile(project) {
42042 // TODO: Check the case when the project is disposed. An InferredProject
42043 // could be disposed when a tsconfig.json is added to the workspace,
42044 // in which case it becomes a ConfiguredProject (or vice-versa).
42045 // We need to make sure that the FileWatcher is closed.
42046 if (!(project instanceof ts.server.ConfiguredProject)) {
42047 return;
42048 }
42049 const { host } = project.projectService;
42050 host.watchFile(project.getConfigFilePath(), (fileName, eventKind) => {
42051 project.log(`Config file changed: ${fileName}`);
42052 if (eventKind === ts.FileWatcherEventKind.Changed) {
42053 this.options = parseNgCompilerOptions(project, this.parseConfigHost);
42054 logCompilerOptions(project, this.options);
42055 }
42056 });
42057 }
42058 }
42059 function logCompilerOptions(project, options) {
42060 const { logger } = project.projectService;
42061 const projectName = project.getProjectName();
42062 logger.info(`Angular compiler options for ${projectName}: ` + JSON.stringify(options, null, 2));
42063 }
42064 function parseNgCompilerOptions(project, host) {
42065 if (!(project instanceof ts.server.ConfiguredProject)) {
42066 return {};
42067 }
42068 const { options, errors } = readConfiguration(project.getConfigFilePath(), /* existingOptions */ undefined, host);
42069 if (errors.length > 0) {
42070 project.setProjectErrors(errors);
42071 }
42072 // Projects loaded into the Language Service often include test files which are not part of the
42073 // app's main compilation unit, and these test files often include inline NgModules that declare
42074 // components from the app. These declarations conflict with the main declarations of such
42075 // components in the app's NgModules. This conflict is not normally present during regular
42076 // compilation because the app and the tests are part of separate compilation units.
42077 //
42078 // As a temporary mitigation of this problem, we instruct the compiler to ignore classes which
42079 // are not exported. In many cases, this ensures the test NgModules are ignored by the compiler
42080 // and only the real component declaration is used.
42081 options.compileNonExportedClasses = false;
42082 return options;
42083 }
42084 function createTypeCheckingProgramStrategy(project) {
42085 return {
42086 supportsInlineOperations: false,
42087 shimPathForComponent(component) {
42088 return TypeCheckShimGenerator.shimFor(absoluteFromSourceFile(component.getSourceFile()));
42089 },
42090 getProgram() {
42091 const program = project.getLanguageService().getProgram();
42092 if (!program) {
42093 throw new Error('Language service does not have a program!');
42094 }
42095 return program;
42096 },
42097 updateFiles(contents) {
42098 for (const [fileName, newText] of contents) {
42099 const scriptInfo = getOrCreateTypeCheckScriptInfo(project, fileName);
42100 const snapshot = scriptInfo.getSnapshot();
42101 const length = snapshot.getLength();
42102 scriptInfo.editContent(0, length, newText);
42103 }
42104 },
42105 };
42106 }
42107 function getOrCreateTypeCheckScriptInfo(project, tcf) {
42108 // First check if there is already a ScriptInfo for the tcf
42109 const { projectService } = project;
42110 let scriptInfo = projectService.getScriptInfo(tcf);
42111 if (!scriptInfo) {
42112 // ScriptInfo needs to be opened by client to be able to set its user-defined
42113 // content. We must also provide file content, otherwise the service will
42114 // attempt to fetch the content from disk and fail.
42115 scriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath(ts.server.toNormalizedPath(tcf), true, // openedByClient
42116 '', // fileContent
42117 // script info added by plugins should be marked as external, see
42118 // https://github.com/microsoft/TypeScript/blob/b217f22e798c781f55d17da72ed099a9dee5c650/src/compiler/program.ts#L1897-L1899
42119 ts.ScriptKind.External);
42120 if (!scriptInfo) {
42121 throw new Error(`Failed to create script info for ${tcf}`);
42122 }
42123 }
42124 // Add ScriptInfo to project if it's missing. A ScriptInfo needs to be part of
42125 // the project so that it becomes part of the program.
42126 if (!project.containsScriptInfo(scriptInfo)) {
42127 project.addRoot(scriptInfo);
42128 }
42129 return scriptInfo;
42130 }
42131 function nodeContextFromTarget(target) {
42132 switch (target.kind) {
42133 case TargetNodeKind.ElementInTagContext:
42134 return CompletionNodeContext.ElementTag;
42135 case TargetNodeKind.ElementInBodyContext:
42136 // Completions in element bodies are for new attributes.
42137 return CompletionNodeContext.ElementAttributeKey;
42138 case TargetNodeKind.TwoWayBindingContext:
42139 return CompletionNodeContext.TwoWayBinding;
42140 case TargetNodeKind.AttributeInKeyContext:
42141 return CompletionNodeContext.ElementAttributeKey;
42142 case TargetNodeKind.AttributeInValueContext:
42143 if (target.node instanceof BoundEvent) {
42144 return CompletionNodeContext.EventValue;
42145 }
42146 else {
42147 return CompletionNodeContext.None;
42148 }
42149 default:
42150 // No special context is available.
42151 return CompletionNodeContext.None;
42152 }
42153 }
42154
42155 /**
42156 * @license
42157 * Copyright Google LLC All Rights Reserved.
42158 *
42159 * Use of this source code is governed by an MIT-style license that can be
42160 * found in the LICENSE file at https://angular.io/license
42161 */
42162 function create(info) {
42163 const { project, languageService: tsLS, config } = info;
42164 const angularOnly = (config === null || config === void 0 ? void 0 : config.angularOnly) === true;
42165 const ngLS = new LanguageService(project, tsLS);
42166 function getSemanticDiagnostics(fileName) {
42167 const diagnostics = [];
42168 if (!angularOnly) {
42169 diagnostics.push(...tsLS.getSemanticDiagnostics(fileName));
42170 }
42171 diagnostics.push(...ngLS.getSemanticDiagnostics(fileName));
42172 return diagnostics;
42173 }
42174 function getQuickInfoAtPosition(fileName, position) {
42175 var _a;
42176 if (angularOnly) {
42177 return ngLS.getQuickInfoAtPosition(fileName, position);
42178 }
42179 else {
42180 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42181 return (_a = tsLS.getQuickInfoAtPosition(fileName, position)) !== null && _a !== void 0 ? _a : ngLS.getQuickInfoAtPosition(fileName, position);
42182 }
42183 }
42184 function getTypeDefinitionAtPosition(fileName, position) {
42185 var _a;
42186 if (angularOnly) {
42187 return ngLS.getTypeDefinitionAtPosition(fileName, position);
42188 }
42189 else {
42190 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42191 return (_a = tsLS.getTypeDefinitionAtPosition(fileName, position)) !== null && _a !== void 0 ? _a : ngLS.getTypeDefinitionAtPosition(fileName, position);
42192 }
42193 }
42194 function getDefinitionAndBoundSpan(fileName, position) {
42195 var _a;
42196 if (angularOnly) {
42197 return ngLS.getDefinitionAndBoundSpan(fileName, position);
42198 }
42199 else {
42200 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42201 return (_a = tsLS.getDefinitionAndBoundSpan(fileName, position)) !== null && _a !== void 0 ? _a : ngLS.getDefinitionAndBoundSpan(fileName, position);
42202 }
42203 }
42204 function getReferencesAtPosition(fileName, position) {
42205 return ngLS.getReferencesAtPosition(fileName, position);
42206 }
42207 function findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename) {
42208 // Most operations combine results from all extensions. However, rename locations are exclusive
42209 // (results from only one extension are used) so our rename locations are a superset of the TS
42210 // rename locations. As a result, we do not check the `angularOnly` flag here because we always
42211 // want to include results at TS locations as well as locations in templates.
42212 return ngLS.findRenameLocations(fileName, position);
42213 }
42214 function getRenameInfo(fileName, position) {
42215 // See the comment in `findRenameLocations` explaining why we don't check the `angularOnly`
42216 // flag.
42217 return ngLS.getRenameInfo(fileName, position);
42218 }
42219 function getCompletionsAtPosition(fileName, position, options) {
42220 var _a;
42221 if (angularOnly) {
42222 return ngLS.getCompletionsAtPosition(fileName, position, options);
42223 }
42224 else {
42225 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42226 return (_a = tsLS.getCompletionsAtPosition(fileName, position, options)) !== null && _a !== void 0 ? _a : ngLS.getCompletionsAtPosition(fileName, position, options);
42227 }
42228 }
42229 function getCompletionEntryDetails(fileName, position, entryName, formatOptions, source, preferences) {
42230 var _a;
42231 if (angularOnly) {
42232 return ngLS.getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences);
42233 }
42234 else {
42235 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42236 return (_a = tsLS.getCompletionEntryDetails(fileName, position, entryName, formatOptions, source, preferences)) !== null && _a !== void 0 ? _a : ngLS.getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences);
42237 }
42238 }
42239 function getCompletionEntrySymbol(fileName, position, name, source) {
42240 var _a;
42241 if (angularOnly) {
42242 return ngLS.getCompletionEntrySymbol(fileName, position, name);
42243 }
42244 else {
42245 // If TS could answer the query, then return that result. Otherwise, return from Angular LS.
42246 return (_a = tsLS.getCompletionEntrySymbol(fileName, position, name, source)) !== null && _a !== void 0 ? _a : ngLS.getCompletionEntrySymbol(fileName, position, name);
42247 }
42248 }
42249 /**
42250 * Gets global diagnostics related to the program configuration and compiler options.
42251 */
42252 function getCompilerOptionsDiagnostics() {
42253 const diagnostics = [];
42254 if (!angularOnly) {
42255 diagnostics.push(...tsLS.getCompilerOptionsDiagnostics());
42256 }
42257 diagnostics.push(...ngLS.getCompilerOptionsDiagnostics());
42258 return diagnostics;
42259 }
42260 function getTcb(fileName, position) {
42261 return ngLS.getTcb(fileName, position);
42262 }
42263 return Object.assign(Object.assign({}, tsLS), { getSemanticDiagnostics,
42264 getTypeDefinitionAtPosition,
42265 getQuickInfoAtPosition,
42266 getDefinitionAndBoundSpan,
42267 getReferencesAtPosition,
42268 findRenameLocations,
42269 getRenameInfo,
42270 getCompletionsAtPosition,
42271 getCompletionEntryDetails,
42272 getCompletionEntrySymbol,
42273 getTcb,
42274 getCompilerOptionsDiagnostics });
42275 }
42276 function getExternalFiles(project) {
42277 if (!project.hasRoots()) {
42278 return []; // project has not been initialized
42279 }
42280 const typecheckFiles = [];
42281 for (const scriptInfo of project.getScriptInfos()) {
42282 if (scriptInfo.scriptKind === ts.ScriptKind.External) {
42283 // script info for typecheck file is marked as external, see
42284 // getOrCreateTypeCheckScriptInfo() in
42285 // packages/language-service/ivy/language_service.ts
42286 typecheckFiles.push(scriptInfo.fileName);
42287 }
42288 }
42289 return typecheckFiles;
42290 }
42291
42292 exports.create = create;
42293 exports.getExternalFiles = getExternalFiles;
42294
42295 Object.defineProperty(exports, '__esModule', { value: true });
42296
42297});
42298//# sourceMappingURL=ivy.js.map